text: // Copyright 2013 The Flutter Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "flutter/shell/platform/embedder/embedder_engine.h"
  
  #include "flutter/fml/make_copyable.h"
  #include "flutter/shell/platform/embedder/vsync_waiter_embedder.h"
  
  namespace flutter {
  
  struct ShellArgs {
    Settings settings;
    Shell::CreateCallback<PlatformView> on_create_platform_view;
    Shell::CreateCallback<Rasterizer> on_create_rasterizer;
    ShellArgs(Settings p_settings,
              Shell::CreateCallback<PlatformView> p_on_create_platform_view,
              Shell::CreateCallback<Rasterizer> p_on_create_rasterizer)
        : settings(std::move(p_settings)),
          on_create_platform_view(std::move(p_on_create_platform_view)),
          on_create_rasterizer(std::move(p_on_create_rasterizer)) {}
  };
  
  EmbedderEngine::EmbedderEngine(
      std::unique_ptr<EmbedderThreadHost> thread_host,
      flutter::TaskRunners task_runners,
      flutter::Settings settings,
      RunConfiguration run_configuration,
      Shell::CreateCallback<PlatformView> on_create_platform_view,
      Shell::CreateCallback<Rasterizer> on_create_rasterizer
  #ifdef SHELL_ENABLE_GL
      ,
      EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback
  #endif
      )
      : thread_host_(std::move(thread_host)),
        task_runners_(task_runners),
        run_configuration_(std::move(run_configuration)),
        shell_args_(std::make_unique<ShellArgs>(std::move(settings),
                                                on_create_platform_view,
                                                on_create_rasterizer))
  #ifdef SHELL_ENABLE_GL
        ,
        external_texture_callback_(external_texture_callback)
  #endif
  {
  }
  
  EmbedderEngine::~EmbedderEngine() = default;
  
  bool EmbedderEngine::LaunchShell() {
    if (!shell_args_) {
      FML_DLOG(ERROR) << "Invalid shell arguments.";
      return false;
    }
  
    if (shell_) {
      FML_DLOG(ERROR) << "Shell already initialized";
    }
  
    shell_ = Shell::Create(
        flutter::PlatformData(), task_runners_, shell_args_->settings,
        shell_args_->on_create_platform_view, shell_args_->on_create_rasterizer);
  
    // Reset the args no matter what. They will never be used to initialize a
    // shell again.
    shell_args_.reset();
  
    return IsValid();
  }
  
  bool EmbedderEngine::CollectShell() {
    shell_.reset();
    return IsValid();
  }
  
  bool EmbedderEngine::RunRootIsolate() {
    if (!IsValid() || !run_configuration_.IsValid()) {
      return false;
    }
    shell_->RunEngine(std::move(run_configuration_));
    return true;
  }
  
  bool EmbedderEngine::IsValid() const {
    return static_cast<bool>(shell_);
  }
  
  const TaskRunners& EmbedderEngine::GetTaskRunners() const {
    return task_runners_;
  }
  
  bool EmbedderEngine::NotifyCreated() {
    if (!IsValid()) {
      return false;
    }
  
    shell_->GetPlatformView()->NotifyCreated();
    return true;
  }
  
  bool EmbedderEngine::NotifyDestroyed() {
    if (!IsValid()) {
      return false;
    }
  
    shell_->GetPlatformView()->NotifyDestroyed();
    return true;
  }
  
  bool EmbedderEngine::SetViewportMetrics(flutter::ViewportMetrics metrics) {
    if (!IsValid()) {
      return false;
    }
  
    auto platform_view = shell_->GetPlatformView();
    if (!platform_view) {
      return false;
    }
    platform_view->SetViewportMetrics(std::move(metrics));
    return true;
  }
  
  bool EmbedderEngine::DispatchPointerDataPacket(
      std::unique_ptr<flutter::PointerDataPacket> packet) {
    if (!IsValid() || !packet) {
      return false;
    }
  
    auto platform_view = shell_->GetPlatformView();
    if (!platform_view) {
      return false;
    }
  
    platform_view->DispatchPointerDataPacket(std::move(packet));
    return true;
  }
  
  bool EmbedderEngine::DispatchKeyDataPacket(
      std::unique_ptr<flutter::KeyDataPacket> packet,
      KeyDataResponse callback) {
    if (!IsValid() || !packet) {
      return false;
    }
  
    auto platform_view = shell_->GetPlatformView();
    if (!platform_view) {
      return false;
    }
  
    platform_view->DispatchKeyDataPacket(std::move(packet), std::move(callback));
    return true;
  }
  
  bool EmbedderEngine::SendPlatformMessage(
      fml::RefPtr<flutter::PlatformMessage> message) {
    if (!IsValid() || !message) {
      return false;
    }
  
    auto platform_view = shell_->GetPlatformView();
    if (!platform_view) {
      return false;
    }
  
    platform_view->DispatchPlatformMessage(message);
    return true;
  }
  
  bool EmbedderEngine::RegisterTexture(int64_t texture) {
  #ifdef SHELL_ENABLE_GL
    if (!IsValid() || !external_texture_callback_) {
      return false;
    }
    shell_->GetPlatformView()->RegisterTexture(
        std::make_unique<EmbedderExternalTextureGL>(texture,
                                                    external_texture_callback_));
  #endif
  
    return true;
  }
  
  bool EmbedderEngine::UnregisterTexture(int64_t texture) {
  #ifdef SHELL_ENABLE_GL
    if (!IsValid() || !external_texture_callback_) {
      return false;
    }
    shell_->GetPlatformView()->UnregisterTexture(texture);
  #endif
  
    return true;
  }
  
  bool EmbedderEngine::MarkTextureFrameAvailable(int64_t texture) {
  #ifdef SHELL_ENABLE_GL
    if (!IsValid() || !external_texture_callback_) {
      return false;
    }
    shell_->GetPlatformView()->MarkTextureFrameAvailable(texture);
  #endif
  
    return true;
  }
  
  bool EmbedderEngine::SetSemanticsEnabled(bool enabled) {
    if (!IsValid()) {
      return false;
    }
  
    auto platform_view = shell_->GetPlatformView();
    if (!platform_view) {
      return false;
    }
    platform_view->SetSemanticsEnabled(enabled);
    return true;
  }
  
  bool EmbedderEngine::SetAccessibilityFeatures(int32_t flags) {
    if (!IsValid()) {
      return false;
    }
    auto platform_view = shell_->GetPlatformView();
    if (!platform_view) {
      return false;
    }
    platform_view->SetAccessibilityFeatures(flags);
    return true;
  }
  
  bool EmbedderEngine::DispatchSemanticsAction(int id,
                                               flutter::SemanticsAction action,
                                               std::vector<uint8_t> args) {
    if (!IsValid()) {
      return false;
    }
    auto platform_view = shell_->GetPlatformView();
    if (!platform_view) {
      return false;
    }
    platform_view->DispatchSemanticsAction(id, action, std::move(args));
    return true;
  }
  
  bool EmbedderEngine::OnVsyncEvent(intptr_t baton,
                                    fml::TimePoint frame_start_time,
                                    fml::TimePoint frame_target_time) {
    if (!IsValid()) {
      return false;
    }
  
    return VsyncWaiterEmbedder::OnEmbedderVsync(baton, frame_start_time,
                                                frame_target_time);
  }
  
  bool EmbedderEngine::ReloadSystemFonts() {
    if (!IsValid()) {
      return false;
    }
  
    return shell_->ReloadSystemFonts();
  }
  
  bool EmbedderEngine::PostRenderThreadTask(const fml::closure& task) {
    if (!IsValid()) {
      return false;
    }
  
    shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(task);
    return true;
  }
  
  bool EmbedderEngine::RunTask(const FlutterTask* task) {
    // The shell doesn't need to be running or valid for access to the thread
    // host. This is why there is no `IsValid` check here. This allows embedders
    // to perform custom task runner interop before the shell is running.
    if (task == nullptr) {
      return false;
    }
    return thread_host_->PostTask(reinterpret_cast<int64_t>(task->runner),
                                  task->task);
  }
  
  bool EmbedderEngine::PostTaskOnEngineManagedNativeThreads(
      std::function<void(FlutterNativeThreadType)> closure) const {
    if (!IsValid() || closure == nullptr) {
      return false;
    }
  
    const auto trampoline = [closure](FlutterNativeThreadType type,
                                      fml::RefPtr<fml::TaskRunner> runner) {
      runner->PostTask([closure, type] { closure(type); });
    };
  
    // Post the task to all thread host threads.
    const auto& task_runners = shell_->GetTaskRunners();
    trampoline(kFlutterNativeThreadTypeRender,
               task_runners.GetRasterTaskRunner());
    trampoline(kFlutterNativeThreadTypeWorker, task_runners.GetIOTaskRunner());
    trampoline(kFlutterNativeThreadTypeUI, task_runners.GetUITaskRunner());
    trampoline(kFlutterNativeThreadTypePlatform,
               task_runners.GetPlatformTaskRunner());
  
    // Post the task to all worker threads.
    auto vm = shell_->GetDartVM();
    vm->GetConcurrentMessageLoop()->PostTaskToAllWorkers(
        [closure]() { closure(kFlutterNativeThreadTypeWorker); });
  
    return true;
  }
  
  Shell& EmbedderEngine::GetShell() {
    FML_DCHECK(shell_);
    return *shell_.get();
  }
  
  }  // namespace flutter
  