<commit_before>#include <algorithm>
#include <cstddef>
#include <string_view>
#include <type_traits>

template <std::size_t N>
struct fixed_string {
  constexpr fixed_string(const char (&foo)[N + 1]) {
    std::copy_n(foo, N + 1, data);
  }
  constexpr fixed_string(std::string_view s) {
    static_assert(s.size() <= N);
    std::copy(s.begin(), s.end(), data);
  }
  constexpr std::string_view sv() const { return std::string_view(data); }
  auto operator<=>(const fixed_string&) const = default;
  char data[N + 1] = {};
};
template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;

template <fixed_string Tag, typename T>
struct tag_and_value {
  T value;
};

template <typename... TagsAndValues>
struct parms : TagsAndValues... {};

template <typename... TagsAndValues>
parms(TagsAndValues...) -> parms<TagsAndValues...>;

template <fixed_string Tag>
struct arg_type {
  template <typename T>
  constexpr auto operator=(T t) const {
    return tag_and_value<Tag, T>{std::move(t)};
  }
};

template <fixed_string Tag>
inline constexpr auto arg = arg_type<Tag>{};

template <typename T>
struct default_init {
  constexpr default_init() = default;
  auto operator<=>(const default_init&) const = default;
  constexpr auto operator()() const {
    if constexpr (std::is_default_constructible_v<T>) {
      return T{};
    }
  }
};

template <typename T, typename Self, typename F>
auto call_init(Self&, F& f) requires(requires {
  { f() } -> std::convertible_to<T>;
}) {
  return f();
}

template <typename T, typename Self, typename F>
auto call_init(Self& self, F& f) requires(requires {
  { f(self) } -> std::convertible_to<T>;
}) {
  return f(self);
}

template <typename T, typename Self, typename F>
auto call_init(Self& self, F& f) requires(requires {
  { f() } -> std::same_as<void>;
}) {}

template <fixed_string Tag, typename T, auto Init = default_init<T>()>
struct member {
  constexpr static auto tag() { return Tag; }
  constexpr static auto init() { return Init; }
  using element_type = T;
  T value;
  template <typename OtherT>
  constexpr member(tag_and_value<Tag, OtherT> tv)
      : value(std::move(tv.value)) {}

  template <typename Self>
  constexpr member(Self& self) : value(call_init<T>(self, Init)) {}
  constexpr member(member&&) = default;
  constexpr member(const member&) = default;

  constexpr member& operator=(member&&) = default;
  constexpr member& operator=(const member&) = default;

  auto operator<=>(const member&) const = default;
};

template <typename... Members>
struct meta_struct_impl : Members... {
  template <typename Parms>
  constexpr meta_struct_impl(Parms p) : Members(std::move(p))... {}

  constexpr meta_struct_impl() : Members(*this)... {}
  constexpr meta_struct_impl(meta_struct_impl&&) = default;
  constexpr meta_struct_impl(const meta_struct_impl&) = default;
  constexpr meta_struct_impl& operator=(meta_struct_impl&&) = default;
  constexpr meta_struct_impl& operator=(const meta_struct_impl&) = default;

  auto operator<=>(const meta_struct_impl&) const = default;
};

template <typename... Members>
struct meta_struct : meta_struct_impl<Members...> {
  using super = meta_struct_impl<Members...>;
  template <typename... TagsAndValues>
  constexpr meta_struct(TagsAndValues... tags_and_values)
      : super(parms(std::move(tags_and_values)...)) {}

  constexpr meta_struct() = default;
  constexpr meta_struct(meta_struct&&) = default;
  constexpr meta_struct(const meta_struct&) = default;
  constexpr meta_struct& operator=(meta_struct&&) = default;
  constexpr meta_struct& operator=(const meta_struct&) = default;

  auto operator<=>(const meta_struct&) const = default;
};

template <typename F, typename... MembersImpl>
constexpr decltype(auto) meta_struct_apply(
    F&& f, meta_struct_impl<MembersImpl...>& m) {
  return std::forward<F>(f)(static_cast<MembersImpl&>(m)...);
}

template <typename F, typename... MembersImpl>
constexpr decltype(auto) meta_struct_apply(
    F&& f, const meta_struct_impl<MembersImpl...>& m) {
  return std::forward<F>(f)(static_cast<const MembersImpl&>(m)...);
}

template <typename F, typename... MembersImpl>
constexpr decltype(auto) meta_struct_apply(
    F&& f, meta_struct_impl<MembersImpl...>&& m) {
  return std::forward<F>(f)(static_cast<MembersImpl&&>(m)...);
}

template <typename MetaStructImpl>
struct apply_static_impl;

template <typename... MembersImpl>
struct apply_static_impl<meta_struct_impl<MembersImpl...>> {
  template <typename F>
  constexpr static decltype(auto) apply(F&& f) {
    return f(static_cast<MembersImpl*>(nullptr)...);
  }
};

template <typename MetaStruct, typename F>
auto meta_struct_apply(F&& f) {
  return apply_static_impl<typename MetaStruct::super>::apply(
      std::forward<F>(f));
}

template <fixed_string tag, typename T, auto Init>
decltype(auto) get_impl(member<tag, T, Init>& m) {
  return (m.value);
}

template <fixed_string tag, typename T, auto Init>
decltype(auto) get_impl(const member<tag, T, Init>& m) {
  return (m.value);
}

template <fixed_string tag, typename T, auto Init>
decltype(auto) get_impl(member<tag, T, Init>&& m) {
  return std::move(m.value);
}

template <fixed_string tag, typename MetaStruct>
decltype(auto) get(MetaStruct&& s) {
  return get_impl<tag>(std::forward<MetaStruct>(s));
}

#include <iostream>
#include <string>

template <typename MetaStruct>
void print(std::ostream& os) {
  meta_struct_apply<MetaStruct>([]<typename... M>(M * ...) {
    std::cout << "The tags are: ";
    ((std::cout << M::tag().sv() << " "), ...);
    std::cout << "\n";
  });
}

template <typename MetaStruct>
void print(std::ostream& os, const MetaStruct& ms) {
  meta_struct_apply(
      [&](const auto&... m) {
        ((std::cout << m.tag().sv() << ":" << m.value << "\n"), ...);
      },
      ms);
};

int main() {
  using Person = meta_struct<                                                //
      member<"id", int>,                                                     //
      member<"score", int, [](auto& self) { return get<"id">(self) + 1; }>,  //
      member<"name", std::string, [] { return "John"; }>                     //
      >;

  print<Person>(std::cout);

  Person p;

  print(std::cout, p);
}
<commit_msg>Remove print functions for meta_struct_print and just put them in main<commit_after>#include <algorithm>
#include <cstddef>
#include <string_view>
#include <type_traits>

template <std::size_t N>
struct fixed_string {
  constexpr fixed_string(const char (&foo)[N + 1]) {
    std::copy_n(foo, N + 1, data);
  }
  constexpr fixed_string(std::string_view s) {
    static_assert(s.size() <= N);
    std::copy(s.begin(), s.end(), data);
  }
  constexpr std::string_view sv() const { return std::string_view(data); }
  auto operator<=>(const fixed_string&) const = default;
  char data[N + 1] = {};
};
template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;

template <fixed_string Tag, typename T>
struct tag_and_value {
  T value;
};

template <typename... TagsAndValues>
struct parms : TagsAndValues... {};

template <typename... TagsAndValues>
parms(TagsAndValues...) -> parms<TagsAndValues...>;

template <fixed_string Tag>
struct arg_type {
  template <typename T>
  constexpr auto operator=(T t) const {
    return tag_and_value<Tag, T>{std::move(t)};
  }
};

template <fixed_string Tag>
inline constexpr auto arg = arg_type<Tag>{};

template <typename T>
struct default_init {
  constexpr default_init() = default;
  auto operator<=>(const default_init&) const = default;
  constexpr auto operator()() const {
    if constexpr (std::is_default_constructible_v<T>) {
      return T{};
    }
  }
};

template <typename T, typename Self, typename F>
auto call_init(Self&, F& f) requires(requires {
  { f() } -> std::convertible_to<T>;
}) {
  return f();
}

template <typename T, typename Self, typename F>
auto call_init(Self& self, F& f) requires(requires {
  { f(self) } -> std::convertible_to<T>;
}) {
  return f(self);
}

template <typename T, typename Self, typename F>
auto call_init(Self& self, F& f) requires(requires {
  { f() } -> std::same_as<void>;
}) {}

template <fixed_string Tag, typename T, auto Init = default_init<T>()>
struct member {
  constexpr static auto tag() { return Tag; }
  constexpr static auto init() { return Init; }
  using element_type = T;
  T value;
  template <typename OtherT>
  constexpr member(tag_and_value<Tag, OtherT> tv)
      : value(std::move(tv.value)) {}

  template <typename Self>
  constexpr member(Self& self) : value(call_init<T>(self, Init)) {}
  constexpr member(member&&) = default;
  constexpr member(const member&) = default;

  constexpr member& operator=(member&&) = default;
  constexpr member& operator=(const member&) = default;

  auto operator<=>(const member&) const = default;
};

template <typename... Members>
struct meta_struct_impl : Members... {
  template <typename Parms>
  constexpr meta_struct_impl(Parms p) : Members(std::move(p))... {}

  constexpr meta_struct_impl() : Members(*this)... {}
  constexpr meta_struct_impl(meta_struct_impl&&) = default;
  constexpr meta_struct_impl(const meta_struct_impl&) = default;
  constexpr meta_struct_impl& operator=(meta_struct_impl&&) = default;
  constexpr meta_struct_impl& operator=(const meta_struct_impl&) = default;

  auto operator<=>(const meta_struct_impl&) const = default;
};

template <typename... Members>
struct meta_struct : meta_struct_impl<Members...> {
  using super = meta_struct_impl<Members...>;
  template <typename... TagsAndValues>
  constexpr meta_struct(TagsAndValues... tags_and_values)
      : super(parms(std::move(tags_and_values)...)) {}

  constexpr meta_struct() = default;
  constexpr meta_struct(meta_struct&&) = default;
  constexpr meta_struct(const meta_struct&) = default;
  constexpr meta_struct& operator=(meta_struct&&) = default;
  constexpr meta_struct& operator=(const meta_struct&) = default;

  auto operator<=>(const meta_struct&) const = default;
};

template <typename F, typename... MembersImpl>
constexpr decltype(auto) meta_struct_apply(
    F&& f, meta_struct_impl<MembersImpl...>& m) {
  return std::forward<F>(f)(static_cast<MembersImpl&>(m)...);
}

template <typename F, typename... MembersImpl>
constexpr decltype(auto) meta_struct_apply(
    F&& f, const meta_struct_impl<MembersImpl...>& m) {
  return std::forward<F>(f)(static_cast<const MembersImpl&>(m)...);
}

template <typename F, typename... MembersImpl>
constexpr decltype(auto) meta_struct_apply(
    F&& f, meta_struct_impl<MembersImpl...>&& m) {
  return std::forward<F>(f)(static_cast<MembersImpl&&>(m)...);
}

template <typename MetaStructImpl>
struct apply_static_impl;

template <typename... MembersImpl>
struct apply_static_impl<meta_struct_impl<MembersImpl...>> {
  template <typename F>
  constexpr static decltype(auto) apply(F&& f) {
    return f(static_cast<MembersImpl*>(nullptr)...);
  }
};

template <typename MetaStruct, typename F>
auto meta_struct_apply(F&& f) {
  return apply_static_impl<typename MetaStruct::super>::apply(
      std::forward<F>(f));
}

template <fixed_string tag, typename T, auto Init>
decltype(auto) get_impl(member<tag, T, Init>& m) {
  return (m.value);
}

template <fixed_string tag, typename T, auto Init>
decltype(auto) get_impl(const member<tag, T, Init>& m) {
  return (m.value);
}

template <fixed_string tag, typename T, auto Init>
decltype(auto) get_impl(member<tag, T, Init>&& m) {
  return std::move(m.value);
}

template <fixed_string tag, typename MetaStruct>
decltype(auto) get(MetaStruct&& s) {
  return get_impl<tag>(std::forward<MetaStruct>(s));
}

#include <iostream>
#include <string>

int main() {
  using Person = meta_struct<                                                //
      member<"id", int>,                                                     //
      member<"score", int, [](auto& self) { return get<"id">(self) + 1; }>,  //
      member<"name", std::string, [] { return "John"; }>                     //
      >;

   meta_struct_apply<Person>([]<typename... M>(M * ...) {
    std::cout << "The tags are: ";
    ((std::cout << M::tag().sv() << " "), ...);
    std::cout << "\n";
  });

  Person p;
  meta_struct_apply(
      [&](const auto&... m) {
        ((std::cout << m.tag().sv() << ":" << m.value << "\n"), ...);
      },
      p);

}
<|endoftext|>


<commit_before>#include "actuators/motors/MotorDriver/MotorEncoderDriver2013.h"

int main()
{
  MotorEncoderDriver2013 drivertermina;
  int millis = 50000;
  int speed=1.7;
  driver.setVelocities(speed, speed, millis);
}
<commit_msg>fixed some mistypes<commit_after>#include "actuators/motors/MotorDriver/MotorEncoderDriver2013.h"

int main()
{
  MotorEncoderDriver2013 driver;
  int millis = 50000;
  int speed=1.7;
  driver.setVelocities(speed, speed, millis);
}

<|endoftext|>


<commit_before>#include <pcl/common/common.h>
#include <pcl/search/kdtree.h>

#include <eigen3/Eigen/Core>
#include <image_cloud/common/small_helpers.hpp>

#ifndef PCL_DEPTH_FILTER_RADIUS_H_
#define PCL_DEPTH_FILTER_RADIUS_H_

namespace filter_3d
{

template <typename PointT>
inline void
depth_discontinuity_radius(
		const pcl::PointCloud<PointT> &in,
		pcl::PointCloud<PointT> &out,
		float radius = 0.3,
		float epsilon = 0.5,
		float distance_max = 20,
		int min_neigbors = 2
)
{
	boost::shared_ptr<pcl::search::KdTree<PointT> > tree_n( new pcl::search::KdTree<PointT>() );
	tree_n->setInputCloud(in.makeShared());
	tree_n->setEpsilon(epsilon);

	if(radius < 0.01)
	{
		radius = 0.01;
	}

	for(int i = 0; i< in.points.size(); ++i){
		std::vector<int> k_indices;
		std::vector<float> square_distance;

		if ( in.points.at(i).z > distance_max) continue;

		//Position in image is known
		if( tree_n->radiusSearch(i, radius, k_indices, square_distance) < min_neigbors ) continue;

		// look for neighbors where at least 1 is closer (Distance to origin) than the other
		int border = 0;
		float min_x = in.points.at(i).x;
		float max_x = min_x;
		float min_y = in.points.at(i).y;
		float max_y = min_y;
		float min_z = in.points.at(i).z;
		float max_z = min_z;

		for(int n = 0; n < k_indices.size(); ++n){
			// is current point an edge point?
			if(in.points.at(k_indices.at((n))).x > max_x)
			{
				max_x = in.points.at(k_indices.at((n))).x;
			}
			if(in.points.at(k_indices.at((n))).x < min_x)
			{
				min_x = in.points.at(k_indices.at((n))).x;
			}
			if(in.points.at(k_indices.at((n))).y > max_y)
			{
				max_y = in.points.at(k_indices.at((n))).y;
			}
			if(in.points.at(k_indices.at((n))).y < min_y)
			{
				min_y = in.points.at(k_indices.at((n))).y;
			}
			if(in.points.at(k_indices.at((n))).z > max_z)
			{
				max_z = in.points.at(k_indices.at((n))).z;
			}
			if(in.points.at(k_indices.at((n))).z < min_z)
			{
				min_z = in.points.at(k_indices.at((n))).z;
			}
		}

		float threshold = 0.05;
		if( (inRange(min_x, threshold, in.points.at(i).x)
			|| inRange(max_x, threshold, in.points.at(i).x)
			|| min_y == in.points.at(i).y
			//|| inRange(max_y, threshold, in.points.at(i).y)
			)
				//&& inRange(max_z, threshold, in.points.at(i).z)
				//&& inRange(min_z, threshold, in.points.at(i).z)
		)
			{
//		if( min_x + delta <=  in.points.at(i).x
//			|| max_x == in.points.at(i).x
////			|| min_y == in.points.at(i).y
////			|| max_y == in.points.at(i).y
//			|| min_z == in.points.at(i).z
//			|| max_z == in.points.at(i).z )
//		{
			out.push_back(in.points.at(i));
		}
	}
}

}
#endif
<commit_msg>add weight to calculation<commit_after>#include <pcl/common/common.h>
#include <pcl/search/kdtree.h>

#include <eigen3/Eigen/Core>
#include <image_cloud/common/small_helpers.hpp>

#ifndef PCL_DEPTH_FILTER_RADIUS_H_
#define PCL_DEPTH_FILTER_RADIUS_H_

namespace filter_3d
{

template <typename PointT>
inline void
depth_discontinuity_radius(
		const pcl::PointCloud<PointT> &in,
		pcl::PointCloud<PointT> &out,
		float radius = 0.3,
		float epsilon = 0.5,
		float distance_max = 20,
		int min_neigbors = 2
)
{
	boost::shared_ptr<pcl::search::KdTree<PointT> > tree_n( new pcl::search::KdTree<PointT>() );
	tree_n->setInputCloud(in.makeShared());
	tree_n->setEpsilon(epsilon);

	if(radius < 0.01)
	{
		radius = 0.01;
	}

	for(int i = 0; i< in.points.size(); ++i){
		std::vector<int> k_indices;
		std::vector<float> square_distance;

		if ( in.points.at(i).z > distance_max) continue;
		if ( in.points.at(i).z < 1 ) continue;

		//Position in image is known
		if( tree_n->radiusSearch(i, radius, k_indices, square_distance) < min_neigbors ) continue;

		// look for neighbors where at least 1 is closer (Distance to origin) than the other
		int border = 0;
		float min_x = in.points.at(i).x;
		float max_x = min_x;
		float min_y = in.points.at(i).y;
		float max_y = min_y;
		float min_z = in.points.at(i).z;
		float max_z = min_z;
		float avg_z =  in.points.at(i).z;

		for(int n = 0; n < k_indices.size(); ++n){
			// is current point an edge point?
			if(in.points.at(k_indices.at((n))).x > max_x)
			{
				max_x = in.points.at(k_indices.at((n))).x;
			}
			if(in.points.at(k_indices.at((n))).x < min_x)
			{
				min_x = in.points.at(k_indices.at((n))).x;
			}
			if(in.points.at(k_indices.at((n))).y > max_y)
			{
				max_y = in.points.at(k_indices.at((n))).y;
			}
			if(in.points.at(k_indices.at((n))).y < min_y)
			{
				min_y = in.points.at(k_indices.at((n))).y;
			}
			if(in.points.at(k_indices.at((n))).z > max_z)
			{
				max_z = in.points.at(k_indices.at((n))).z;
			}
			if(in.points.at(k_indices.at((n))).z < min_z)
			{
				min_z = in.points.at(k_indices.at((n))).z;
			}
			avg_z+= in.points.at(k_indices.at(n)).z;
		}
		avg_z = avg_z/(k_indices.size()+1);

		float threshold = 0.1;
		if( (inRange(min_x, threshold, in.points.at(i).x)
			|| inRange(max_x, threshold, in.points.at(i).x)
			|| min_y == in.points.at(i).y
			//|| inRange(max_y, threshold, in.points.at(i).y)
			)
				//&& inRange(max_z, threshold, in.points.at(i).z)
				//&& inRange(min_z, threshold, in.points.at(i).z)
		)
			{
//		if( min_x + delta <=  in.points.at(i).x
//			|| max_x == in.points.at(i).x
////			|| min_y == in.points.at(i).y
////			|| max_y == in.points.at(i).y
//			|| min_z == in.points.at(i).z
//			|| max_z == in.points.at(i).z )
//		{
			out.push_back(in.points.at(i));
			out.at(out.size()-1).intensity = (avg_z - min_z) * 0.5; // weight edge
		}
	}
}

}
#endif
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
#define __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__

#include <string>

#include <stout/nothing.hpp>
#include <stout/try.hpp>
#include <stout/windows.hpp>

#include <stout/os/mkdir.hpp>

#include <stout/internal/windows/attributes.hpp>
#include <stout/internal/windows/longpath.hpp>

namespace os {
namespace stat {

// Specify whether symlink path arguments should be followed or
// not. APIs in the os::stat family that take a FollowSymlink
// argument all provide FollowSymlink::FOLLOW_SYMLINK as the default value,
// so they will follow symlinks unless otherwise specified.
enum class FollowSymlink
{
  DO_NOT_FOLLOW_SYMLINK,
  FOLLOW_SYMLINK
};

} // namespace stat {
} // namespace os {

namespace internal {
namespace windows {

// We pass this struct to `DeviceIoControl` to get information about a reparse
// point (including things like whether it's a symlink). It is normally part of
// the Device Driver Kit (DDK), specifically `nitfs.h`, but rather than taking
// a dependency on the DDK, we choose to simply copy the struct here. This is a
// well-worn path used by Boost FS[1], among others. See documentation
// here[2][3].
//
// [1] http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/src/operations.cpp
// [2] https://msdn.microsoft.com/en-us/library/cc232007.aspx
// [3] https://msdn.microsoft.com/en-us/library/cc232005.aspx
typedef struct _REPARSE_DATA_BUFFER
{
  // Describes, among other things, which type of reparse point this is (e.g.,
  // a symlink).
  ULONG ReparseTag;
  // Size in bytes of common portion of the `REPARSE_DATA_BUFFER`.
  USHORT ReparseDataLength;
  // Unused. Ignore.
  USHORT Reserved;
  // Holds symlink data.
  struct
  {
    // Byte offset in `PathBuffer` where the substitute name begins.
    // Calculated as an offset from 0.
    USHORT SubstituteNameOffset;
    // Length in bytes of the substitute name.
    USHORT SubstituteNameLength;
    // Byte offset in `PathBuffer` where the print name begins. Calculated as
    // an offset from 0.
    USHORT PrintNameOffset;
    // Length in bytes of the print name.
    USHORT PrintNameLength;
    // Indicates whether symlink is absolute or relative. If flags containing
    // `SYMLINK_FLAG_RELATIVE`, then the substitute name is a relative
    // symlink.
    ULONG Flags;
    // The path string; the Windows declaration is the first byte, but we
    // declare a suitably sized array so we can use this struct more easily. The
    // "path string" itself is a Unicode `wchar` array containing both
    // substitute name and print name. They can occur in any order. Use the
    // offset and length of each in this struct to calculate where each starts
    // and ends.
    //
    // https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
    WCHAR PathBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  } SymbolicLinkReparseBuffer;
} REPARSE_DATA_BUFFER;

// Convenience struct for holding symlink data, meant purely for internal use.
// We pass this around instead of the `REPARSE_DATA_BUFFER` struct, simply
// because this struct is easier to deal with and reason about.
struct SymbolicLink
{
  std::wstring substitute_name;
  std::wstring print_name;
  ULONG flags;
};

// Checks file/folder attributes for a path to see if the reparse point
// attribute is set; this indicates whether the path points at a reparse point,
// rather than a "normal" file or folder.
inline Try<bool> reparse_point_attribute_set(const std::wstring& absolute_path)
{
  const Try<DWORD> attributes = get_file_attributes(absolute_path.data());
  if (attributes.isError()) {
    return Error(attributes.error());
  }

  return (attributes.get() & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
}

// Attempts to extract symlink data out of a `REPARSE_DATA_BUFFER` (which could
// hold other things, e.g., mount point data).
inline Try<SymbolicLink> build_symbolic_link(const REPARSE_DATA_BUFFER& data)
{
  const bool is_symLink = (data.ReparseTag & IO_REPARSE_TAG_SYMLINK) != 0;
  if (!is_symLink) {
    return Error("Data buffer is not a symlink");
  }

  // NOTE: This buffer is not null terminated.
  const WCHAR* substitute_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
  const size_t substitute_name_length =
    data.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);

  // NOTE: This buffer is not null terminated.
  const WCHAR* print_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR);
  const size_t print_name_length =
    data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR);

  return SymbolicLink{
      std::wstring(substitute_name, substitute_name_length),
      std::wstring(print_name, print_name_length),
      data.SymbolicLinkReparseBuffer.Flags};
}

// Attempts to get a file or folder handle for an absolute path, and does not
// follow symlinks. That is, if the path points at a symlink, the handle will
// refer to the symlink rather than the file or folder the symlink points at.
inline Try<SharedHandle> get_handle_no_follow(const std::string& absolute_path)
{
  const Try<DWORD> attributes = ::internal::windows::get_file_attributes(
      ::internal::windows::longpath(absolute_path));

  if (attributes.isError()) {
    return Error(attributes.error());
  }

  bool resolved_path_is_directory = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;

  // NOTE: According to the `CreateFile` documentation[1], the `OPEN_EXISTING`
  // and `FILE_FLAG_OPEN_REPARSE_POINT` flags need to be used when getting a
  // handle for the symlink.
  //
  // Note also that `CreateFile` will appropriately generate a handle for
  // either a folder or a file, as long as the appropriate flag is being set:
  // `FILE_FLAG_BACKUP_SEMANTICS` or `FILE_FLAG_OPEN_REPARSE_POINT`.
  //
  // The `FILE_FLAG_BACKUP_SEMANTICS` flag is being set whenever the target is
  // a directory. According to MSDN[1]: "You must set this flag to obtain a
  // handle to a directory. A directory handle can be passed to some functions
  // instead of a file handle". More `FILE_FLAG_BACKUP_SEMANTICS` documentation
  // can be found in MSDN[2].
  //
  // The `GENERIC_READ` flag is being used because it's the most common way of
  // opening a file for reading only. The `FILE_SHARE_READ` allows other
  // processes to read the file at the same time. MSDN[1] provides a more
  // detailed explanation of these flags.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx
  const DWORD access_flags = resolved_path_is_directory
    ? (FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)
    : FILE_FLAG_OPEN_REPARSE_POINT;

  const HANDLE handle = ::CreateFileW(
      longpath(absolute_path).data(),
      GENERIC_READ,     // Open the file for reading only.
      FILE_SHARE_READ,  // Just reading this file, allow others to do the same.
      nullptr,          // Ignored.
      OPEN_EXISTING,    // Open existing symlink.
      access_flags,     // Open symlink, not the file it points to.
      nullptr);         // Ignored.

  if (handle == INVALID_HANDLE_VALUE) {
    return WindowsError(
        "'internal::windows::get_handle_no_follow': 'CreateFile' call failed "
        "at path '" + absolute_path + "'");
  }

  return SharedHandle(handle, CloseHandle);
}

// Attempts to get the symlink data for a file or folder handle.
inline Try<SymbolicLink> get_symbolic_link_data(const HANDLE handle)
{
  // To get the symlink data, we call `DeviceIoControl`. This function is part
  // of the Device Driver Kit (DDK)[1] and, along with `FSCTL_GET_REPARSE_POINT`
  // is used to emit information about reparse points (and, thus, symlinks,
  // since symlinks are implemented with reparse points). This technique is
  // being used in Boost FS code as well[2].
  //
  // Summarized, the documentation tells us that we need to pass in
  // `FSCTL_GET_REPARSE_POINT` to get the function to populate a
  // `REPARSE_DATA_BUFFER` struct with data about a reparse point.
  // The `REPARSE_DATA_BUFFER` struct is defined in a DDK header file,
  // so to avoid bringing in a multitude of DDK headers we take a cue from
  // Boost FS, and copy the struct into this header (see above).
  //
  // Finally, for context, it may be worth looking at the MSDN
  // documentation[3] for `DeviceIoControl` itself.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
  // [2] https://svn.boost.org/trac/boost/ticket/4663
  // [3] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
  REPARSE_DATA_BUFFER buffer;
  DWORD ignored = 0;

  // The semantics of this function are: get the reparse data associated with
  // the `handle` of some open directory or file, and that data in
  // `reparse_point_data`.
  const BOOL reparse_data_obtained = ::DeviceIoControl(
      handle,                   // Handle to file or directory.
      FSCTL_GET_REPARSE_POINT,  // Gets reparse point data for file/folder.
      nullptr,                  // Ignored.
      0,                        // Ignored.
      &buffer,
      sizeof(buffer),
      &ignored,                 // Ignored.
      nullptr);                 // Ignored.

  if (!reparse_data_obtained) {
    return WindowsError(
        "'internal::windows::get_symbolic_link_data': 'DeviceIoControl' call "
        "failed");
  }

  return build_symbolic_link(buffer);
}

// Creates a reparse point with the specified target. The target can be either
// a file (in which case a junction is created), or a folder (in which case a
// mount point is created).
inline Try<Nothing> create_symbolic_link(
    const std::string& target,
    const std::string& reparse_point)
{
  // Determine if target is a folder or a file. This makes a difference
  // in the way we call `create_symbolic_link`.
  const Try<DWORD> attributes = get_file_attributes(longpath(target));

  bool target_is_folder = false;
  if (attributes.isSome()) {
    target_is_folder = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;
  }

  // Bail out if target is already a reparse point.
  Try<bool> attribute_set = reparse_point_attribute_set(longpath(target));
  if (attribute_set.isSome() && attribute_set.get()) {
    return Error(
        "Path '" + target + "' is already a reparse point");
  }

  // Avoid requiring administrative privileges to create symbolic links.
  DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
  if (target_is_folder) {
    flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
  }

  // `CreateSymbolicLink` normally adjusts the process token's privileges to
  // allow for symlink creation; however, we explicitly avoid this with the
  // above flag.
  if (!::CreateSymbolicLinkW(
          // Path to link.
          longpath(reparse_point).data(),
          // Path to target.
          longpath(target).data(),
          flags)) {
    return WindowsError();
  }

  return Nothing();
}

} // namespace windows {
} // namespace internal {

#endif // __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
<commit_msg>Windows: Added internal `get_handle_follow` which follows symlinks.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
#define __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__

#include <string>

#include <stout/nothing.hpp>
#include <stout/try.hpp>
#include <stout/windows.hpp>

#include <stout/os/mkdir.hpp>

#include <stout/internal/windows/attributes.hpp>
#include <stout/internal/windows/longpath.hpp>

namespace os {
namespace stat {

// Specify whether symlink path arguments should be followed or
// not. APIs in the os::stat family that take a FollowSymlink
// argument all provide FollowSymlink::FOLLOW_SYMLINK as the default value,
// so they will follow symlinks unless otherwise specified.
enum class FollowSymlink
{
  DO_NOT_FOLLOW_SYMLINK,
  FOLLOW_SYMLINK
};

} // namespace stat {
} // namespace os {

namespace internal {
namespace windows {

// We pass this struct to `DeviceIoControl` to get information about a reparse
// point (including things like whether it's a symlink). It is normally part of
// the Device Driver Kit (DDK), specifically `nitfs.h`, but rather than taking
// a dependency on the DDK, we choose to simply copy the struct here. This is a
// well-worn path used by Boost FS[1], among others. See documentation
// here[2][3].
//
// [1] http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/src/operations.cpp // NOLINT(whitespace/line_length)
// [2] https://msdn.microsoft.com/en-us/library/cc232007.aspx
// [3] https://msdn.microsoft.com/en-us/library/cc232005.aspx
typedef struct _REPARSE_DATA_BUFFER
{
  // Describes, among other things, which type of reparse point this is (e.g.,
  // a symlink).
  ULONG ReparseTag;
  // Size in bytes of common portion of the `REPARSE_DATA_BUFFER`.
  USHORT ReparseDataLength;
  // Unused. Ignore.
  USHORT Reserved;
  // Holds symlink data.
  struct
  {
    // Byte offset in `PathBuffer` where the substitute name begins.
    // Calculated as an offset from 0.
    USHORT SubstituteNameOffset;
    // Length in bytes of the substitute name.
    USHORT SubstituteNameLength;
    // Byte offset in `PathBuffer` where the print name begins. Calculated as
    // an offset from 0.
    USHORT PrintNameOffset;
    // Length in bytes of the print name.
    USHORT PrintNameLength;
    // Indicates whether symlink is absolute or relative. If flags containing
    // `SYMLINK_FLAG_RELATIVE`, then the substitute name is a relative
    // symlink.
    ULONG Flags;
    // The path string; the Windows declaration is the first byte, but we
    // declare a suitably sized array so we can use this struct more easily. The
    // "path string" itself is a Unicode `wchar` array containing both
    // substitute name and print name. They can occur in any order. Use the
    // offset and length of each in this struct to calculate where each starts
    // and ends.
    //
    // https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx // NOLINT(whitespace/line_length)
    WCHAR PathBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  } SymbolicLinkReparseBuffer;
} REPARSE_DATA_BUFFER;

// Convenience struct for holding symlink data, meant purely for internal use.
// We pass this around instead of the `REPARSE_DATA_BUFFER` struct, simply
// because this struct is easier to deal with and reason about.
struct SymbolicLink
{
  std::wstring substitute_name;
  std::wstring print_name;
  ULONG flags;
};

// Checks file/folder attributes for a path to see if the reparse point
// attribute is set; this indicates whether the path points at a reparse point,
// rather than a "normal" file or folder.
inline Try<bool> reparse_point_attribute_set(const std::wstring& absolute_path)
{
  const Try<DWORD> attributes = get_file_attributes(absolute_path.data());
  if (attributes.isError()) {
    return Error(attributes.error());
  }

  return (attributes.get() & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
}

// Attempts to extract symlink data out of a `REPARSE_DATA_BUFFER` (which could
// hold other things, e.g., mount point data).
inline Try<SymbolicLink> build_symbolic_link(const REPARSE_DATA_BUFFER& data)
{
  const bool is_symLink = (data.ReparseTag & IO_REPARSE_TAG_SYMLINK) != 0;
  if (!is_symLink) {
    return Error("Data buffer is not a symlink");
  }

  // NOTE: This buffer is not null terminated.
  const WCHAR* substitute_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
  const size_t substitute_name_length =
    data.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);

  // NOTE: This buffer is not null terminated.
  const WCHAR* print_name =
    data.SymbolicLinkReparseBuffer.PathBuffer +
    data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR);
  const size_t print_name_length =
    data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR);

  return SymbolicLink{
      std::wstring(substitute_name, substitute_name_length),
      std::wstring(print_name, print_name_length),
      data.SymbolicLinkReparseBuffer.Flags};
}

// Attempts to get a file or folder handle for an absolute path, and follows
// symlinks. That is, if the path points at a symlink, the handle will refer to
// the file or folder the symlink points at, rather than the symlink itself.
inline Try<SharedHandle> get_handle_follow(const std::string& absolute_path)
{
  const Try<DWORD> attributes = get_file_attributes(longpath(absolute_path));

  if (attributes.isError()) {
    return Error(attributes.error());
  }

  bool resolved_path_is_directory = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;

  // NOTE: The name of `CreateFile` is misleading: it is also used to retrieve
  // handles to existing files or directories as if it were actually `OpenPath`
  // (which does not exist). We use `OPEN_EXISTING` but not
  // `FILE_FLAG_OPEN_REPARSE_POINT` to explicitily follow (resolve) symlinks in
  // the path to the file or directory.
  //
  // Note also that `CreateFile` will appropriately generate a handle for
  // either a folder or a file, as long as the appropriate flag is being set:
  // `FILE_FLAG_BACKUP_SEMANTICS`.
  //
  // The `FILE_FLAG_BACKUP_SEMANTICS` flag is being set whenever the target is
  // a directory. According to MSDN[1]: "You must set this flag to obtain a
  // handle to a directory. A directory handle can be passed to some functions
  // instead of a file handle". More `FILE_FLAG_BACKUP_SEMANTICS` documentation
  // can be found in MSDN[2].
  //
  // The `GENERIC_READ` flag is being used because it's the most common way of
  // opening a file for reading only. The `FILE_SHARE_READ` allows other
  // processes to read the file at the same time. MSDN[1] provides a more
  // detailed explanation of these flags.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx // NOLINT(whitespace/line_length)
  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx // NOLINT(whitespace/line_length)
  const DWORD access_flags = resolved_path_is_directory
    ? FILE_FLAG_BACKUP_SEMANTICS
    : FILE_ATTRIBUTE_NORMAL;

  const HANDLE handle = ::CreateFileW(
      longpath(absolute_path).data(),
      GENERIC_READ,     // Open the file for reading only.
      FILE_SHARE_READ,  // Just reading this file, allow others to do the same.
      nullptr,          // Ignored.
      OPEN_EXISTING,    // Open existing file.
      access_flags,     // Open file, not the symlink itself.
      nullptr);         // Ignored.

  if (handle == INVALID_HANDLE_VALUE) {
    return WindowsError();
  }

  return SharedHandle(handle, ::CloseHandle);
}

// Attempts to get a file or folder handle for an absolute path, and does not
// follow symlinks. That is, if the path points at a symlink, the handle will
// refer to the symlink rather than the file or folder the symlink points at.
inline Try<SharedHandle> get_handle_no_follow(const std::string& absolute_path)
{
  const Try<DWORD> attributes = get_file_attributes(longpath(absolute_path));

  if (attributes.isError()) {
    return Error(attributes.error());
  }

  bool resolved_path_is_directory = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;

  // NOTE: According to the `CreateFile` documentation[1], the `OPEN_EXISTING`
  // and `FILE_FLAG_OPEN_REPARSE_POINT` flags need to be used when getting a
  // handle for the symlink.
  //
  // Note also that `CreateFile` will appropriately generate a handle for
  // either a folder or a file, as long as the appropriate flag is being set:
  // `FILE_FLAG_BACKUP_SEMANTICS` or `FILE_FLAG_OPEN_REPARSE_POINT`.
  //
  // The `FILE_FLAG_BACKUP_SEMANTICS` flag is being set whenever the target is
  // a directory. According to MSDN[1]: "You must set this flag to obtain a
  // handle to a directory. A directory handle can be passed to some functions
  // instead of a file handle". More `FILE_FLAG_BACKUP_SEMANTICS` documentation
  // can be found in MSDN[2].
  //
  // The `GENERIC_READ` flag is being used because it's the most common way of
  // opening a file for reading only. The `FILE_SHARE_READ` allows other
  // processes to read the file at the same time. MSDN[1] provides a more
  // detailed explanation of these flags.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx // NOLINT(whitespace/line_length)
  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx // NOLINT(whitespace/line_length)
  const DWORD access_flags = resolved_path_is_directory
    ? (FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)
    : FILE_FLAG_OPEN_REPARSE_POINT;

  const HANDLE handle = ::CreateFileW(
      longpath(absolute_path).data(),
      GENERIC_READ,     // Open the file for reading only.
      FILE_SHARE_READ,  // Just reading this file, allow others to do the same.
      nullptr,          // Ignored.
      OPEN_EXISTING,    // Open existing symlink.
      access_flags,     // Open symlink, not the file it points to.
      nullptr);         // Ignored.

  if (handle == INVALID_HANDLE_VALUE) {
    return WindowsError();
  }

  return SharedHandle(handle, ::CloseHandle);
}

// Attempts to get the symlink data for a file or folder handle.
inline Try<SymbolicLink> get_symbolic_link_data(const HANDLE handle)
{
  // To get the symlink data, we call `DeviceIoControl`. This function is part
  // of the Device Driver Kit (DDK)[1] and, along with `FSCTL_GET_REPARSE_POINT`
  // is used to emit information about reparse points (and, thus, symlinks,
  // since symlinks are implemented with reparse points). This technique is
  // being used in Boost FS code as well[2].
  //
  // Summarized, the documentation tells us that we need to pass in
  // `FSCTL_GET_REPARSE_POINT` to get the function to populate a
  // `REPARSE_DATA_BUFFER` struct with data about a reparse point.
  // The `REPARSE_DATA_BUFFER` struct is defined in a DDK header file,
  // so to avoid bringing in a multitude of DDK headers we take a cue from
  // Boost FS, and copy the struct into this header (see above).
  //
  // Finally, for context, it may be worth looking at the MSDN
  // documentation[3] for `DeviceIoControl` itself.
  //
  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx // NOLINT(whitespace/line_length)
  // [2] https://svn.boost.org/trac/boost/ticket/4663
  // [3] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx // NOLINT(whitespace/line_length)
  REPARSE_DATA_BUFFER buffer;
  DWORD ignored = 0;

  // The semantics of this function are: get the reparse data associated with
  // the `handle` of some open directory or file, and that data in
  // `reparse_point_data`.
  const BOOL reparse_data_obtained = ::DeviceIoControl(
      handle,                   // Handle to file or directory.
      FSCTL_GET_REPARSE_POINT,  // Gets reparse point data for file/folder.
      nullptr,                  // Ignored.
      0,                        // Ignored.
      &buffer,
      sizeof(buffer),
      &ignored,                 // Ignored.
      nullptr);                 // Ignored.

  if (!reparse_data_obtained) {
    return WindowsError(
        "'internal::windows::get_symbolic_link_data': 'DeviceIoControl' call "
        "failed");
  }

  return build_symbolic_link(buffer);
}

// Creates a reparse point with the specified target. The target can be either
// a file (in which case a junction is created), or a folder (in which case a
// mount point is created).
inline Try<Nothing> create_symbolic_link(
    const std::string& target,
    const std::string& reparse_point)
{
  // Determine if target is a folder or a file. This makes a difference
  // in the way we call `create_symbolic_link`.
  const Try<DWORD> attributes = get_file_attributes(longpath(target));

  bool target_is_folder = false;
  if (attributes.isSome()) {
    target_is_folder = attributes.get() & FILE_ATTRIBUTE_DIRECTORY;
  }

  // Bail out if target is already a reparse point.
  Try<bool> attribute_set = reparse_point_attribute_set(longpath(target));
  if (attribute_set.isSome() && attribute_set.get()) {
    return Error(
        "Path '" + target + "' is already a reparse point");
  }

  // Avoid requiring administrative privileges to create symbolic links.
  DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
  if (target_is_folder) {
    flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
  }

  // `CreateSymbolicLink` normally adjusts the process token's privileges to
  // allow for symlink creation; however, we explicitly avoid this with the
  // above flag.
  if (!::CreateSymbolicLinkW(
          // Path to link.
          longpath(reparse_point).data(),
          // Path to target.
          longpath(target).data(),
          flags)) {
    return WindowsError();
  }

  return Nothing();
}

} // namespace windows {
} // namespace internal {

#endif // __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
<|endoftext|>


<commit_before>// [WriteFile Name=GODictionaryRenderer, Category=DisplayInformation]
// [Legal]
// Copyright 2016 Esri.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [Legal]

#include "GODictionaryRenderer.h"

#include <QQmlProperty>

#include "DictionaryRenderer.h"
#include "GraphicListModel.h"
#include "Map.h"
#include "MapQuickView.h"
#include "PolygonBuilder.h"
#include "MultipointBuilder.h"
#include "DictionarySymbolStyle.h"

using namespace Esri::ArcGISRuntime;

const QString GODictionaryRenderer::FIELD_CONTROL_POINTS = QStringLiteral("_control_points");
const QString GODictionaryRenderer::FIELD_WKID = QStringLiteral("_wkid");

GODictionaryRenderer::GODictionaryRenderer(QQuickItem* parent) :
  QQuickItem(parent)
{
}

GODictionaryRenderer::~GODictionaryRenderer()
{
}

void GODictionaryRenderer::init()
{
  qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView");
  qmlRegisterType<GODictionaryRenderer>("Esri.Samples", 1, 0, "GODictionaryRendererSample");
}

bool GODictionaryRenderer::graphicsLoaded() const
{
  return m_graphicsLoaded;
}

void GODictionaryRenderer::componentComplete()
{
  QQuickItem::componentComplete();

  // QML properties
  m_dataPath = QQmlProperty::read(this, "dataPath").toUrl().toLocalFile();

  //! [Apply Dictionary Renderer Graphics Overlay Cpp]
  // Create graphics overlay
  m_graphicsOverlay = new GraphicsOverlay(this);

  // Create dictionary renderer and apply to the graphics overlay
  DictionarySymbolStyle* dictionarySymbolStyle = new DictionarySymbolStyle("mil2525d", m_dataPath + "/styles/mil2525d.stylx", this);
  DictionaryRenderer* renderer = new DictionaryRenderer(dictionarySymbolStyle, this);
  m_graphicsOverlay->setRenderer(renderer);
  //! [Apply Dictionary Renderer Graphics Overlay Cpp]

  // Create a map and give it to the MapView
  m_mapView = findChild<MapQuickView*>("mapView");
  m_map = new Map(Basemap::topographic(this), this);

  parseXmlFile();
  m_mapView->graphicsOverlays()->append(m_graphicsOverlay);

  m_mapView->setMap(m_map);

  connect(m_map, &Map::doneLoading, this, [this]()
  {
    zoomToGraphics();
  });
}

void GODictionaryRenderer::parseXmlFile()
{
  bool readingMessage = false;
  QVariantMap elementValues;
  QString currentElementName;

  QFile xmlFile(m_dataPath + "/xml/Mil2525DMessages.xml");
  // Open the file for reading
  if (xmlFile.isOpen())
  {
    xmlFile.reset();
  }
  else
  {
    xmlFile.open(QIODevice::ReadOnly | QIODevice::Text);
  }
  m_xmlParser.setDevice(&xmlFile);

  // Traverse the XML in a loop
  while (!m_xmlParser.atEnd())
  {
    m_xmlParser.readNext();

    // Is this the start or end of a message element?
    if (m_xmlParser.name() == "message")
    {
      if (!readingMessage)
      {
        // This is the start of a message element.
        elementValues.clear();
      }
      else
      {
        /**
                 * This is the end of a message element. Here we have a complete message that defines
                 * a military feature to display on the map. Create a graphic from its attributes.
                 */
        createGraphic(elementValues);
      }
      // Either we just started reading a message, or we just finished reading a message.
      readingMessage = !readingMessage;
    }
    // Are we already inside a message element?
    else if (readingMessage)
    {
      // Is this the start of an element inside a message?
      if (m_xmlParser.isStartElement())
      {
        // Remember which element we're reading
        currentElementName = m_xmlParser.name().toString();
      }
      // Is this text?
      else if (m_xmlParser.isCharacters())
      {
        // Is this text inside an element?
        if (!currentElementName.isEmpty())
        {
          // Get the text and store it as the current element's value
          QStringRef trimmedText = m_xmlParser.text().trimmed();
          if (!trimmedText.isEmpty())
          {
            elementValues[currentElementName] = trimmedText.toString();
          }
        }
      }
    }
  }

  emit graphicsLoadedChanged();
}

void GODictionaryRenderer::createGraphic(QVariantMap rawAttributes)
{
  // If _wkid was absent, use WGS 1984 (4326) by default.
  int wkid = rawAttributes.keys().contains(FIELD_WKID) ? rawAttributes[FIELD_WKID].toInt() : 4326;
  SpatialReference sr(wkid);
  Geometry geom;
  QStringList pointStrings = rawAttributes[FIELD_CONTROL_POINTS].toString().split(";");
  if (pointStrings.length() == 1)
  {
    // It's a point
    QStringList coords = pointStrings[0].split(",");
    geom = Point(coords[0].toDouble(), coords[1].toDouble(), sr);
  }
  else {
    // It's a multipoint
    auto builder = new MultipointBuilder(sr, this);
    PointCollection* collection = new PointCollection(sr, this);
    for (const auto& pointString : pointStrings)
    {
      QStringList coords = pointString.split(",");
      if (coords.length() >= 2)
        collection->addPoint(coords[0].toDouble(), coords[1].toDouble());
    }
    builder->setPoints(collection);
    geom = builder->toGeometry();
  }

  if (!geom.isEmpty())
  {
    // Get rid of _control_points and _wkid. They are not needed in the graphic's
    // attributes.
    rawAttributes.remove(FIELD_CONTROL_POINTS);
    rawAttributes.remove(FIELD_WKID);

    Graphic* graphic = new Graphic(geom, rawAttributes, this);
    m_graphicsOverlay->graphics()->append(graphic);
  }
}

void GODictionaryRenderer::zoomToGraphics()
{
  if (m_graphicsOverlay)
    m_mapView->setViewpointGeometry(m_graphicsOverlay->extent(), 20);
}
<commit_msg>Update GODictionaryRenderer.cpp<commit_after>// [WriteFile Name=GODictionaryRenderer, Category=DisplayInformation]
// [Legal]
// Copyright 2016 Esri.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [Legal]

#include "GODictionaryRenderer.h"

#include <QQmlProperty>

#include "DictionaryRenderer.h"
#include "GraphicListModel.h"
#include "Map.h"
#include "MapQuickView.h"
#include "PolygonBuilder.h"
#include "MultipointBuilder.h"
#include "DictionarySymbolStyle.h"

using namespace Esri::ArcGISRuntime;

const QString GODictionaryRenderer::FIELD_CONTROL_POINTS = QStringLiteral("_control_points");
const QString GODictionaryRenderer::FIELD_WKID = QStringLiteral("_wkid");

GODictionaryRenderer::GODictionaryRenderer(QQuickItem* parent) :
  QQuickItem(parent)
{
}

GODictionaryRenderer::~GODictionaryRenderer()
{
}

void GODictionaryRenderer::init()
{
  qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView");
  qmlRegisterType<GODictionaryRenderer>("Esri.Samples", 1, 0, "GODictionaryRendererSample");
}

bool GODictionaryRenderer::graphicsLoaded() const
{
  return m_graphicsLoaded;
}

void GODictionaryRenderer::componentComplete()
{
  QQuickItem::componentComplete();

  // QML properties
  m_dataPath = QQmlProperty::read(this, "dataPath").toUrl().toLocalFile();

  //! [Apply Dictionary Renderer Graphics Overlay Cpp]
  // Create graphics overlay
  m_graphicsOverlay = new GraphicsOverlay(this);

  // Create dictionary renderer and apply to the graphics overlay
  DictionarySymbolStyle* dictionarySymbolStyle = new DictionarySymbolStyle("mil2525d", m_dataPath + "/styles/mil2525d.stylx", this);
  DictionaryRenderer* renderer = new DictionaryRenderer(dictionarySymbolStyle, this);
  m_graphicsOverlay->setRenderer(renderer);
  //! [Apply Dictionary Renderer Graphics Overlay Cpp]

  // Create a map and give it to the MapView
  m_mapView = findChild<MapQuickView*>("mapView");
  m_map = new Map(Basemap::topographic(this), this);

  parseXmlFile();
  m_mapView->graphicsOverlays()->append(m_graphicsOverlay);

  connect(m_map, &Map::doneLoading, this, [this]()
  {
    zoomToGraphics();
  });

  m_mapView->setMap(m_map);
}

void GODictionaryRenderer::parseXmlFile()
{
  bool readingMessage = false;
  QVariantMap elementValues;
  QString currentElementName;

  QFile xmlFile(m_dataPath + "/xml/Mil2525DMessages.xml");
  // Open the file for reading
  if (xmlFile.isOpen())
  {
    xmlFile.reset();
  }
  else
  {
    xmlFile.open(QIODevice::ReadOnly | QIODevice::Text);
  }
  m_xmlParser.setDevice(&xmlFile);

  // Traverse the XML in a loop
  while (!m_xmlParser.atEnd())
  {
    m_xmlParser.readNext();

    // Is this the start or end of a message element?
    if (m_xmlParser.name() == "message")
    {
      if (!readingMessage)
      {
        // This is the start of a message element.
        elementValues.clear();
      }
      else
      {
        /**
                 * This is the end of a message element. Here we have a complete message that defines
                 * a military feature to display on the map. Create a graphic from its attributes.
                 */
        createGraphic(elementValues);
      }
      // Either we just started reading a message, or we just finished reading a message.
      readingMessage = !readingMessage;
    }
    // Are we already inside a message element?
    else if (readingMessage)
    {
      // Is this the start of an element inside a message?
      if (m_xmlParser.isStartElement())
      {
        // Remember which element we're reading
        currentElementName = m_xmlParser.name().toString();
      }
      // Is this text?
      else if (m_xmlParser.isCharacters())
      {
        // Is this text inside an element?
        if (!currentElementName.isEmpty())
        {
          // Get the text and store it as the current element's value
          QStringRef trimmedText = m_xmlParser.text().trimmed();
          if (!trimmedText.isEmpty())
          {
            elementValues[currentElementName] = trimmedText.toString();
          }
        }
      }
    }
  }

  emit graphicsLoadedChanged();
}

void GODictionaryRenderer::createGraphic(QVariantMap rawAttributes)
{
  // If _wkid was absent, use WGS 1984 (4326) by default.
  int wkid = rawAttributes.keys().contains(FIELD_WKID) ? rawAttributes[FIELD_WKID].toInt() : 4326;
  SpatialReference sr(wkid);
  Geometry geom;
  QStringList pointStrings = rawAttributes[FIELD_CONTROL_POINTS].toString().split(";");
  if (pointStrings.length() == 1)
  {
    // It's a point
    QStringList coords = pointStrings[0].split(",");
    geom = Point(coords[0].toDouble(), coords[1].toDouble(), sr);
  }
  else {
    // It's a multipoint
    auto builder = new MultipointBuilder(sr, this);
    PointCollection* collection = new PointCollection(sr, this);
    for (const auto& pointString : pointStrings)
    {
      QStringList coords = pointString.split(",");
      if (coords.length() >= 2)
        collection->addPoint(coords[0].toDouble(), coords[1].toDouble());
    }
    builder->setPoints(collection);
    geom = builder->toGeometry();
  }

  if (!geom.isEmpty())
  {
    // Get rid of _control_points and _wkid. They are not needed in the graphic's
    // attributes.
    rawAttributes.remove(FIELD_CONTROL_POINTS);
    rawAttributes.remove(FIELD_WKID);

    Graphic* graphic = new Graphic(geom, rawAttributes, this);
    m_graphicsOverlay->graphics()->append(graphic);
  }
}

void GODictionaryRenderer::zoomToGraphics()
{
  if (m_graphicsOverlay)
    m_mapView->setViewpointGeometry(m_graphicsOverlay->extent(), 20);
}
<|endoftext|>


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

#include "PipeWireOut.h"

#include <musikcore/sdk/constants.h>
#include <musikcore/sdk/IPreferences.h>
#include <musikcore/sdk/ISchema.h>
#include <spa/param/audio/format-utils.h>
#include <spa/utils/result.h>
#include <unistd.h>
#include <iostream>
#include <chrono>

constexpr size_t SAMPLES_PER_BUFFER = 2048;
constexpr size_t SAMPLE_SIZE_BYTES = sizeof(float);
constexpr size_t MAX_BUFFERS = 16;

static IPreferences* prefs = nullptr;

extern "C" void SetPreferences(IPreferences* prefs) {
    ::prefs = prefs;
}

extern "C" musik::core::sdk::ISchema* GetSchema() {
    auto schema = new TSchema<>();
    return schema;
}

void PipeWireOut::OnStreamStateChanged(void* data, enum pw_stream_state old, enum pw_stream_state state, const char* error) {
    std::cerr << "[PipeWire] state changed from " << old << " to " << state << "\n";
}

void PipeWireOut::OnDrained(void* data) {
    std::cerr << "[PipeWire] drained\n";
    PipeWireOut* self = static_cast<PipeWireOut*>(data);
    self->drainCondition.notify_all();
}

void PipeWireOut::OnStreamProcess(void* data) {
    PipeWireOut* self = static_cast<PipeWireOut*>(data);

    {
        std::unique_lock<std::recursive_mutex> lock(self->mutex);

        if (self->state != State::Playing) {
            return;
        }

        OutBufferContext& outContext = self->outBufferContext;

        if (!outContext.Valid()) {
            outContext.Initialize(pw_stream_dequeue_buffer(self->pwStream));
            if (!outContext.Valid()) {
                // std::cerr << "[PipeWire] no more output buffers available to fill\n";
                return;
            }
        }

        uint32_t channelCount;

        while (outContext.remaining > 0 && !self->buffers.empty()) {
            //std::cerr << "[PipeWire] " << outContext.remaining << " bytes still need to be filled...\n";

            InBufferContext* inContext = self->buffers.front();
            channelCount = (uint32_t) inContext->buffer->Channels();
            uint32_t inBufferSize = (uint32_t) inContext->buffer->Bytes();
            uint32_t bytesToCopy = std::min(outContext.remaining, inContext->remaining);

            if (outContext.remaining >= inContext->remaining) {
                self->buffers.pop_front();
                self->bufferCondition.notify_all();
            }

            memcpy(outContext.writePtr, inContext->readPtr, bytesToCopy);
            inContext->Advance(bytesToCopy); /* will `delete this` if emptied */
            outContext.Advance(bytesToCopy);
        }

        if (outContext.remaining == 0) {
            outContext.Finalize(self->pwStream, SAMPLE_SIZE_BYTES * channelCount);
        }
    }
}

PipeWireOut::PipeWireOut() {
    this->pwStreamEvents = { PW_VERSION_STREAM_EVENTS };
    this->pwStreamEvents.state_changed = PipeWireOut::OnStreamStateChanged;
    this->pwStreamEvents.process = PipeWireOut::OnStreamProcess;
    this->pwStreamEvents.drained = PipeWireOut::OnDrained;
}

PipeWireOut::~PipeWireOut() {
    this->StopPipeWire();
}

void PipeWireOut::Release() {
    delete this;
}

void PipeWireOut::Pause() {
    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        this->state = State::Paused;
    }
    {
        if (this->pwThreadLoop && this->pwStream) {
            pw_thread_loop_lock(this->pwThreadLoop);
            pw_stream_set_active(this->pwStream, false);
            pw_thread_loop_unlock(this->pwThreadLoop);
        }
    }
}

void PipeWireOut::Resume() {
    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        this->state = State::Playing;
    }
    {
        if (this->pwThreadLoop && this->pwStream) {
            pw_thread_loop_lock(this->pwThreadLoop);
            pw_stream_set_active(this->pwStream, true);
            pw_thread_loop_unlock(this->pwThreadLoop);
        }
    }
}

void PipeWireOut::SetVolume(double volume) {
    //pw_stream_set_control(stream->stream, SPA_PROP_channelVolumes, stream->volume.channels, stream->volume.values, 0);
    /* CAL TODO */
    this->volume = volume;
}

double PipeWireOut::GetVolume() {
    return this->volume;
}

void PipeWireOut::Stop() {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    this->DiscardInputBuffers();
    this->state = State::Stopped;
    if (this->pwThreadLoop && this->pwStream) {
        pw_thread_loop_lock(this->pwThreadLoop);
        pw_stream_set_active(this->pwStream, false);
        pw_stream_flush(this->pwStream, false);
        pw_thread_loop_unlock(this->pwThreadLoop);
    }
}

void PipeWireOut::DiscardInputBuffers() {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    for (auto& buffer : this->buffers) {
        buffer->Discard();
    }
    this->buffers.clear();
    this->bufferCondition.notify_all();
}

void PipeWireOut::Drain() {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    while (this->buffers.size()) {
        bufferCondition.wait(lock);
    }
    if (this->pwThreadLoop && this->pwStream) {
        pw_thread_loop_lock(this->pwThreadLoop);
        pw_stream_flush(this->pwStream, true);
        pw_thread_loop_unlock(this->pwThreadLoop);
        drainCondition.wait_for(lock, std::chrono::milliseconds(10000));
    }
}

IDeviceList* PipeWireOut::GetDeviceList() {
    /* CAL TODO */
    return nullptr;
}

bool PipeWireOut::SetDefaultDevice(const char* deviceId) {
    /* CAL TODO */
    return false;
}

IDevice* PipeWireOut::GetDefaultDevice() {
    /* CAL TODO */
    return nullptr;
}

void PipeWireOut::StopPipeWire() {
    std::cerr << "[PipeWire] shutdown started...\n";

    this->Stop();

    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        this->state = State::Shutdown;
    }

    if (this->pwThreadLoop) {
        pw_thread_loop_stop(this->pwThreadLoop);

        if (this->pwStream) {
            this->outBufferContext.Finalize(
                this->pwStream,
                SAMPLE_SIZE_BYTES * this->channelCount);
            pw_stream_destroy(this->pwStream);
            this->pwStream = nullptr;
        }

        pw_thread_loop_destroy(this->pwThreadLoop);
        this->pwThreadLoop = nullptr;
    }

    this->initialized = false;
    this->channelCount = 0;
    this->sampleRate = 0;

    std::cerr << "[PipeWire] shutdown complete.\n";
}

bool PipeWireOut::StartPipeWire(IBuffer* buffer) {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);

    pw_init(nullptr, nullptr);

    this->pwThreadLoop = pw_thread_loop_new("musikcube", nullptr);
    if (this->pwThreadLoop) {
        int result;

        if ((result = pw_thread_loop_start(this->pwThreadLoop)) != 0) {
            std::cerr << "[PipeWire] error starting thread loop: " << spa_strerror(result) << "\n";
            goto cleanup;
        };

        pw_thread_loop_lock(this->pwThreadLoop);

        this->pwStream = pw_stream_new_simple(
            pw_thread_loop_get_loop(this->pwThreadLoop),
            "musikcube",
            pw_properties_new(
                PW_KEY_MEDIA_TYPE, "Audio",
                PW_KEY_MEDIA_CATEGORY, "Playback",
                PW_KEY_MEDIA_ROLE, "Music",
                NULL),
            &this->pwStreamEvents,
            this);

        if (this->pwStream) {
            uint8_t builderBuffer[4096];
            spa_pod_builder builder = SPA_POD_BUILDER_INIT(builderBuffer, sizeof(builderBuffer));
            const spa_pod *params[2];

            this->channelCount = buffer->Channels();
            this->sampleRate = buffer->SampleRate();

            spa_audio_info_raw audioInfo;
            spa_zero(audioInfo);
            audioInfo.format = SPA_AUDIO_FORMAT_F32;
            audioInfo.channels = (uint32_t) this->channelCount;
            audioInfo.rate = (uint32_t) this->sampleRate;

            params[0] = spa_format_audio_raw_build(&builder, SPA_PARAM_EnumFormat, &audioInfo);

            if (!params[0]) {
                std::cerr << "[PipeWire] failed to create audio format\n";
                goto cleanup;
            }

            params[1] = (spa_pod*) spa_pod_builder_add_object(
                &builder,
                SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
                SPA_PARAM_BUFFERS_buffers, SPA_POD_Int(MAX_BUFFERS),
                SPA_PARAM_BUFFERS_size, SPA_POD_Int(SAMPLES_PER_BUFFER * SAMPLE_SIZE_BYTES * buffer->Channels()),
                SPA_PARAM_BUFFERS_stride, SPA_POD_Int(SAMPLE_SIZE_BYTES * audioInfo.channels));

            pw_stream_flags streamFlags = (pw_stream_flags)(
                PW_STREAM_FLAG_AUTOCONNECT |
                PW_STREAM_FLAG_MAP_BUFFERS);

            result = pw_stream_connect(
                this->pwStream,
                PW_DIRECTION_OUTPUT,
                PW_ID_ANY,
                streamFlags,
                params,
                2);

            if (result == 0) {
                pw_thread_loop_unlock(this->pwThreadLoop);
                std::cerr << "[PipeWire] stream created and connected\n";
                this->initialized = true;
                return true;
            }
            else {
                std::cerr << "[PipeWire] error starting stream: " << spa_strerror(result) << "\n";
            }
        }
    }

cleanup:
    pw_thread_loop_unlock(this->pwThreadLoop);
    std::cerr << "[PipeWire] stream not initialized.\n";
    this->StopPipeWire();
    return false;
}

OutputState PipeWireOut::Play(IBuffer *buffer, IBufferProvider *provider) {
    if (!this->initialized) {
        if (!this->StartPipeWire(buffer)) {
            return OutputState::InvalidState;
        }
    }

    if (this->channelCount != buffer->Channels() || this->sampleRate != buffer->SampleRate()) {
        State lastState = this->state;
        this->Drain();
        this->StopPipeWire();
        if (!this->StartPipeWire(buffer)) {
            return OutputState::InvalidState;
        }
        this->state = lastState;
    }

    if (this->state != State::Playing) {
        return OutputState::InvalidState;
    }

    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        if (this->buffers.size() >= MAX_BUFFERS) {
            return OutputState::BufferFull;
        }
        this->buffers.push_back(new InBufferContext(buffer, provider));
        bufferCondition.notify_all();
    }

    return OutputState::BufferWritten;
}

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

#include "PipeWireOut.h"

#include <musikcore/sdk/constants.h>
#include <musikcore/sdk/IPreferences.h>
#include <musikcore/sdk/ISchema.h>
#include <spa/param/audio/format-utils.h>
#include <spa/param/props.h>
#include <spa/utils/result.h>
#include <unistd.h>
#include <iostream>
#include <chrono>

constexpr size_t SAMPLES_PER_BUFFER = 2048;
constexpr size_t SAMPLE_SIZE_BYTES = sizeof(float);
constexpr size_t MAX_BUFFERS = 16;

static IPreferences* prefs = nullptr;

/* CAL TODO: use IDebug instead of stderr */

extern "C" void SetPreferences(IPreferences* prefs) {
    ::prefs = prefs;
}

extern "C" musik::core::sdk::ISchema* GetSchema() {
    auto schema = new TSchema<>();
    return schema;
}

void PipeWireOut::OnStreamStateChanged(void* data, enum pw_stream_state old, enum pw_stream_state state, const char* error) {
    std::cerr << "[PipeWire] state changed from " << old << " to " << state << "\n";
}

void PipeWireOut::OnDrained(void* data) {
    std::cerr << "[PipeWire] drained\n";
    PipeWireOut* self = static_cast<PipeWireOut*>(data);
    self->drainCondition.notify_all();
}

void PipeWireOut::OnStreamProcess(void* data) {
    PipeWireOut* self = static_cast<PipeWireOut*>(data);

    {
        std::unique_lock<std::recursive_mutex> lock(self->mutex);

        if (self->state != State::Playing) {
            return;
        }

        OutBufferContext& outContext = self->outBufferContext;

        if (!outContext.Valid()) {
            outContext.Initialize(pw_stream_dequeue_buffer(self->pwStream));
            if (!outContext.Valid()) {
                // std::cerr << "[PipeWire] no more output buffers available to fill\n";
                return;
            }
        }

        uint32_t channelCount;

        while (outContext.remaining > 0 && !self->buffers.empty()) {
            //std::cerr << "[PipeWire] " << outContext.remaining << " bytes still need to be filled...\n";

            InBufferContext* inContext = self->buffers.front();
            channelCount = (uint32_t) inContext->buffer->Channels();
            uint32_t inBufferSize = (uint32_t) inContext->buffer->Bytes();
            uint32_t bytesToCopy = std::min(outContext.remaining, inContext->remaining);

            if (outContext.remaining >= inContext->remaining) {
                self->buffers.pop_front();
                self->bufferCondition.notify_all();
            }

            memcpy(outContext.writePtr, inContext->readPtr, bytesToCopy);
            inContext->Advance(bytesToCopy); /* will `delete this` if emptied */
            outContext.Advance(bytesToCopy);
        }

        if (outContext.remaining == 0) {
            outContext.Finalize(self->pwStream, SAMPLE_SIZE_BYTES * channelCount);
        }
    }
}

PipeWireOut::PipeWireOut() {
    this->pwStreamEvents = { PW_VERSION_STREAM_EVENTS };
    this->pwStreamEvents.state_changed = PipeWireOut::OnStreamStateChanged;
    this->pwStreamEvents.process = PipeWireOut::OnStreamProcess;
    this->pwStreamEvents.drained = PipeWireOut::OnDrained;
}

PipeWireOut::~PipeWireOut() {
    this->StopPipeWire();
}

void PipeWireOut::Release() {
    delete this;
}

void PipeWireOut::Pause() {
    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        this->state = State::Paused;
    }
    {
        if (this->pwThreadLoop && this->pwStream) {
            pw_thread_loop_lock(this->pwThreadLoop);
            pw_stream_set_active(this->pwStream, false);
            pw_thread_loop_unlock(this->pwThreadLoop);
        }
    }
}

void PipeWireOut::Resume() {
    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        this->state = State::Playing;
    }
    {
        if (this->pwThreadLoop && this->pwStream) {
            pw_thread_loop_lock(this->pwThreadLoop);
            pw_stream_set_active(this->pwStream, true);
            pw_thread_loop_unlock(this->pwThreadLoop);
        }
    }
}

void PipeWireOut::SetVolume(double volume) {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    if (this->pwThreadLoop && this->pwStream) {
        pw_thread_loop_lock(this->pwThreadLoop);
        float* channelVolumes = new float[this->channelCount];
        for (long i = 0; i < this->channelCount; i++) {
            channelVolumes[i] = volume;
        }
        pw_stream_set_control(
            this->pwStream,
            SPA_PROP_channelVolumes,
            this->channelCount,
            channelVolumes,
            0);
        delete[] channelVolumes;
        pw_thread_loop_unlock(this->pwThreadLoop);
    }
    this->volume = volume;
}

double PipeWireOut::GetVolume() {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    return this->volume;
}

void PipeWireOut::Stop() {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    this->DiscardInputBuffers();
    this->state = State::Stopped;
    if (this->pwThreadLoop && this->pwStream) {
        pw_thread_loop_lock(this->pwThreadLoop);
        pw_stream_set_active(this->pwStream, false);
        pw_stream_flush(this->pwStream, false);
        pw_thread_loop_unlock(this->pwThreadLoop);
    }
}

void PipeWireOut::DiscardInputBuffers() {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    for (auto& buffer : this->buffers) {
        buffer->Discard();
    }
    this->buffers.clear();
    this->bufferCondition.notify_all();
}

void PipeWireOut::Drain() {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);
    while (this->buffers.size()) {
        bufferCondition.wait(lock);
    }
    if (this->pwThreadLoop && this->pwStream) {
        pw_thread_loop_lock(this->pwThreadLoop);
        pw_stream_flush(this->pwStream, true);
        pw_thread_loop_unlock(this->pwThreadLoop);
        drainCondition.wait_for(lock, std::chrono::milliseconds(10000));
    }
}

IDeviceList* PipeWireOut::GetDeviceList() {
    /* CAL TODO */
    return nullptr;
}

bool PipeWireOut::SetDefaultDevice(const char* deviceId) {
    /* CAL TODO */
    return false;
}

IDevice* PipeWireOut::GetDefaultDevice() {
    /* CAL TODO */
    return nullptr;
}

void PipeWireOut::StopPipeWire() {
    std::cerr << "[PipeWire] shutdown started...\n";

    this->Stop();

    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        this->state = State::Shutdown;
    }

    if (this->pwThreadLoop) {
        pw_thread_loop_stop(this->pwThreadLoop);

        if (this->pwStream) {
            this->outBufferContext.Finalize(
                this->pwStream,
                SAMPLE_SIZE_BYTES * this->channelCount);
            pw_stream_destroy(this->pwStream);
            this->pwStream = nullptr;
        }

        pw_thread_loop_destroy(this->pwThreadLoop);
        this->pwThreadLoop = nullptr;
    }

    this->initialized = false;
    this->channelCount = 0;
    this->sampleRate = 0;

    std::cerr << "[PipeWire] shutdown complete.\n";
}

bool PipeWireOut::StartPipeWire(IBuffer* buffer) {
    std::unique_lock<std::recursive_mutex> lock(this->mutex);

    pw_init(nullptr, nullptr);

    this->pwThreadLoop = pw_thread_loop_new("musikcube", nullptr);
    if (this->pwThreadLoop) {
        int result;

        if ((result = pw_thread_loop_start(this->pwThreadLoop)) != 0) {
            std::cerr << "[PipeWire] error starting thread loop: " << spa_strerror(result) << "\n";
            goto cleanup;
        };

        pw_thread_loop_lock(this->pwThreadLoop);

        this->pwStream = pw_stream_new_simple(
            pw_thread_loop_get_loop(this->pwThreadLoop),
            "musikcube",
            pw_properties_new(
                PW_KEY_MEDIA_TYPE, "Audio",
                PW_KEY_MEDIA_CATEGORY, "Playback",
                PW_KEY_MEDIA_ROLE, "Music",
                NULL),
            &this->pwStreamEvents,
            this);

        if (this->pwStream) {
            uint8_t builderBuffer[4096];
            spa_pod_builder builder = SPA_POD_BUILDER_INIT(builderBuffer, sizeof(builderBuffer));
            const spa_pod *params[2];

            this->channelCount = buffer->Channels();
            this->sampleRate = buffer->SampleRate();

            spa_audio_info_raw audioInfo;
            spa_zero(audioInfo);
            audioInfo.format = SPA_AUDIO_FORMAT_F32;
            audioInfo.channels = (uint32_t) this->channelCount;
            audioInfo.rate = (uint32_t) this->sampleRate;

            params[0] = spa_format_audio_raw_build(&builder, SPA_PARAM_EnumFormat, &audioInfo);

            if (!params[0]) {
                std::cerr << "[PipeWire] failed to create audio format\n";
                goto cleanup;
            }

            params[1] = (spa_pod*) spa_pod_builder_add_object(
                &builder,
                SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
                SPA_PARAM_BUFFERS_buffers, SPA_POD_Int(MAX_BUFFERS),
                SPA_PARAM_BUFFERS_size, SPA_POD_Int(SAMPLES_PER_BUFFER * SAMPLE_SIZE_BYTES * buffer->Channels()),
                SPA_PARAM_BUFFERS_stride, SPA_POD_Int(SAMPLE_SIZE_BYTES * audioInfo.channels));

            pw_stream_flags streamFlags = (pw_stream_flags)(
                PW_STREAM_FLAG_AUTOCONNECT |
                PW_STREAM_FLAG_MAP_BUFFERS);

            result = pw_stream_connect(
                this->pwStream,
                PW_DIRECTION_OUTPUT,
                PW_ID_ANY,
                streamFlags,
                params,
                2);

            if (result == 0) {
                pw_thread_loop_unlock(this->pwThreadLoop);
                std::cerr << "[PipeWire] stream created and connected\n";
                this->initialized = true;
                return true;
            }
            else {
                std::cerr << "[PipeWire] error starting stream: " << spa_strerror(result) << "\n";
            }
        }
    }

cleanup:
    pw_thread_loop_unlock(this->pwThreadLoop);
    std::cerr << "[PipeWire] stream not initialized.\n";
    this->StopPipeWire();
    return false;
}

OutputState PipeWireOut::Play(IBuffer *buffer, IBufferProvider *provider) {
    if (!this->initialized) {
        if (!this->StartPipeWire(buffer)) {
            return OutputState::InvalidState;
        }
        this->SetVolume(this->volume);
    }

    if (this->channelCount != buffer->Channels() || this->sampleRate != buffer->SampleRate()) {
        State lastState = this->state;
        this->Drain();
        this->StopPipeWire();
        if (!this->StartPipeWire(buffer)) {
            return OutputState::InvalidState;
        }
        this->state = lastState;
    }

    if (this->state != State::Playing) {
        return OutputState::InvalidState;
    }

    {
        std::unique_lock<std::recursive_mutex> lock(this->mutex);
        if (this->buffers.size() >= MAX_BUFFERS) {
            return OutputState::BufferFull;
        }
        this->buffers.push_back(new InBufferContext(buffer, provider));
        bufferCondition.notify_all();
    }

    return OutputState::BufferWritten;
}

double PipeWireOut::Latency() {
    /* CAL TODO: i see how to set latency, but not a good way to query
    PipeWire to see what it actually is */
    return 0.0;
}
<|endoftext|>


<commit_before>#pragma once

#include <map>
#include <ros/console.h>

#include "AbstractCameraQueue.hpp"

template <class T>
class TrackingQueue {
public:
	TrackingQueue();
	~TrackingQueue();

	void enqueue(std::vector<CameraData> data);
	void enqueue(CameraData data);

	size_t getSize();
	std::vector<CameraData> dequeue();
	bool dataAvailable();

private:
	std::map<int, AbstractCameraQueue*> queues;
	std::vector<int> ids;
	int rrIndex;
	size_t size;
};

template <class T>
TrackingQueue<T>::TrackingQueue()
{
	size = 0;
	rrIndex = 0;
}

template <class T>
TrackingQueue<T>::~TrackingQueue()
{
	for (std::map<int, AbstractCameraQueue*>::iterator it = queues.begin(); it != queues.end(); it++) {
		delete it->second;
	}
}

template <class T>
void TrackingQueue<T>::enqueue(std::vector<CameraData> data)
{
	for (std::vector<CameraData>::iterator it = data.begin(); it != data.end(); it++) {
		enqueue(*it);
	}
}

template <class T>
void TrackingQueue<T>::enqueue(CameraData data)
{
	if (queues.count(data.quadcopterId) == 0) {
		queues[data.quadcopterId] = new T(3L * 1000 * 1000 * 1000 / 30,
										  5L * 1000 * 1000 * 1000 / 30,
										  1L * 1000 * 1000 * 1000 / 30,
										  true);
		ids.push_back(data.quadcopterId);
	}

	size++;
	queues[data.quadcopterId]->enqueue(data);
}

template <class T>
size_t TrackingQueue<T>::getSize()
{
	return size;
}

template <class T>
std::vector<CameraData> TrackingQueue<T>::dequeue()
{
	if (size == 0) {
		return std::vector<CameraData>();
	}

	int index = rrIndex;
	int loopCount = 0;

	do {
		index  = (index + 1) % ids.size();
		loopCount++;

		if (queues[ids[index]]->dataAvailable()) {
			break;
		}
	} while (index != rrIndex);

	if (loopCount > ids.size()) {
		return std::vector<CameraData>();
	} else {
		rrIndex = index;
		std::vector<CameraData> result = queues[ids[index]]->dequeue();
		size -= result.size();

		return result;
	}
}

template <class T>
bool TrackingQueue<T>::dataAvailable()
{
	for (std::map<int, AbstractCameraQueue*>::iterator it = queues.begin(); it != queues.end(); it++) {
		if (it->second->dataAvailable()) {
			return true;
		}
	}

	return false;
}
<commit_msg>More parameter tuning<commit_after>#pragma once

#include <map>
#include <ros/console.h>

#include "AbstractCameraQueue.hpp"

template <class T>
class TrackingQueue {
public:
	TrackingQueue();
	~TrackingQueue();

	void enqueue(std::vector<CameraData> data);
	void enqueue(CameraData data);

	size_t getSize();
	std::vector<CameraData> dequeue();
	bool dataAvailable();

private:
	std::map<int, AbstractCameraQueue*> queues;
	std::vector<int> ids;
	int rrIndex;
	size_t size;
};

template <class T>
TrackingQueue<T>::TrackingQueue()
{
	size = 0;
	rrIndex = 0;
}

template <class T>
TrackingQueue<T>::~TrackingQueue()
{
	for (std::map<int, AbstractCameraQueue*>::iterator it = queues.begin(); it != queues.end(); it++) {
		delete it->second;
	}
}

template <class T>
void TrackingQueue<T>::enqueue(std::vector<CameraData> data)
{
	for (std::vector<CameraData>::iterator it = data.begin(); it != data.end(); it++) {
		enqueue(*it);
	}
}

template <class T>
void TrackingQueue<T>::enqueue(CameraData data)
{
	if (queues.count(data.quadcopterId) == 0) {
		queues[data.quadcopterId] = new T(3L * 1000 * 1000 * 1000 / 30,
										  5L * 1000 * 1000 * 1000 / 30,
										  2L * 1000 * 1000 * 1000 / 30,
										  true);
		ids.push_back(data.quadcopterId);
	}

	size++;
	queues[data.quadcopterId]->enqueue(data);
}

template <class T>
size_t TrackingQueue<T>::getSize()
{
	return size;
}

template <class T>
std::vector<CameraData> TrackingQueue<T>::dequeue()
{
	if (size == 0) {
		return std::vector<CameraData>();
	}

	int index = rrIndex;
	int loopCount = 0;

	do {
		index  = (index + 1) % ids.size();
		loopCount++;

		if (queues[ids[index]]->dataAvailable()) {
			break;
		}
	} while (index != rrIndex);

	if (loopCount > ids.size()) {
		return std::vector<CameraData>();
	} else {
		rrIndex = index;
		std::vector<CameraData> result = queues[ids[index]]->dequeue();
		size -= result.size();

		return result;
	}
}

template <class T>
bool TrackingQueue<T>::dataAvailable()
{
	for (std::map<int, AbstractCameraQueue*>::iterator it = queues.begin(); it != queues.end(); it++) {
		if (it->second->dataAvailable()) {
			return true;
		}
	}

	return false;
}
<|endoftext|>


<commit_before>// This is the main DLL file.

#include "stdafx.h"

#include "doctrendererwrapper.h"
#include "../../../docbuilder.h"

#ifdef _WIN64
#pragma comment(lib, "../../../../../SDK/lib/win_64/doctrenderer.lib")
#else
#pragma comment(lib, "../../../../../SDK/lib/win_32/doctrenderer.lib")
#endif

namespace doctrendererwrapper {

	static wchar_t* StringToStdString(String^ param)
	{
		return (wchar_t*)System::Runtime::InteropServices::Marshal::StringToHGlobalUni(param).ToPointer();
	}

	ref class CWrapper_Private
	{
	public:
		NSDoctRenderer::CDocBuilder* m_pBuilder;

		CWrapper_Private(bool bIsCheckSystemFonts)
		{
			m_pBuilder = new NSDoctRenderer::CDocBuilder(bIsCheckSystemFonts);
		}
		~CWrapper_Private()
		{
			delete m_pBuilder;
		}
	};

	CWrapper::CWrapper(bool bIsCheckSystemFonts)
	{
		m_pInternal = gcnew CWrapper_Private(bIsCheckSystemFonts);
	}
	CWrapper::~CWrapper()
	{
		delete m_pInternal;
	}

	bool CWrapper::OpenFile(String^ path, String^ params)
	{
		return m_pInternal->m_pBuilder->OpenFile(StringToStdString(path), StringToStdString(params));
	}
	bool CWrapper::CreateFile(int type)
	{
		return m_pInternal->m_pBuilder->CreateFile(type);
	}
	void CWrapper::SetTmpFolder(String^ folder)
	{
		m_pInternal->m_pBuilder->SetTmpFolder(StringToStdString(folder));
	}
	bool CWrapper::SaveFile(int type, String^ path)
	{
		return m_pInternal->m_pBuilder->SaveFile(type, StringToStdString(path));
	}
	void CWrapper::CloseFile()
	{
		m_pInternal->m_pBuilder->CloseFile();
	}
	bool CWrapper::ExecuteCommand(String^ command)
	{
		return m_pInternal->m_pBuilder->ExecuteCommand(StringToStdString(command));
	}

	bool CWrapper::Run(String^ path)
	{
		return m_pInternal->m_pBuilder->Run(StringToStdString(path));
	}

	bool CWrapper::RunText(String^ text)
	{
		return m_pInternal->m_pBuilder->RunTextW(StringToStdString(text));
	}

	void CWrapper::Initialize()
	{
		NSDoctRenderer::CDocBuilder::Initialize();
	}
	void CWrapper::Destroy()
	{
		NSDoctRenderer::CDocBuilder::Dispose();
	}

}

<commit_msg>no message<commit_after>// This is the main DLL file.

#include "stdafx.h"

#include "doctrendererwrapper.h"
#include "../../../docbuilder.h"

#ifdef _WIN64
#pragma comment(lib, "../../../../../build/lib/win_64/doctrenderer.lib")
#else
#pragma comment(lib, "../../../../../build/lib/win_32/doctrenderer.lib")
#endif

namespace doctrendererwrapper {

	static wchar_t* StringToStdString(String^ param)
	{
		return (wchar_t*)System::Runtime::InteropServices::Marshal::StringToHGlobalUni(param).ToPointer();
	}

	ref class CWrapper_Private
	{
	public:
		NSDoctRenderer::CDocBuilder* m_pBuilder;

		CWrapper_Private(bool bIsCheckSystemFonts)
		{
			m_pBuilder = new NSDoctRenderer::CDocBuilder(bIsCheckSystemFonts);
		}
		~CWrapper_Private()
		{
			delete m_pBuilder;
		}
	};

	CWrapper::CWrapper(bool bIsCheckSystemFonts)
	{
		m_pInternal = gcnew CWrapper_Private(bIsCheckSystemFonts);
	}
	CWrapper::~CWrapper()
	{
		delete m_pInternal;
	}

	bool CWrapper::OpenFile(String^ path, String^ params)
	{
		return m_pInternal->m_pBuilder->OpenFile(StringToStdString(path), StringToStdString(params));
	}
	bool CWrapper::CreateFile(int type)
	{
		return m_pInternal->m_pBuilder->CreateFile(type);
	}
	void CWrapper::SetTmpFolder(String^ folder)
	{
		m_pInternal->m_pBuilder->SetTmpFolder(StringToStdString(folder));
	}
	bool CWrapper::SaveFile(int type, String^ path)
	{
		return m_pInternal->m_pBuilder->SaveFile(type, StringToStdString(path));
	}
	void CWrapper::CloseFile()
	{
		m_pInternal->m_pBuilder->CloseFile();
	}
	bool CWrapper::ExecuteCommand(String^ command)
	{
		return m_pInternal->m_pBuilder->ExecuteCommand(StringToStdString(command));
	}

	bool CWrapper::Run(String^ path)
	{
		return m_pInternal->m_pBuilder->Run(StringToStdString(path));
	}

	bool CWrapper::RunText(String^ text)
	{
		return m_pInternal->m_pBuilder->RunTextW(StringToStdString(text));
	}

	void CWrapper::Initialize()
	{
		NSDoctRenderer::CDocBuilder::Initialize();
	}
	void CWrapper::Destroy()
	{
		NSDoctRenderer::CDocBuilder::Dispose();
	}

}

<|endoftext|>