<commit_before>/* Copyright 2022 The MediaPipe Authors. All Rights Reserved.

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.
==============================================================================*/

#include "mediapipe/tasks/cc/vision/gesture_recognizer/gesture_recognizer.h"

#include <memory>
#include <type_traits>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/formats/classification.pb.h"
#include "mediapipe/framework/formats/image.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/tasks/cc/common.h"
#include "mediapipe/tasks/cc/components/image_preprocessing.h"
#include "mediapipe/tasks/cc/components/processors/proto/classifier_options.pb.h"
#include "mediapipe/tasks/cc/core/base_task_api.h"
#include "mediapipe/tasks/cc/core/model_resources.h"
#include "mediapipe/tasks/cc/core/proto/inference_subgraph.pb.h"
#include "mediapipe/tasks/cc/core/task_runner.h"
#include "mediapipe/tasks/cc/core/utils.h"
#include "mediapipe/tasks/cc/vision/core/base_vision_task_api.h"
#include "mediapipe/tasks/cc/vision/core/vision_task_api_factory.h"
#include "mediapipe/tasks/cc/vision/gesture_recognizer/proto/gesture_recognizer_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/gesture_recognizer/proto/hand_gesture_recognizer_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/hand_detector/proto/hand_detector_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/hand_landmarker/proto/hand_landmarker_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/hand_landmarker/proto/hand_landmarks_detector_graph_options.pb.h"

namespace mediapipe {
namespace tasks {
namespace vision {
namespace gesture_recognizer {

namespace {

using GestureRecognizerGraphOptionsProto = ::mediapipe::tasks::vision::
    gesture_recognizer::proto::GestureRecognizerGraphOptions;

using ::mediapipe::tasks::components::containers::GestureRecognitionResult;

constexpr char kHandGestureSubgraphTypeName[] =
    "mediapipe.tasks.vision.gesture_recognizer.GestureRecognizerGraph";

constexpr char kImageTag[] = "IMAGE";
constexpr char kImageInStreamName[] = "image_in";
constexpr char kImageOutStreamName[] = "image_out";
constexpr char kHandGesturesTag[] = "HAND_GESTURES";
constexpr char kHandGesturesStreamName[] = "hand_gestures";
constexpr char kHandednessTag[] = "HANDEDNESS";
constexpr char kHandednessStreamName[] = "handedness";
constexpr char kHandLandmarksTag[] = "LANDMARKS";
constexpr char kHandLandmarksStreamName[] = "landmarks";
constexpr char kHandWorldLandmarksTag[] = "WORLD_LANDMARKS";
constexpr char kHandWorldLandmarksStreamName[] = "world_landmarks";
constexpr int kMicroSecondsPerMilliSecond = 1000;

// Creates a MediaPipe graph config that contains a subgraph node of
// "mediapipe.tasks.vision.GestureRecognizerGraph". If the task is running
// in the live stream mode, a "FlowLimiterCalculator" will be added to limit the
// number of frames in flight.
CalculatorGraphConfig CreateGraphConfig(
    std::unique_ptr<GestureRecognizerGraphOptionsProto> options,
    bool enable_flow_limiting) {
  api2::builder::Graph graph;
  auto& subgraph = graph.AddNode(kHandGestureSubgraphTypeName);
  subgraph.GetOptions<GestureRecognizerGraphOptionsProto>().Swap(options.get());
  graph.In(kImageTag).SetName(kImageInStreamName);
  subgraph.Out(kHandGesturesTag).SetName(kHandGesturesStreamName) >>
      graph.Out(kHandGesturesTag);
  subgraph.Out(kHandednessTag).SetName(kHandednessStreamName) >>
      graph.Out(kHandednessTag);
  subgraph.Out(kHandLandmarksTag).SetName(kHandLandmarksStreamName) >>
      graph.Out(kHandLandmarksTag);
  subgraph.Out(kHandWorldLandmarksTag).SetName(kHandWorldLandmarksStreamName) >>
      graph.Out(kHandWorldLandmarksTag);
  subgraph.Out(kImageTag).SetName(kImageOutStreamName) >> graph.Out(kImageTag);
  if (enable_flow_limiting) {
    return tasks::core::AddFlowLimiterCalculator(graph, subgraph, {kImageTag},
                                                 kHandGesturesTag);
  }
  graph.In(kImageTag) >> subgraph.In(kImageTag);
  return graph.GetConfig();
}

// Converts the user-facing GestureRecognizerOptions struct to the internal
// GestureRecognizerGraphOptions proto.
std::unique_ptr<GestureRecognizerGraphOptionsProto>
ConvertGestureRecognizerGraphOptionsProto(GestureRecognizerOptions* options) {
  auto options_proto = std::make_unique<GestureRecognizerGraphOptionsProto>();

  bool use_stream_mode = options->running_mode != core::RunningMode::IMAGE;

  // TODO remove these workarounds for base options of subgraphs.
  // Configure hand detector options.
  auto base_options_proto_for_hand_detector =
      std::make_unique<tasks::core::proto::BaseOptions>(
          tasks::core::ConvertBaseOptionsToProto(
              &(options->base_options_for_hand_detector)));
  base_options_proto_for_hand_detector->set_use_stream_mode(use_stream_mode);
  auto* hand_detector_graph_options =
      options_proto->mutable_hand_landmarker_graph_options()
          ->mutable_hand_detector_graph_options();
  hand_detector_graph_options->mutable_base_options()->Swap(
      base_options_proto_for_hand_detector.get());
  hand_detector_graph_options->set_num_hands(options->num_hands);
  hand_detector_graph_options->set_min_detection_confidence(
      options->min_hand_detection_confidence);

  // Configure hand landmark detector options.
  auto base_options_proto_for_hand_landmarker =
      std::make_unique<tasks::core::proto::BaseOptions>(
          tasks::core::ConvertBaseOptionsToProto(
              &(options->base_options_for_hand_landmarker)));
  base_options_proto_for_hand_landmarker->set_use_stream_mode(use_stream_mode);
  auto* hand_landmarks_detector_graph_options =
      options_proto->mutable_hand_landmarker_graph_options()
          ->mutable_hand_landmarks_detector_graph_options();
  hand_landmarks_detector_graph_options->mutable_base_options()->Swap(
      base_options_proto_for_hand_landmarker.get());
  hand_landmarks_detector_graph_options->set_min_detection_confidence(
      options->min_hand_presence_confidence);

  auto* hand_landmarker_graph_options =
      options_proto->mutable_hand_landmarker_graph_options();
  hand_landmarker_graph_options->set_min_tracking_confidence(
      options->min_tracking_confidence);

  // Configure hand gesture recognizer options.
  auto base_options_proto_for_gesture_recognizer =
      std::make_unique<tasks::core::proto::BaseOptions>(
          tasks::core::ConvertBaseOptionsToProto(
              &(options->base_options_for_gesture_recognizer)));
  base_options_proto_for_gesture_recognizer->set_use_stream_mode(
      use_stream_mode);
  auto* hand_gesture_recognizer_graph_options =
      options_proto->mutable_hand_gesture_recognizer_graph_options();
  hand_gesture_recognizer_graph_options->mutable_base_options()->Swap(
      base_options_proto_for_gesture_recognizer.get());
  if (options->min_gesture_confidence >= 0) {
    hand_gesture_recognizer_graph_options->mutable_classifier_options()
        ->set_score_threshold(options->min_gesture_confidence);
  }
  return options_proto;
}

}  // namespace

absl::StatusOr<std::unique_ptr<GestureRecognizer>> GestureRecognizer::Create(
    std::unique_ptr<GestureRecognizerOptions> options) {
  auto options_proto = ConvertGestureRecognizerGraphOptionsProto(options.get());
  tasks::core::PacketsCallback packets_callback = nullptr;
  if (options->result_callback) {
    auto result_callback = options->result_callback;
    packets_callback = [=](absl::StatusOr<tasks::core::PacketMap>
                               status_or_packets) {
      if (!status_or_packets.ok()) {
        Image image;
        result_callback(status_or_packets.status(), image,
                        Timestamp::Unset().Value());
        return;
      }
      if (status_or_packets.value()[kImageOutStreamName].IsEmpty()) {
        return;
      }
      Packet gesture_packet =
          status_or_packets.value()[kHandGesturesStreamName];
      Packet handedness_packet =
          status_or_packets.value()[kHandednessStreamName];
      Packet hand_landmarks_packet =
          status_or_packets.value()[kHandLandmarksStreamName];
      Packet hand_world_landmarks_packet =
          status_or_packets.value()[kHandWorldLandmarksStreamName];
      Packet image_packet = status_or_packets.value()[kImageOutStreamName];
      result_callback(
          {{gesture_packet.Get<std::vector<ClassificationList>>(),
            handedness_packet.Get<std::vector<ClassificationList>>(),
            hand_landmarks_packet.Get<std::vector<NormalizedLandmarkList>>(),
            hand_world_landmarks_packet.Get<std::vector<LandmarkList>>()}},
          image_packet.Get<Image>(),
          gesture_packet.Timestamp().Value() / kMicroSecondsPerMilliSecond);
    };
  }
  return core::VisionTaskApiFactory::Create<GestureRecognizer,
                                            GestureRecognizerGraphOptionsProto>(
      CreateGraphConfig(
          std::move(options_proto),
          options->running_mode == core::RunningMode::LIVE_STREAM),
      std::move(options->base_options.op_resolver), options->running_mode,
      std::move(packets_callback));
}

absl::StatusOr<GestureRecognitionResult> GestureRecognizer::Recognize(
    mediapipe::Image image) {
  if (image.UsesGpu()) {
    return CreateStatusWithPayload(
        absl::StatusCode::kInvalidArgument,
        "GPU input images are currently not supported.",
        MediaPipeTasksStatus::kRunnerUnexpectedInputError);
  }
  ASSIGN_OR_RETURN(auto output_packets,
                   ProcessImageData({{kImageInStreamName,
                                      MakePacket<Image>(std::move(image))}}));
  return {
      {/* gestures= */ {output_packets[kHandGesturesStreamName]
                            .Get<std::vector<ClassificationList>>()},
       /* handedness= */
       {output_packets[kHandednessStreamName]
            .Get<std::vector<mediapipe::ClassificationList>>()},
       /* hand_landmarks= */
       {output_packets[kHandLandmarksStreamName]
            .Get<std::vector<mediapipe::NormalizedLandmarkList>>()},
       /* hand_world_landmarks */
       {output_packets[kHandWorldLandmarksStreamName]
            .Get<std::vector<mediapipe::LandmarkList>>()}},
  };
}

absl::StatusOr<GestureRecognitionResult> GestureRecognizer::RecognizeForVideo(
    mediapipe::Image image, int64 timestamp_ms) {
  if (image.UsesGpu()) {
    return CreateStatusWithPayload(
        absl::StatusCode::kInvalidArgument,
        absl::StrCat("GPU input images are currently not supported."),
        MediaPipeTasksStatus::kRunnerUnexpectedInputError);
  }
  ASSIGN_OR_RETURN(
      auto output_packets,
      ProcessVideoData(
          {{kImageInStreamName,
            MakePacket<Image>(std::move(image))
                .At(Timestamp(timestamp_ms * kMicroSecondsPerMilliSecond))}}));
  return {
      {/* gestures= */ {output_packets[kHandGesturesStreamName]
                            .Get<std::vector<ClassificationList>>()},
       /* handedness= */
       {output_packets[kHandednessStreamName]
            .Get<std::vector<mediapipe::ClassificationList>>()},
       /* hand_landmarks= */
       {output_packets[kHandLandmarksStreamName]
            .Get<std::vector<mediapipe::NormalizedLandmarkList>>()},
       /* hand_world_landmarks */
       {output_packets[kHandWorldLandmarksStreamName]
            .Get<std::vector<mediapipe::LandmarkList>>()}},
  };
}

absl::Status GestureRecognizer::RecognizeAsync(mediapipe::Image image,
                                               int64 timestamp_ms) {
  if (image.UsesGpu()) {
    return CreateStatusWithPayload(
        absl::StatusCode::kInvalidArgument,
        absl::StrCat("GPU input images are currently not supported."),
        MediaPipeTasksStatus::kRunnerUnexpectedInputError);
  }
  return SendLiveStreamData(
      {{kImageInStreamName,
        MakePacket<Image>(std::move(image))
            .At(Timestamp(timestamp_ms * kMicroSecondsPerMilliSecond))}});
}

}  // namespace gesture_recognizer
}  // namespace vision
}  // namespace tasks
}  // namespace mediapipe
<commit_msg>Fix empty packet bug with no hands detected.<commit_after>/* Copyright 2022 The MediaPipe Authors. All Rights Reserved.

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.
==============================================================================*/

#include "mediapipe/tasks/cc/vision/gesture_recognizer/gesture_recognizer.h"

#include <memory>
#include <type_traits>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/formats/classification.pb.h"
#include "mediapipe/framework/formats/image.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/tasks/cc/common.h"
#include "mediapipe/tasks/cc/components/image_preprocessing.h"
#include "mediapipe/tasks/cc/components/processors/proto/classifier_options.pb.h"
#include "mediapipe/tasks/cc/core/base_task_api.h"
#include "mediapipe/tasks/cc/core/model_resources.h"
#include "mediapipe/tasks/cc/core/proto/inference_subgraph.pb.h"
#include "mediapipe/tasks/cc/core/task_runner.h"
#include "mediapipe/tasks/cc/core/utils.h"
#include "mediapipe/tasks/cc/vision/core/base_vision_task_api.h"
#include "mediapipe/tasks/cc/vision/core/vision_task_api_factory.h"
#include "mediapipe/tasks/cc/vision/gesture_recognizer/proto/gesture_recognizer_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/gesture_recognizer/proto/hand_gesture_recognizer_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/hand_detector/proto/hand_detector_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/hand_landmarker/proto/hand_landmarker_graph_options.pb.h"
#include "mediapipe/tasks/cc/vision/hand_landmarker/proto/hand_landmarks_detector_graph_options.pb.h"

namespace mediapipe {
namespace tasks {
namespace vision {
namespace gesture_recognizer {

namespace {

using GestureRecognizerGraphOptionsProto = ::mediapipe::tasks::vision::
    gesture_recognizer::proto::GestureRecognizerGraphOptions;

using ::mediapipe::tasks::components::containers::GestureRecognitionResult;

constexpr char kHandGestureSubgraphTypeName[] =
    "mediapipe.tasks.vision.gesture_recognizer.GestureRecognizerGraph";

constexpr char kImageTag[] = "IMAGE";
constexpr char kImageInStreamName[] = "image_in";
constexpr char kImageOutStreamName[] = "image_out";
constexpr char kHandGesturesTag[] = "HAND_GESTURES";
constexpr char kHandGesturesStreamName[] = "hand_gestures";
constexpr char kHandednessTag[] = "HANDEDNESS";
constexpr char kHandednessStreamName[] = "handedness";
constexpr char kHandLandmarksTag[] = "LANDMARKS";
constexpr char kHandLandmarksStreamName[] = "landmarks";
constexpr char kHandWorldLandmarksTag[] = "WORLD_LANDMARKS";
constexpr char kHandWorldLandmarksStreamName[] = "world_landmarks";
constexpr int kMicroSecondsPerMilliSecond = 1000;

// Creates a MediaPipe graph config that contains a subgraph node of
// "mediapipe.tasks.vision.GestureRecognizerGraph". If the task is running
// in the live stream mode, a "FlowLimiterCalculator" will be added to limit the
// number of frames in flight.
CalculatorGraphConfig CreateGraphConfig(
    std::unique_ptr<GestureRecognizerGraphOptionsProto> options,
    bool enable_flow_limiting) {
  api2::builder::Graph graph;
  auto& subgraph = graph.AddNode(kHandGestureSubgraphTypeName);
  subgraph.GetOptions<GestureRecognizerGraphOptionsProto>().Swap(options.get());
  graph.In(kImageTag).SetName(kImageInStreamName);
  subgraph.Out(kHandGesturesTag).SetName(kHandGesturesStreamName) >>
      graph.Out(kHandGesturesTag);
  subgraph.Out(kHandednessTag).SetName(kHandednessStreamName) >>
      graph.Out(kHandednessTag);
  subgraph.Out(kHandLandmarksTag).SetName(kHandLandmarksStreamName) >>
      graph.Out(kHandLandmarksTag);
  subgraph.Out(kHandWorldLandmarksTag).SetName(kHandWorldLandmarksStreamName) >>
      graph.Out(kHandWorldLandmarksTag);
  subgraph.Out(kImageTag).SetName(kImageOutStreamName) >> graph.Out(kImageTag);
  if (enable_flow_limiting) {
    return tasks::core::AddFlowLimiterCalculator(graph, subgraph, {kImageTag},
                                                 kHandGesturesTag);
  }
  graph.In(kImageTag) >> subgraph.In(kImageTag);
  return graph.GetConfig();
}

// Converts the user-facing GestureRecognizerOptions struct to the internal
// GestureRecognizerGraphOptions proto.
std::unique_ptr<GestureRecognizerGraphOptionsProto>
ConvertGestureRecognizerGraphOptionsProto(GestureRecognizerOptions* options) {
  auto options_proto = std::make_unique<GestureRecognizerGraphOptionsProto>();

  bool use_stream_mode = options->running_mode != core::RunningMode::IMAGE;

  // TODO remove these workarounds for base options of subgraphs.
  // Configure hand detector options.
  auto base_options_proto_for_hand_detector =
      std::make_unique<tasks::core::proto::BaseOptions>(
          tasks::core::ConvertBaseOptionsToProto(
              &(options->base_options_for_hand_detector)));
  base_options_proto_for_hand_detector->set_use_stream_mode(use_stream_mode);
  auto* hand_detector_graph_options =
      options_proto->mutable_hand_landmarker_graph_options()
          ->mutable_hand_detector_graph_options();
  hand_detector_graph_options->mutable_base_options()->Swap(
      base_options_proto_for_hand_detector.get());
  hand_detector_graph_options->set_num_hands(options->num_hands);
  hand_detector_graph_options->set_min_detection_confidence(
      options->min_hand_detection_confidence);

  // Configure hand landmark detector options.
  auto base_options_proto_for_hand_landmarker =
      std::make_unique<tasks::core::proto::BaseOptions>(
          tasks::core::ConvertBaseOptionsToProto(
              &(options->base_options_for_hand_landmarker)));
  base_options_proto_for_hand_landmarker->set_use_stream_mode(use_stream_mode);
  auto* hand_landmarks_detector_graph_options =
      options_proto->mutable_hand_landmarker_graph_options()
          ->mutable_hand_landmarks_detector_graph_options();
  hand_landmarks_detector_graph_options->mutable_base_options()->Swap(
      base_options_proto_for_hand_landmarker.get());
  hand_landmarks_detector_graph_options->set_min_detection_confidence(
      options->min_hand_presence_confidence);

  auto* hand_landmarker_graph_options =
      options_proto->mutable_hand_landmarker_graph_options();
  hand_landmarker_graph_options->set_min_tracking_confidence(
      options->min_tracking_confidence);

  // Configure hand gesture recognizer options.
  auto base_options_proto_for_gesture_recognizer =
      std::make_unique<tasks::core::proto::BaseOptions>(
          tasks::core::ConvertBaseOptionsToProto(
              &(options->base_options_for_gesture_recognizer)));
  base_options_proto_for_gesture_recognizer->set_use_stream_mode(
      use_stream_mode);
  auto* hand_gesture_recognizer_graph_options =
      options_proto->mutable_hand_gesture_recognizer_graph_options();
  hand_gesture_recognizer_graph_options->mutable_base_options()->Swap(
      base_options_proto_for_gesture_recognizer.get());
  if (options->min_gesture_confidence >= 0) {
    hand_gesture_recognizer_graph_options->mutable_classifier_options()
        ->set_score_threshold(options->min_gesture_confidence);
  }
  return options_proto;
}

}  // namespace

absl::StatusOr<std::unique_ptr<GestureRecognizer>> GestureRecognizer::Create(
    std::unique_ptr<GestureRecognizerOptions> options) {
  auto options_proto = ConvertGestureRecognizerGraphOptionsProto(options.get());
  tasks::core::PacketsCallback packets_callback = nullptr;
  if (options->result_callback) {
    auto result_callback = options->result_callback;
    packets_callback = [=](absl::StatusOr<tasks::core::PacketMap>
                               status_or_packets) {
      if (!status_or_packets.ok()) {
        Image image;
        result_callback(status_or_packets.status(), image,
                        Timestamp::Unset().Value());
        return;
      }
      if (status_or_packets.value()[kImageOutStreamName].IsEmpty()) {
        return;
      }
      Packet image_packet = status_or_packets.value()[kImageOutStreamName];
      if (status_or_packets.value()[kHandGesturesStreamName].IsEmpty()) {
        Packet empty_packet =
            status_or_packets.value()[kHandGesturesStreamName];
        result_callback(
            {{{}, {}, {}, {}}}, image_packet.Get<Image>(),
            empty_packet.Timestamp().Value() / kMicroSecondsPerMilliSecond);
        return;
      }
      Packet gesture_packet =
          status_or_packets.value()[kHandGesturesStreamName];
      Packet handedness_packet =
          status_or_packets.value()[kHandednessStreamName];
      Packet hand_landmarks_packet =
          status_or_packets.value()[kHandLandmarksStreamName];
      Packet hand_world_landmarks_packet =
          status_or_packets.value()[kHandWorldLandmarksStreamName];
      result_callback(
          {{gesture_packet.Get<std::vector<ClassificationList>>(),
            handedness_packet.Get<std::vector<ClassificationList>>(),
            hand_landmarks_packet.Get<std::vector<NormalizedLandmarkList>>(),
            hand_world_landmarks_packet.Get<std::vector<LandmarkList>>()}},
          image_packet.Get<Image>(),
          gesture_packet.Timestamp().Value() / kMicroSecondsPerMilliSecond);
    };
  }
  return core::VisionTaskApiFactory::Create<GestureRecognizer,
                                            GestureRecognizerGraphOptionsProto>(
      CreateGraphConfig(
          std::move(options_proto),
          options->running_mode == core::RunningMode::LIVE_STREAM),
      std::move(options->base_options.op_resolver), options->running_mode,
      std::move(packets_callback));
}

absl::StatusOr<GestureRecognitionResult> GestureRecognizer::Recognize(
    mediapipe::Image image) {
  if (image.UsesGpu()) {
    return CreateStatusWithPayload(
        absl::StatusCode::kInvalidArgument,
        "GPU input images are currently not supported.",
        MediaPipeTasksStatus::kRunnerUnexpectedInputError);
  }
  ASSIGN_OR_RETURN(auto output_packets,
                   ProcessImageData({{kImageInStreamName,
                                      MakePacket<Image>(std::move(image))}}));
  if (output_packets[kHandGesturesStreamName].IsEmpty()) {
    return {{{}, {}, {}, {}}};
  }
  return {
      {/* gestures= */ {output_packets[kHandGesturesStreamName]
                            .Get<std::vector<ClassificationList>>()},
       /* handedness= */
       {output_packets[kHandednessStreamName]
            .Get<std::vector<mediapipe::ClassificationList>>()},
       /* hand_landmarks= */
       {output_packets[kHandLandmarksStreamName]
            .Get<std::vector<mediapipe::NormalizedLandmarkList>>()},
       /* hand_world_landmarks */
       {output_packets[kHandWorldLandmarksStreamName]
            .Get<std::vector<mediapipe::LandmarkList>>()}},
  };
}

absl::StatusOr<GestureRecognitionResult> GestureRecognizer::RecognizeForVideo(
    mediapipe::Image image, int64 timestamp_ms) {
  if (image.UsesGpu()) {
    return CreateStatusWithPayload(
        absl::StatusCode::kInvalidArgument,
        absl::StrCat("GPU input images are currently not supported."),
        MediaPipeTasksStatus::kRunnerUnexpectedInputError);
  }
  ASSIGN_OR_RETURN(
      auto output_packets,
      ProcessVideoData(
          {{kImageInStreamName,
            MakePacket<Image>(std::move(image))
                .At(Timestamp(timestamp_ms * kMicroSecondsPerMilliSecond))}}));
  if (output_packets[kHandGesturesStreamName].IsEmpty()) {
    return {{{}, {}, {}, {}}};
  }
  return {
      {/* gestures= */ {output_packets[kHandGesturesStreamName]
                            .Get<std::vector<ClassificationList>>()},
       /* handedness= */
       {output_packets[kHandednessStreamName]
            .Get<std::vector<mediapipe::ClassificationList>>()},
       /* hand_landmarks= */
       {output_packets[kHandLandmarksStreamName]
            .Get<std::vector<mediapipe::NormalizedLandmarkList>>()},
       /* hand_world_landmarks */
       {output_packets[kHandWorldLandmarksStreamName]
            .Get<std::vector<mediapipe::LandmarkList>>()}},
  };
}

absl::Status GestureRecognizer::RecognizeAsync(mediapipe::Image image,
                                               int64 timestamp_ms) {
  if (image.UsesGpu()) {
    return CreateStatusWithPayload(
        absl::StatusCode::kInvalidArgument,
        absl::StrCat("GPU input images are currently not supported."),
        MediaPipeTasksStatus::kRunnerUnexpectedInputError);
  }
  return SendLiveStreamData(
      {{kImageInStreamName,
        MakePacket<Image>(std::move(image))
            .At(Timestamp(timestamp_ms * kMicroSecondsPerMilliSecond))}});
}

}  // namespace gesture_recognizer
}  // namespace vision
}  // namespace tasks
}  // namespace mediapipe
<|endoftext|>


<commit_before><commit_msg>Planning: reduced the distance for cost calculation.<commit_after><|endoftext|>


<commit_before><commit_msg>prediction: bug fix for null pointer issue in FeatureExtractor<commit_after><|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "formeditoritem.h"
#include "formeditorscene.h"
#include "formeditornodeinstanceview.h"
#include "selectiontool.h"

#include <modelnode.h>
#include <nodemetainfo.h>
#include <widgetqueryview.h>

#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsView>
#include <QTimeLine>

#include <cmath>

#include <invalidmodelnodeexception.h>
#include <invalidnodestateexception.h>

namespace QmlDesigner {

FormEditorScene *FormEditorItem::scene() const {
    return qobject_cast<FormEditorScene*>(QGraphicsItem::scene());
}

FormEditorItem::FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene)
    : QGraphicsObject(scene->formLayerItem()),
    m_snappingLineCreator(this),
    m_qmlItemNode(qmlItemNode),
    m_borderWidth(1.0),
    m_opacity(0.6)
{
    setCacheMode(QGraphicsItem::DeviceCoordinateCache);
    setup();
}

void FormEditorItem::setup()
{
    if (qmlItemNode().hasInstanceParent())
        setParentItem(scene()->itemForQmlItemNode(qmlItemNode().instanceParent().toQmlItemNode()));

    if (QGraphicsItem::parentItem() == scene()->formLayerItem())
        m_borderWidth = 0.0;

    setFlag(QGraphicsItem::ItemIsMovable, true);
    updateGeometry();
    updateVisibilty();
}

QRectF FormEditorItem::boundingRect() const
{
    return m_boundingRect;
}

void FormEditorItem::updateGeometry()
{
    prepareGeometryChange();
    m_boundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.);
    setTransform(qmlItemNode().instanceTransform());
    setTransform(m_attentionTransform, true);
    //the property for zValue is called z in QGraphicsObject
    Q_ASSERT(qmlItemNode().instanceValue("z").isValid());
    setZValue(qmlItemNode().instanceValue("z").toDouble());
}

void FormEditorItem::updateVisibilty()
{
//    setVisible(nodeInstance().isVisible());
//    setOpacity(nodeInstance().opacity());
}

void FormEditorItem::showAttention()
{
    if (m_attentionTimeLine.isNull()) {
        m_attentionTimeLine = new QTimeLine(500, this);
        m_attentionTimeLine->setCurveShape(QTimeLine::SineCurve);
        connect(m_attentionTimeLine.data(), SIGNAL(valueChanged(qreal)), SLOT(changeAttention(qreal)));
        connect(m_attentionTimeLine.data(), SIGNAL(finished()), m_attentionTimeLine.data(), SLOT(deleteLater()));

        m_attentionTimeLine->start();
    }
}

void FormEditorItem::changeAttention(qreal value)
{
    if (QGraphicsItem::parentItem() == scene()->formLayerItem()) {
        setAttentionHighlight(value);
    } else {
        setAttentionHighlight(value);
        setAttentionScale(value);
    }
}

FormEditorView *FormEditorItem::formEditorView() const
{
    return scene()->editorView();
}

void FormEditorItem::setAttentionScale(double sinusScale)
{
    if (!qFuzzyIsNull(sinusScale)) {
        double scale = std::sqrt(sinusScale);
        m_attentionTransform.reset();
        QPointF centerPoint(qmlItemNode().instanceBoundingRect().center());
        m_attentionTransform.translate(centerPoint.x(), centerPoint.y());
        m_attentionTransform.scale(scale * 0.15 + 1.0, scale * 0.15 + 1.0);
        m_attentionTransform.translate(-centerPoint.x(), -centerPoint.y());
        m_inverseAttentionTransform = m_attentionTransform.inverted();
        prepareGeometryChange();
        setTransform(qmlItemNode().instanceTransform());
        setTransform(m_attentionTransform, true);
    } else {
        m_attentionTransform.reset();
        prepareGeometryChange();
        setTransform(qmlItemNode().instanceTransform());
    }
}

void FormEditorItem::setAttentionHighlight(double value)
{
    m_opacity = 0.6 + value;
    if (QGraphicsItem::parentItem() == scene()->formLayerItem())
        m_borderWidth = value * 4;
    else
        m_borderWidth = 1. + value * 3;

    update();
}

void FormEditorItem::setHighlightBoundingRect(bool highlight)
{
    if (m_highlightBoundingRect != highlight) {
        m_highlightBoundingRect = highlight;
        update();
    }
}

FormEditorItem::~FormEditorItem()
{
   scene()->removeItemFromHash(this);
}

/* \brief returns the parent item skipping all proxyItem*/
FormEditorItem *FormEditorItem::parentItem() const
{
    return qgraphicsitem_cast<FormEditorItem*> (QGraphicsItem::parentItem());
}

FormEditorItem* FormEditorItem::fromQGraphicsItem(QGraphicsItem *graphicsItem)
{
    return qgraphicsitem_cast<FormEditorItem*>(graphicsItem);
}

//static QRectF alignedRect(const QRectF &rect)
//{
//    QRectF alignedRect(rect);
//    alignedRect.setTop(std::floor(rect.top()) + 0.5);
//    alignedRect.setBottom(std::floor(rect.bottom()) + 0.5);
//    alignedRect.setLeft(std::floor(rect.left()) + 0.5);
//    alignedRect.setRight(std::floor(rect.right()) + 0.5);
//
//    return alignedRect;
//}

void FormEditorItem::paintBoundingRect(QPainter *painter) const
{
    if (QGraphicsItem::parentItem() == scene()->formLayerItem() && qFuzzyIsNull(m_borderWidth))
          return;

    QPen pen;
    pen.setJoinStyle(Qt::MiterJoin);

    switch(scene()->paintMode()) {
        case FormEditorScene::AnchorMode: {
                pen.setColor(Qt::black);
                pen.setWidth(m_borderWidth);
            }
            break;
        case FormEditorScene::NormalMode: {
                if (m_highlightBoundingRect)
                    pen.setColor("#AAAAAA");
                else
                    pen.setColor("#888888");
            }
            break;
    }

    painter->setPen(pen);
//    int offset =  m_borderWidth / 2;
    painter->drawRect(boundingRect().adjusted(0., 0., -1., -1.));
}

void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    if (!qmlItemNode().isValid())
        return;

    painter->save();
    painter->setRenderHint(QPainter::Antialiasing, true);
    switch(scene()->paintMode()) {
        case FormEditorScene::AnchorMode:
            painter->setOpacity(m_opacity);
            break;
        case FormEditorScene::NormalMode:
            painter->setOpacity(qmlItemNode().instanceValue("opacity").toDouble());
            break;
    }

    qmlItemNode().paintInstance(painter);

    painter->setRenderHint(QPainter::Antialiasing, false);

    if (scene()->showBoundingRects() || m_highlightBoundingRect)
        paintBoundingRect(painter);

    painter->restore();
}

AbstractFormEditorTool* FormEditorItem::tool() const
{
    return static_cast<FormEditorScene*>(scene())->currentTool();
}

SnapLineMap FormEditorItem::topSnappingLines() const
{
    return m_snappingLineCreator.topLines();
}

SnapLineMap FormEditorItem::bottomSnappingLines() const
{
    return m_snappingLineCreator.bottomLines();
}

SnapLineMap FormEditorItem::leftSnappingLines() const
{
    return m_snappingLineCreator.leftLines();
}

SnapLineMap FormEditorItem::rightSnappingLines() const
{
    return m_snappingLineCreator.rightLines();
}

SnapLineMap FormEditorItem::horizontalCenterSnappingLines() const
{
    return m_snappingLineCreator.horizontalCenterLines();
}

SnapLineMap FormEditorItem::verticalCenterSnappingLines() const
{
    return m_snappingLineCreator.verticalCenterLines();
}

SnapLineMap FormEditorItem::topSnappingOffsets() const
{
    return m_snappingLineCreator.topOffsets();
}

SnapLineMap FormEditorItem::bottomSnappingOffsets() const
{
    return m_snappingLineCreator.bottomOffsets();
}

SnapLineMap FormEditorItem::leftSnappingOffsets() const
{
    return m_snappingLineCreator.leftOffsets();
}

SnapLineMap FormEditorItem::rightSnappingOffsets() const
{
    return m_snappingLineCreator.rightOffsets();
}

void FormEditorItem::updateSnappingLines(const QList<FormEditorItem*> &exceptionList,
                                         FormEditorItem *transformationSpaceItem)
{
    m_snappingLineCreator.update(exceptionList, transformationSpaceItem);
}

QList<FormEditorItem*> FormEditorItem::childFormEditorItems() const
{
    QList<FormEditorItem*> formEditorItemList;

    foreach (QGraphicsItem *item, childItems()) {
        FormEditorItem *formEditorItem = fromQGraphicsItem(item);
        if (formEditorItem)
            formEditorItemList.append(formEditorItem);
    }

    return formEditorItemList;
}

bool FormEditorItem::isContainer() const
{
    return qmlItemNode().modelNode().metaInfo().isContainer();
}

QmlItemNode FormEditorItem::qmlItemNode() const
{
    return m_qmlItemNode;
}

}

<commit_msg>Fix drawing bug in bounding rect for form editor items<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "formeditoritem.h"
#include "formeditorscene.h"
#include "formeditornodeinstanceview.h"
#include "selectiontool.h"

#include <modelnode.h>
#include <nodemetainfo.h>
#include <widgetqueryview.h>

#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsView>
#include <QTimeLine>

#include <cmath>

#include <invalidmodelnodeexception.h>
#include <invalidnodestateexception.h>

namespace QmlDesigner {

FormEditorScene *FormEditorItem::scene() const {
    return qobject_cast<FormEditorScene*>(QGraphicsItem::scene());
}

FormEditorItem::FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene)
    : QGraphicsObject(scene->formLayerItem()),
    m_snappingLineCreator(this),
    m_qmlItemNode(qmlItemNode),
    m_borderWidth(1.0),
    m_opacity(0.6),
    m_highlightBoundingRect(false)
{
    setCacheMode(QGraphicsItem::DeviceCoordinateCache);
    setup();
}

void FormEditorItem::setup()
{
    if (qmlItemNode().hasInstanceParent())
        setParentItem(scene()->itemForQmlItemNode(qmlItemNode().instanceParent().toQmlItemNode()));

    if (QGraphicsItem::parentItem() == scene()->formLayerItem())
        m_borderWidth = 0.0;

    setFlag(QGraphicsItem::ItemIsMovable, true);
    updateGeometry();
    updateVisibilty();
}

QRectF FormEditorItem::boundingRect() const
{
    return m_boundingRect;
}

void FormEditorItem::updateGeometry()
{
    prepareGeometryChange();
    m_boundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.);
    setTransform(qmlItemNode().instanceTransform());
    setTransform(m_attentionTransform, true);
    //the property for zValue is called z in QGraphicsObject
    Q_ASSERT(qmlItemNode().instanceValue("z").isValid());
    setZValue(qmlItemNode().instanceValue("z").toDouble());
}

void FormEditorItem::updateVisibilty()
{
//    setVisible(nodeInstance().isVisible());
//    setOpacity(nodeInstance().opacity());
}

void FormEditorItem::showAttention()
{
    if (m_attentionTimeLine.isNull()) {
        m_attentionTimeLine = new QTimeLine(500, this);
        m_attentionTimeLine->setCurveShape(QTimeLine::SineCurve);
        connect(m_attentionTimeLine.data(), SIGNAL(valueChanged(qreal)), SLOT(changeAttention(qreal)));
        connect(m_attentionTimeLine.data(), SIGNAL(finished()), m_attentionTimeLine.data(), SLOT(deleteLater()));

        m_attentionTimeLine->start();
    }
}

void FormEditorItem::changeAttention(qreal value)
{
    if (QGraphicsItem::parentItem() == scene()->formLayerItem()) {
        setAttentionHighlight(value);
    } else {
        setAttentionHighlight(value);
        setAttentionScale(value);
    }
}

FormEditorView *FormEditorItem::formEditorView() const
{
    return scene()->editorView();
}

void FormEditorItem::setAttentionScale(double sinusScale)
{
    if (!qFuzzyIsNull(sinusScale)) {
        double scale = std::sqrt(sinusScale);
        m_attentionTransform.reset();
        QPointF centerPoint(qmlItemNode().instanceBoundingRect().center());
        m_attentionTransform.translate(centerPoint.x(), centerPoint.y());
        m_attentionTransform.scale(scale * 0.15 + 1.0, scale * 0.15 + 1.0);
        m_attentionTransform.translate(-centerPoint.x(), -centerPoint.y());
        m_inverseAttentionTransform = m_attentionTransform.inverted();
        prepareGeometryChange();
        setTransform(qmlItemNode().instanceTransform());
        setTransform(m_attentionTransform, true);
    } else {
        m_attentionTransform.reset();
        prepareGeometryChange();
        setTransform(qmlItemNode().instanceTransform());
    }
}

void FormEditorItem::setAttentionHighlight(double value)
{
    m_opacity = 0.6 + value;
    if (QGraphicsItem::parentItem() == scene()->formLayerItem())
        m_borderWidth = value * 4;
    else
        m_borderWidth = 1. + value * 3;

    update();
}

void FormEditorItem::setHighlightBoundingRect(bool highlight)
{
    if (m_highlightBoundingRect != highlight) {
        m_highlightBoundingRect = highlight;
        update();
    }
}

FormEditorItem::~FormEditorItem()
{
   scene()->removeItemFromHash(this);
}

/* \brief returns the parent item skipping all proxyItem*/
FormEditorItem *FormEditorItem::parentItem() const
{
    return qgraphicsitem_cast<FormEditorItem*> (QGraphicsItem::parentItem());
}

FormEditorItem* FormEditorItem::fromQGraphicsItem(QGraphicsItem *graphicsItem)
{
    return qgraphicsitem_cast<FormEditorItem*>(graphicsItem);
}

//static QRectF alignedRect(const QRectF &rect)
//{
//    QRectF alignedRect(rect);
//    alignedRect.setTop(std::floor(rect.top()) + 0.5);
//    alignedRect.setBottom(std::floor(rect.bottom()) + 0.5);
//    alignedRect.setLeft(std::floor(rect.left()) + 0.5);
//    alignedRect.setRight(std::floor(rect.right()) + 0.5);
//
//    return alignedRect;
//}

void FormEditorItem::paintBoundingRect(QPainter *painter) const
{
    if (QGraphicsItem::parentItem() == scene()->formLayerItem() && qFuzzyIsNull(m_borderWidth))
          return;

    QPen pen;
    pen.setJoinStyle(Qt::MiterJoin);

    switch(scene()->paintMode()) {
        case FormEditorScene::AnchorMode: {
                pen.setColor(Qt::black);
                pen.setWidth(m_borderWidth);
            }
            break;
        case FormEditorScene::NormalMode: {
                if (scene()->showBoundingRects()) {
                    if (m_highlightBoundingRect)
                        pen.setColor("#AAAAAA");
                    else
                        pen.setColor("#888888");
                } else {
                    if (m_highlightBoundingRect)
                        pen.setColor("#AAAAAA");
                    else
                        pen.setColor(Qt::transparent);
                }
            }
            break;
    }

    painter->setPen(pen);
//    int offset =  m_borderWidth / 2;
    painter->drawRect(boundingRect().adjusted(0., 0., -1., -1.));
}

void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    if (!qmlItemNode().isValid())
        return;

    painter->save();
    painter->setRenderHint(QPainter::Antialiasing, true);
    switch(scene()->paintMode()) {
        case FormEditorScene::AnchorMode:
            painter->setOpacity(m_opacity);
            break;
        case FormEditorScene::NormalMode:
            painter->setOpacity(qmlItemNode().instanceValue("opacity").toDouble());
            break;
    }

    qmlItemNode().paintInstance(painter);

    painter->setRenderHint(QPainter::Antialiasing, false);

    paintBoundingRect(painter);

    painter->restore();
}

AbstractFormEditorTool* FormEditorItem::tool() const
{
    return static_cast<FormEditorScene*>(scene())->currentTool();
}

SnapLineMap FormEditorItem::topSnappingLines() const
{
    return m_snappingLineCreator.topLines();
}

SnapLineMap FormEditorItem::bottomSnappingLines() const
{
    return m_snappingLineCreator.bottomLines();
}

SnapLineMap FormEditorItem::leftSnappingLines() const
{
    return m_snappingLineCreator.leftLines();
}

SnapLineMap FormEditorItem::rightSnappingLines() const
{
    return m_snappingLineCreator.rightLines();
}

SnapLineMap FormEditorItem::horizontalCenterSnappingLines() const
{
    return m_snappingLineCreator.horizontalCenterLines();
}

SnapLineMap FormEditorItem::verticalCenterSnappingLines() const
{
    return m_snappingLineCreator.verticalCenterLines();
}

SnapLineMap FormEditorItem::topSnappingOffsets() const
{
    return m_snappingLineCreator.topOffsets();
}

SnapLineMap FormEditorItem::bottomSnappingOffsets() const
{
    return m_snappingLineCreator.bottomOffsets();
}

SnapLineMap FormEditorItem::leftSnappingOffsets() const
{
    return m_snappingLineCreator.leftOffsets();
}

SnapLineMap FormEditorItem::rightSnappingOffsets() const
{
    return m_snappingLineCreator.rightOffsets();
}

void FormEditorItem::updateSnappingLines(const QList<FormEditorItem*> &exceptionList,
                                         FormEditorItem *transformationSpaceItem)
{
    m_snappingLineCreator.update(exceptionList, transformationSpaceItem);
}

QList<FormEditorItem*> FormEditorItem::childFormEditorItems() const
{
    QList<FormEditorItem*> formEditorItemList;

    foreach (QGraphicsItem *item, childItems()) {
        FormEditorItem *formEditorItem = fromQGraphicsItem(item);
        if (formEditorItem)
            formEditorItemList.append(formEditorItem);
    }

    return formEditorItemList;
}

bool FormEditorItem::isContainer() const
{
    return qmlItemNode().modelNode().metaInfo().isContainer();
}

QmlItemNode FormEditorItem::qmlItemNode() const
{
    return m_qmlItemNode;
}

}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "puppetcreator.h"

#include <QProcess>
#include <QTemporaryDir>
#include <QCoreApplication>
#include <QCryptographicHash>
#include <QDateTime>
#include <QMessageBox>
#include <QThread>

#include <projectexplorer/kit.h>
#include <projectexplorer/toolchain.h>
#include <utils/environment.h>
#include <coreplugin/icore.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
#include <coreplugin/icore.h>

#include <qmldesignerwarning.h>
#include "puppetbuildprogressdialog.h"

namespace QmlDesigner {

bool PuppetCreator::m_useOnlyFallbackPuppet = !qgetenv("USE_ONLY_FALLBACK_PUPPET").isEmpty();
QHash<Core::Id, PuppetCreator::PuppetType> PuppetCreator::m_qml1PuppetForKitPuppetHash;
QHash<Core::Id, PuppetCreator::PuppetType> PuppetCreator::m_qml2PuppetForKitPuppetHash;

QByteArray PuppetCreator::qtHash() const
{
    if (m_kit) {
        QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);
        if (currentQtVersion)
            return QCryptographicHash::hash(currentQtVersion->qmakeProperty("QT_INSTALL_DATA").toUtf8(), QCryptographicHash::Sha1).toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
    }

    return QByteArray();
}

QDateTime PuppetCreator::qtLastModified() const
{
    if (m_kit) {
        QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);
        if (currentQtVersion)
            return QFileInfo(currentQtVersion->qmakeProperty("QT_INSTALL_LIBS")).lastModified();
    }

    return QDateTime();
}

PuppetCreator::PuppetCreator(ProjectExplorer::Kit *kit, const QString &qtCreatorVersion)
    : m_qtCreatorVersion(qtCreatorVersion),
      m_kit(kit),
      m_availablePuppetType(FallbackPuppet)
{
}

PuppetCreator::~PuppetCreator()
{
}

void PuppetCreator::createPuppetExecutableIfMissing(PuppetCreator::QmlPuppetVersion puppetVersion)
{
    if (puppetVersion == Qml1Puppet)
        createQml1PuppetExecutableIfMissing();
    else
        createQml2PuppetExecutableIfMissing();
}

QProcess *PuppetCreator::createPuppetProcess(PuppetCreator::QmlPuppetVersion puppetVersion, const QString &puppetMode, const QString &socketToken, QObject *handlerObject, const char *outputSlot, const char *finishSlot) const
{
    QString puppetPath;
    if (puppetVersion == Qml1Puppet)
        puppetPath = qmlpuppetPath(m_availablePuppetType);
     else
        puppetPath = qml2puppetPath(m_availablePuppetType);

    return puppetProcess(puppetPath,
                         puppetMode,
                         socketToken,
                         handlerObject,
                         outputSlot,
                         finishSlot);
}

QProcess *PuppetCreator::puppetProcess(const QString &puppetPath,
                                       const QString &puppetMode,
                                       const QString &socketToken,
                                       QObject *handlerObject,
                                       const char *outputSlot,
                                       const char *finishSlot) const
{
    QProcess *puppetProcess = new QProcess;
    puppetProcess->setObjectName(puppetMode);
    puppetProcess->setProcessEnvironment(processEnvironment());
    QObject::connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), puppetProcess, SLOT(kill()));
    QObject::connect(puppetProcess, SIGNAL(finished(int,QProcess::ExitStatus)), handlerObject, finishSlot);
    bool fowardQmlpuppetOutput = !qgetenv("FORWARD_QMLPUPPET_OUTPUT").isEmpty();
    if (fowardQmlpuppetOutput) {
        puppetProcess->setProcessChannelMode(QProcess::MergedChannels);
        QObject::connect(puppetProcess, SIGNAL(readyRead()), handlerObject, outputSlot);
    }
    puppetProcess->start(puppetPath, QStringList() << socketToken << puppetMode << "-graphicssystem raster");

    if (!qgetenv("DEBUG_QML_PUPPET").isEmpty())
        QMessageBox::information(Core::ICore::dialogParent(),
                                 QStringLiteral("Puppet is starting ..."),
                                 QStringLiteral("You can now attach your debugger to the puppet."));

    return puppetProcess;
}

static QString idealProcessCount()
{
    int processCount = QThread::idealThreadCount() + 1;
    if (processCount < 1)
        processCount = 4;

    return QString::number(processCount);
}

bool PuppetCreator::build(const QString &qmlPuppetProjectFilePath) const
{
    PuppetBuildProgressDialog progressDialog;

    m_compileLog.clear();

    QTemporaryDir buildDirectory;

    bool buildSucceeded = false;

    if (qtIsSupported()) {
        if (buildDirectory.isValid()) {
            QStringList qmakeArguments;
            qmakeArguments.append(QStringLiteral("-r"));
            qmakeArguments.append(QStringLiteral("-after"));
            qmakeArguments.append(QStringLiteral("DESTDIR=") + qmlpuppetDirectory(UserSpacePuppet));
#ifndef QT_DEBUG
            qmakeArguments.append(QStringLiteral("CONFIG+=release"));
#endif
            qmakeArguments.append(qmlPuppetProjectFilePath);
            buildSucceeded = startBuildProcess(buildDirectory.path(), qmakeCommand(), qmakeArguments);
            if (buildSucceeded) {
                progressDialog.show();
                QString buildingCommand = buildCommand();
                QStringList buildArguments;
                if (buildingCommand.contains("make")) {
                    buildArguments.append(QStringLiteral("-j"));
                    buildArguments.append(idealProcessCount());
                }
                buildSucceeded = startBuildProcess(buildDirectory.path(), buildingCommand, buildArguments, &progressDialog);
                progressDialog.close();
            }

            if (!buildSucceeded)
                QmlDesignerWarning::show(QCoreApplication::translate("PuppetCreator", "Emulation layer building was unsuccessful"),
                                         QCoreApplication::translate("PuppetCreator",
                                                                     "The emulation layer (Qml Puppet) cannot be built. "
                                                                     "The fallback emulation layer, which does not support all features, will be used."
                                                                     ));
        }
    } else {
        QmlDesignerWarning::show(QCoreApplication::translate("PuppetCreator", "Qt Version is not supported"),
                                 QCoreApplication::translate("PuppetCreator",
                                                             "The emulation layer (Qml Puppet) cannot be built because the Qt version is too old "
                                                             "or it cannot run natively on your computer. "
                                                             "The fallback emulation layer, which does not support all features, will be used."
                                                             ));
    }

    return buildSucceeded;
}

static void warnAboutInvalidKit()
{
    QmlDesignerWarning::show(QCoreApplication::translate("PuppetCreator", "Kit is invalid"),
                             QCoreApplication::translate("PuppetCreator",
                                                         "The emulation layer (Qml Puppet) cannot be built because the kit is not configured correctly. "
                                                         "For example the compiler can be misconfigured. "
                                                         "Fix the kit configuration and restart Qt Creator. "
                                                         "Otherwise, the fallback emulation layer, which does not support all features, will be used."
                                                         ));
}

void PuppetCreator::createQml1PuppetExecutableIfMissing()
{
    if (!m_useOnlyFallbackPuppet && m_kit) {
            if (m_qml1PuppetForKitPuppetHash.contains(m_kit->id())) {
                m_availablePuppetType = m_qml1PuppetForKitPuppetHash.value(m_kit->id());
            } else if (checkQmlpuppetIsReady()) {
                m_availablePuppetType = UserSpacePuppet;
            } else {
                if (m_kit->isValid()) {

                    bool buildSucceeded = build(qmlpuppetProjectFile());
                    if (buildSucceeded)
                        m_availablePuppetType = UserSpacePuppet;
                    else
                        m_availablePuppetType = FallbackPuppet;
                } else {
                    warnAboutInvalidKit();
                    m_availablePuppetType = FallbackPuppet;
                }
                m_qml1PuppetForKitPuppetHash.insert(m_kit->id(), m_availablePuppetType);
            }
    } else {
        m_availablePuppetType = FallbackPuppet;
    }
}

void PuppetCreator::createQml2PuppetExecutableIfMissing()
{
    if (!m_useOnlyFallbackPuppet && m_kit) {

        if (m_qml2PuppetForKitPuppetHash.contains(m_kit->id())) {
            m_availablePuppetType = m_qml2PuppetForKitPuppetHash.value(m_kit->id());
        } else if (checkQml2puppetIsReady()) {
            m_availablePuppetType = UserSpacePuppet;
        } else {
            if (m_kit->isValid()) {
                bool buildSucceeded = build(qml2puppetProjectFile());
                if (buildSucceeded)
                    m_availablePuppetType = UserSpacePuppet;
                else
                    m_availablePuppetType = FallbackPuppet;
            } else {
                warnAboutInvalidKit();
                m_availablePuppetType = FallbackPuppet;
            }
            m_qml2PuppetForKitPuppetHash.insert(m_kit->id(), m_availablePuppetType);
        }
    } else {
        m_availablePuppetType = FallbackPuppet;
    }
}

QString PuppetCreator::qmlpuppetDirectory(PuppetType puppetType) const
{

    if (puppetType == UserSpacePuppet)
        return Core::ICore::userResourcePath()
                + QStringLiteral("/qmlpuppet/")
                + QCoreApplication::applicationVersion() + QStringLiteral("/")
                + qtHash();

    return qmlpuppetFallbackDirectory();
}

QString PuppetCreator::qmlpuppetFallbackDirectory() const
{
    return QCoreApplication::applicationDirPath();
}

QString PuppetCreator::qml2puppetPath(PuppetType puppetType) const
{
    return qmlpuppetDirectory(puppetType) + QStringLiteral("/qml2puppet") + QStringLiteral(QTC_HOST_EXE_SUFFIX);
}

QString PuppetCreator::qmlpuppetPath(PuppetType puppetType) const
{
    return qmlpuppetDirectory(puppetType) + QStringLiteral("/qmlpuppet") + QStringLiteral(QTC_HOST_EXE_SUFFIX);
}

QProcessEnvironment PuppetCreator::processEnvironment() const
{
    Utils::Environment environment = Utils::Environment::systemEnvironment();
    m_kit->addToEnvironment(environment);
    environment.set("QML_BAD_GUI_RENDER_LOOP", "true");
    environment.set("QML_USE_MOCKUPS", "true");
    environment.set("QML_PUPPET_MODE", "true");

    return environment.toProcessEnvironment();
}

QString PuppetCreator::buildCommand() const
{
    Utils::Environment environment = Utils::Environment::systemEnvironment();
    m_kit->addToEnvironment(environment);

    ProjectExplorer::ToolChain *toolChain = ProjectExplorer::ToolChainKitInformation::toolChain(m_kit);

    if (toolChain)
        return toolChain->makeCommand(environment);

    return QString();
}

QString PuppetCreator::qmakeCommand() const
{
    QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);
    if (currentQtVersion)
        return currentQtVersion->qmakeCommand().toString();

    return QString();
}

QString PuppetCreator::compileLog() const
{
    return m_compileLog;
}

bool PuppetCreator::startBuildProcess(const QString &buildDirectoryPath,
                                      const QString &command,
                                      const QStringList &processArguments,
                                      PuppetBuildProgressDialog *progressDialog) const
{
    if (command.isEmpty())
        return false;

    QProcess process;
    process.setProcessChannelMode(QProcess::SeparateChannels);
    process.setProcessEnvironment(processEnvironment());
    process.setWorkingDirectory(buildDirectoryPath);
    process.start(command, processArguments);
    process.waitForStarted();
    while (process.waitForReadyRead(-1)) {
        QByteArray newOutput = process.readAllStandardOutput();
        if (progressDialog)
            progressDialog->newBuildOutput(newOutput);

        m_compileLog.append(newOutput);
    }

    process.waitForFinished();

    if (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0)
        return true;
    else
        return false;
}

QString PuppetCreator::puppetSourceDirectoryPath()
{
    return Core::ICore::resourcePath() + QStringLiteral("/qml/qmlpuppet");
}

QString PuppetCreator::qml2puppetProjectFile()
{
    return puppetSourceDirectoryPath() + QStringLiteral("/qml2puppet/qml2puppet.pro");
}

QString PuppetCreator::qmlpuppetProjectFile()
{
    return puppetSourceDirectoryPath() + QStringLiteral("/qmlpuppet/qmlpuppet.pro");
}

bool PuppetCreator::checkPuppetIsReady(const QString &puppetPath) const
{
    QFileInfo puppetFileInfo(puppetPath);

    return puppetFileInfo.exists() && puppetFileInfo.lastModified() > qtLastModified();
}

bool PuppetCreator::checkQml2puppetIsReady() const
{
    return checkPuppetIsReady(qml2puppetPath(UserSpacePuppet));
}

bool PuppetCreator::checkQmlpuppetIsReady() const
{
    return checkPuppetIsReady(qmlpuppetPath(UserSpacePuppet));
}

bool PuppetCreator::qtIsSupported() const
{
    QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);

    if (currentQtVersion
            && currentQtVersion->isValid()
            && currentQtVersion->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0)
            && (currentQtVersion->type() == QLatin1String(QtSupport::Constants::DESKTOPQT)
                || currentQtVersion->type() == QLatin1String(QtSupport::Constants::SIMULATORQT)))
        return true;

    return false;
}

bool PuppetCreator::checkPuppetVersion(const QString &qmlPuppetPath)
{

    QProcess qmlPuppetVersionProcess;
    qmlPuppetVersionProcess.start(qmlPuppetPath, QStringList() << "--version");
    qmlPuppetVersionProcess.waitForReadyRead(6000);

    QByteArray versionString = qmlPuppetVersionProcess.readAll();

    bool canConvert;
    unsigned int versionNumber = versionString.toUInt(&canConvert);

    return canConvert && versionNumber == 2;

}

} // namespace QmlDesigner
<commit_msg>QmlDesigner: Use -j X only for make<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "puppetcreator.h"

#include <QProcess>
#include <QTemporaryDir>
#include <QCoreApplication>
#include <QCryptographicHash>
#include <QDateTime>
#include <QMessageBox>
#include <QThread>

#include <projectexplorer/kit.h>
#include <projectexplorer/toolchain.h>
#include <utils/environment.h>
#include <coreplugin/icore.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
#include <coreplugin/icore.h>

#include <qmldesignerwarning.h>
#include "puppetbuildprogressdialog.h"

namespace QmlDesigner {

bool PuppetCreator::m_useOnlyFallbackPuppet = !qgetenv("USE_ONLY_FALLBACK_PUPPET").isEmpty();
QHash<Core::Id, PuppetCreator::PuppetType> PuppetCreator::m_qml1PuppetForKitPuppetHash;
QHash<Core::Id, PuppetCreator::PuppetType> PuppetCreator::m_qml2PuppetForKitPuppetHash;

QByteArray PuppetCreator::qtHash() const
{
    if (m_kit) {
        QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);
        if (currentQtVersion)
            return QCryptographicHash::hash(currentQtVersion->qmakeProperty("QT_INSTALL_DATA").toUtf8(), QCryptographicHash::Sha1).toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
    }

    return QByteArray();
}

QDateTime PuppetCreator::qtLastModified() const
{
    if (m_kit) {
        QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);
        if (currentQtVersion)
            return QFileInfo(currentQtVersion->qmakeProperty("QT_INSTALL_LIBS")).lastModified();
    }

    return QDateTime();
}

PuppetCreator::PuppetCreator(ProjectExplorer::Kit *kit, const QString &qtCreatorVersion)
    : m_qtCreatorVersion(qtCreatorVersion),
      m_kit(kit),
      m_availablePuppetType(FallbackPuppet)
{
}

PuppetCreator::~PuppetCreator()
{
}

void PuppetCreator::createPuppetExecutableIfMissing(PuppetCreator::QmlPuppetVersion puppetVersion)
{
    if (puppetVersion == Qml1Puppet)
        createQml1PuppetExecutableIfMissing();
    else
        createQml2PuppetExecutableIfMissing();
}

QProcess *PuppetCreator::createPuppetProcess(PuppetCreator::QmlPuppetVersion puppetVersion, const QString &puppetMode, const QString &socketToken, QObject *handlerObject, const char *outputSlot, const char *finishSlot) const
{
    QString puppetPath;
    if (puppetVersion == Qml1Puppet)
        puppetPath = qmlpuppetPath(m_availablePuppetType);
     else
        puppetPath = qml2puppetPath(m_availablePuppetType);

    return puppetProcess(puppetPath,
                         puppetMode,
                         socketToken,
                         handlerObject,
                         outputSlot,
                         finishSlot);
}

QProcess *PuppetCreator::puppetProcess(const QString &puppetPath,
                                       const QString &puppetMode,
                                       const QString &socketToken,
                                       QObject *handlerObject,
                                       const char *outputSlot,
                                       const char *finishSlot) const
{
    QProcess *puppetProcess = new QProcess;
    puppetProcess->setObjectName(puppetMode);
    puppetProcess->setProcessEnvironment(processEnvironment());
    QObject::connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), puppetProcess, SLOT(kill()));
    QObject::connect(puppetProcess, SIGNAL(finished(int,QProcess::ExitStatus)), handlerObject, finishSlot);
    bool fowardQmlpuppetOutput = !qgetenv("FORWARD_QMLPUPPET_OUTPUT").isEmpty();
    if (fowardQmlpuppetOutput) {
        puppetProcess->setProcessChannelMode(QProcess::MergedChannels);
        QObject::connect(puppetProcess, SIGNAL(readyRead()), handlerObject, outputSlot);
    }
    puppetProcess->start(puppetPath, QStringList() << socketToken << puppetMode << "-graphicssystem raster");

    if (!qgetenv("DEBUG_QML_PUPPET").isEmpty())
        QMessageBox::information(Core::ICore::dialogParent(),
                                 QStringLiteral("Puppet is starting ..."),
                                 QStringLiteral("You can now attach your debugger to the puppet."));

    return puppetProcess;
}

static QString idealProcessCount()
{
    int processCount = QThread::idealThreadCount() + 1;
    if (processCount < 1)
        processCount = 4;

    return QString::number(processCount);
}

bool PuppetCreator::build(const QString &qmlPuppetProjectFilePath) const
{
    PuppetBuildProgressDialog progressDialog;

    m_compileLog.clear();

    QTemporaryDir buildDirectory;

    bool buildSucceeded = false;

    if (qtIsSupported()) {
        if (buildDirectory.isValid()) {
            QStringList qmakeArguments;
            qmakeArguments.append(QStringLiteral("-r"));
            qmakeArguments.append(QStringLiteral("-after"));
            qmakeArguments.append(QStringLiteral("DESTDIR=") + qmlpuppetDirectory(UserSpacePuppet));
#ifndef QT_DEBUG
            qmakeArguments.append(QStringLiteral("CONFIG+=release"));
#endif
            qmakeArguments.append(qmlPuppetProjectFilePath);
            buildSucceeded = startBuildProcess(buildDirectory.path(), qmakeCommand(), qmakeArguments);
            if (buildSucceeded) {
                progressDialog.show();
                QString buildingCommand = buildCommand();
                QStringList buildArguments;
                if (buildingCommand == QStringLiteral("make")) {
                    buildArguments.append(QStringLiteral("-j"));
                    buildArguments.append(idealProcessCount());
                }
                buildSucceeded = startBuildProcess(buildDirectory.path(), buildingCommand, buildArguments, &progressDialog);
                progressDialog.close();
            }

            if (!buildSucceeded)
                QmlDesignerWarning::show(QCoreApplication::translate("PuppetCreator", "Emulation layer building was unsuccessful"),
                                         QCoreApplication::translate("PuppetCreator",
                                                                     "The emulation layer (Qml Puppet) cannot be built. "
                                                                     "The fallback emulation layer, which does not support all features, will be used."
                                                                     ));
        }
    } else {
        QmlDesignerWarning::show(QCoreApplication::translate("PuppetCreator", "Qt Version is not supported"),
                                 QCoreApplication::translate("PuppetCreator",
                                                             "The emulation layer (Qml Puppet) cannot be built because the Qt version is too old "
                                                             "or it cannot run natively on your computer. "
                                                             "The fallback emulation layer, which does not support all features, will be used."
                                                             ));
    }

    return buildSucceeded;
}

static void warnAboutInvalidKit()
{
    QmlDesignerWarning::show(QCoreApplication::translate("PuppetCreator", "Kit is invalid"),
                             QCoreApplication::translate("PuppetCreator",
                                                         "The emulation layer (Qml Puppet) cannot be built because the kit is not configured correctly. "
                                                         "For example the compiler can be misconfigured. "
                                                         "Fix the kit configuration and restart Qt Creator. "
                                                         "Otherwise, the fallback emulation layer, which does not support all features, will be used."
                                                         ));
}

void PuppetCreator::createQml1PuppetExecutableIfMissing()
{
    if (!m_useOnlyFallbackPuppet && m_kit) {
            if (m_qml1PuppetForKitPuppetHash.contains(m_kit->id())) {
                m_availablePuppetType = m_qml1PuppetForKitPuppetHash.value(m_kit->id());
            } else if (checkQmlpuppetIsReady()) {
                m_availablePuppetType = UserSpacePuppet;
            } else {
                if (m_kit->isValid()) {

                    bool buildSucceeded = build(qmlpuppetProjectFile());
                    if (buildSucceeded)
                        m_availablePuppetType = UserSpacePuppet;
                    else
                        m_availablePuppetType = FallbackPuppet;
                } else {
                    warnAboutInvalidKit();
                    m_availablePuppetType = FallbackPuppet;
                }
                m_qml1PuppetForKitPuppetHash.insert(m_kit->id(), m_availablePuppetType);
            }
    } else {
        m_availablePuppetType = FallbackPuppet;
    }
}

void PuppetCreator::createQml2PuppetExecutableIfMissing()
{
    if (!m_useOnlyFallbackPuppet && m_kit) {

        if (m_qml2PuppetForKitPuppetHash.contains(m_kit->id())) {
            m_availablePuppetType = m_qml2PuppetForKitPuppetHash.value(m_kit->id());
        } else if (checkQml2puppetIsReady()) {
            m_availablePuppetType = UserSpacePuppet;
        } else {
            if (m_kit->isValid()) {
                bool buildSucceeded = build(qml2puppetProjectFile());
                if (buildSucceeded)
                    m_availablePuppetType = UserSpacePuppet;
                else
                    m_availablePuppetType = FallbackPuppet;
            } else {
                warnAboutInvalidKit();
                m_availablePuppetType = FallbackPuppet;
            }
            m_qml2PuppetForKitPuppetHash.insert(m_kit->id(), m_availablePuppetType);
        }
    } else {
        m_availablePuppetType = FallbackPuppet;
    }
}

QString PuppetCreator::qmlpuppetDirectory(PuppetType puppetType) const
{

    if (puppetType == UserSpacePuppet)
        return Core::ICore::userResourcePath()
                + QStringLiteral("/qmlpuppet/")
                + QCoreApplication::applicationVersion() + QStringLiteral("/")
                + qtHash();

    return qmlpuppetFallbackDirectory();
}

QString PuppetCreator::qmlpuppetFallbackDirectory() const
{
    return QCoreApplication::applicationDirPath();
}

QString PuppetCreator::qml2puppetPath(PuppetType puppetType) const
{
    return qmlpuppetDirectory(puppetType) + QStringLiteral("/qml2puppet") + QStringLiteral(QTC_HOST_EXE_SUFFIX);
}

QString PuppetCreator::qmlpuppetPath(PuppetType puppetType) const
{
    return qmlpuppetDirectory(puppetType) + QStringLiteral("/qmlpuppet") + QStringLiteral(QTC_HOST_EXE_SUFFIX);
}

QProcessEnvironment PuppetCreator::processEnvironment() const
{
    Utils::Environment environment = Utils::Environment::systemEnvironment();
    m_kit->addToEnvironment(environment);
    environment.set("QML_BAD_GUI_RENDER_LOOP", "true");
    environment.set("QML_USE_MOCKUPS", "true");
    environment.set("QML_PUPPET_MODE", "true");

    return environment.toProcessEnvironment();
}

QString PuppetCreator::buildCommand() const
{
    Utils::Environment environment = Utils::Environment::systemEnvironment();
    m_kit->addToEnvironment(environment);

    ProjectExplorer::ToolChain *toolChain = ProjectExplorer::ToolChainKitInformation::toolChain(m_kit);

    if (toolChain)
        return toolChain->makeCommand(environment);

    return QString();
}

QString PuppetCreator::qmakeCommand() const
{
    QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);
    if (currentQtVersion)
        return currentQtVersion->qmakeCommand().toString();

    return QString();
}

QString PuppetCreator::compileLog() const
{
    return m_compileLog;
}

bool PuppetCreator::startBuildProcess(const QString &buildDirectoryPath,
                                      const QString &command,
                                      const QStringList &processArguments,
                                      PuppetBuildProgressDialog *progressDialog) const
{
    if (command.isEmpty())
        return false;

    QProcess process;
    process.setProcessChannelMode(QProcess::SeparateChannels);
    process.setProcessEnvironment(processEnvironment());
    process.setWorkingDirectory(buildDirectoryPath);
    process.start(command, processArguments);
    process.waitForStarted();
    while (process.waitForReadyRead(-1)) {
        QByteArray newOutput = process.readAllStandardOutput();
        if (progressDialog)
            progressDialog->newBuildOutput(newOutput);

        m_compileLog.append(newOutput);
    }

    process.waitForFinished();

    if (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0)
        return true;
    else
        return false;
}

QString PuppetCreator::puppetSourceDirectoryPath()
{
    return Core::ICore::resourcePath() + QStringLiteral("/qml/qmlpuppet");
}

QString PuppetCreator::qml2puppetProjectFile()
{
    return puppetSourceDirectoryPath() + QStringLiteral("/qml2puppet/qml2puppet.pro");
}

QString PuppetCreator::qmlpuppetProjectFile()
{
    return puppetSourceDirectoryPath() + QStringLiteral("/qmlpuppet/qmlpuppet.pro");
}

bool PuppetCreator::checkPuppetIsReady(const QString &puppetPath) const
{
    QFileInfo puppetFileInfo(puppetPath);

    return puppetFileInfo.exists() && puppetFileInfo.lastModified() > qtLastModified();
}

bool PuppetCreator::checkQml2puppetIsReady() const
{
    return checkPuppetIsReady(qml2puppetPath(UserSpacePuppet));
}

bool PuppetCreator::checkQmlpuppetIsReady() const
{
    return checkPuppetIsReady(qmlpuppetPath(UserSpacePuppet));
}

bool PuppetCreator::qtIsSupported() const
{
    QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(m_kit);

    if (currentQtVersion
            && currentQtVersion->isValid()
            && currentQtVersion->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0)
            && (currentQtVersion->type() == QLatin1String(QtSupport::Constants::DESKTOPQT)
                || currentQtVersion->type() == QLatin1String(QtSupport::Constants::SIMULATORQT)))
        return true;

    return false;
}

bool PuppetCreator::checkPuppetVersion(const QString &qmlPuppetPath)
{

    QProcess qmlPuppetVersionProcess;
    qmlPuppetVersionProcess.start(qmlPuppetPath, QStringList() << "--version");
    qmlPuppetVersionProcess.waitForReadyRead(6000);

    QByteArray versionString = qmlPuppetVersionProcess.readAll();

    bool canConvert;
    unsigned int versionNumber = versionString.toUInt(&canConvert);

    return canConvert && versionNumber == 2;

}

} // namespace QmlDesigner
<|endoftext|>