From 413898c43c8f66a7d49fe8bb74a85ce67733bc86 Mon Sep 17 00:00:00 2001
From: Scott Murray <scott.murray@konsulko.com>
Date: Thu, 1 Jun 2023 14:34:03 -0400
Subject: [PATCH] Add optional agl_shell plugin

Add an optional agl_shell static plugin to expose the activate_app
call from the agl-shell Wayland protocol that is used when running
against the AGL compositor.  This provides a way for a homescreen
application to activate other application surfaces to switch between
multiple applications as is needed for the AGL demo.

Upstream-Status: Pending

Signed-off-by: Scott Murray <scott.murray@konsulko.com>
[Updated to work with upstream OSS 0223 release]
Signed-off-by: Marius Vlad <marius.vlad@collabora.com>

---
 cmake/plugins.cmake                         |  5 ++
 shell/engine.cc                             |  6 ++
 shell/engine.h                              | 10 +++
 shell/platform_channel.cc                   |  6 ++
 shell/static_plugins/agl_shell/agl_shell.cc | 77 +++++++++++++++++++++
 shell/static_plugins/agl_shell/agl_shell.h  | 31 +++++++++
 shell/wayland/display.cc                    |  7 ++
 shell/wayland/display.h                     |  8 +++
 shell/wayland/window.h                      |  2 +
 9 files changed, 152 insertions(+)
 create mode 100644 shell/static_plugins/agl_shell/agl_shell.cc
 create mode 100644 shell/static_plugins/agl_shell/agl_shell.h

diff --git a/cmake/plugins.cmake b/cmake/plugins.cmake
index b720dca..20a55d7 100644
--- a/cmake/plugins.cmake
+++ b/cmake/plugins.cmake
@@ -123,4 +123,9 @@ if (BUILD_PLUGIN_SECURE_STORAGE)
 endif ()
 
 
+option(BUILD_PLUGIN_AGL_SHELL "Includes AGL Shell Wayland Protocol Plugin" OFF)
+if (BUILD_PLUGIN_AGL_SHELL)
+    ENABLE_PLUGIN(agl_shell)
+endif ()
+
 message(STATUS "Plugin Config .......... ${PLUGINS}")
diff --git a/shell/engine.cc b/shell/engine.cc
index d20b6ba..a0e38e9 100644
--- a/shell/engine.cc
+++ b/shell/engine.cc
@@ -614,3 +614,9 @@ MAYBE_UNUSED TextInput* Engine::GetTextInput() const {
   return m_key_event;
 }
 #endif
+
+#if ENABLE_PLUGIN_AGL_SHELL
+std::shared_ptr<Display> Engine::GetDisplay() const {
+  return m_egl_window->GetDisplay();
+}
+#endif
diff --git a/shell/engine.h b/shell/engine.h
index 94808dc..3a06f51 100644
--- a/shell/engine.h
+++ b/shell/engine.h
@@ -60,6 +60,12 @@ class KeyEvent;
 
 #endif
 
+#if ENABLE_PLUGIN_AGL_SHELL
+
+class Display;
+
+#endif
+
 class Engine {
  public:
   /**
@@ -445,6 +451,10 @@ class Engine {
   MAYBE_UNUSED NODISCARD KeyEvent* GetKeyEvent() const;
 #endif
 
+#if ENABLE_PLUGIN_AGL_SHELL
+  std::shared_ptr<Display> GetDisplay() const;
+#endif
+
   /**
    * @brief Get backend of view
    * @return Backend*
diff --git a/shell/platform_channel.cc b/shell/platform_channel.cc
index b72156a..81d7cb5 100644
--- a/shell/platform_channel.cc
+++ b/shell/platform_channel.cc
@@ -62,6 +62,9 @@
 #ifdef ENABLE_PLUGIN_SECURE_STORAGE
 #include "static_plugins/secure_storage/secure_storage.h"
 #endif
+#ifdef ENABLE_PLUGIN_AGL_SHELL
+#include "static_plugins/agl_shell/agl_shell.h"
+#endif
 
 PlatformChannel* PlatformChannel::singleton = nullptr;
 
@@ -122,4 +125,7 @@ PlatformChannel::PlatformChannel() {
   RegisterCallback(SecureStorage::kChannelName,
                    &SecureStorage::OnPlatformMessage);
 #endif
+#ifdef ENABLE_PLUGIN_AGL_SHELL
+  RegisterCallback(AglShell::kChannelName, &AglShell::OnPlatformMessage);
+#endif
 }
diff --git a/shell/static_plugins/agl_shell/agl_shell.cc b/shell/static_plugins/agl_shell/agl_shell.cc
new file mode 100644
index 0000000..e6160a5
--- /dev/null
+++ b/shell/static_plugins/agl_shell/agl_shell.cc
@@ -0,0 +1,77 @@
+// Copyright 2020 Toyota Connected North America
+// Copyright 2022 Konsulko Group
+//
+// 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 "agl_shell.h"
+
+#include <flutter/fml/logging.h>
+#include <flutter/standard_method_codec.h>
+
+#include "engine.h"
+#include "wayland/display.h"
+
+#include <iostream>
+
+void AglShell::OnPlatformMessage(const FlutterPlatformMessage* message,
+                                 void* userdata) {
+  std::unique_ptr<std::vector<uint8_t>> result;
+  auto engine = reinterpret_cast<Engine*>(userdata);
+  auto& codec = flutter::StandardMethodCodec::GetInstance();
+  auto obj = codec.DecodeMethodCall(message->message, message->message_size);
+
+  auto method = obj->method_name();
+  if (method == kMethodActivateApp) {
+    if (obj->arguments()->IsNull()) {
+      result = codec.EncodeErrorEnvelope("argument_error", "Invalid Arguments");
+      goto done;
+    }
+
+    auto args = std::get_if<flutter::EncodableMap>(obj->arguments());
+    if (!args) {
+      result = codec.EncodeErrorEnvelope("argument_error", "Invalid Arguments");
+      goto done;
+    }
+
+    std::string app_id;
+    auto it = args->find(flutter::EncodableValue("app_id"));
+    if (it != args->end()) {
+      app_id = std::get<std::string>(it->second);
+    }
+
+    int32_t index = 0;
+    it = args->find(flutter::EncodableValue("index"));
+    if (it != args->end()) {
+      index = std::get<int32_t>(it->second);
+    }
+
+    if (app_id.empty() || index < 0) {
+      result = codec.EncodeErrorEnvelope("argument_error", "Invalid Arguments");
+      goto done;
+    }
+
+    auto display = engine->GetDisplay();
+    if (display) {
+      display->AglShellDoActivate(app_id, index);
+    }
+    auto val = flutter::EncodableValue(true);
+    result = codec.EncodeSuccessEnvelope(&val);
+  } else {
+    FML_DLOG(INFO) << "AglShell: " << method << " is unhandled";
+    result = codec.EncodeErrorEnvelope("unhandled_method", "Unhandled Method");
+  }
+
+done:
+  engine->SendPlatformMessageResponse(message->response_handle, result->data(),
+                                      result->size());
+}
diff --git a/shell/static_plugins/agl_shell/agl_shell.h b/shell/static_plugins/agl_shell/agl_shell.h
new file mode 100644
index 0000000..698e44c
--- /dev/null
+++ b/shell/static_plugins/agl_shell/agl_shell.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 Toyota Connected North America
+ * Copyright 2022 Konsulko Group
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <shell/platform/embedder/embedder.h>
+
+class AglShell {
+ public:
+  static constexpr char kChannelName[] = "flutter/agl_shell";
+
+  static void OnPlatformMessage(const FlutterPlatformMessage* message,
+                                void* userdata);
+
+ private:
+  static constexpr char kMethodActivateApp[] = "activate_app";
+};
diff --git a/shell/wayland/display.cc b/shell/wayland/display.cc
index 434dd05..99aa5ff 100644
--- a/shell/wayland/display.cc
+++ b/shell/wayland/display.cc
@@ -869,6 +869,13 @@ void Display::AglShellDoSetupActivationArea(uint32_t x,
                                 width, height);
 }
 
+void Display::AglShellDoActivate(const std::string& app_id, size_t index) {
+  if (m_agl.shell) {
+    agl_shell_activate_app(m_agl.shell, app_id.c_str(),
+                           m_all_outputs[index]->output);
+  }
+}
+
 void Display::SetEngine(wl_surface* surface, Engine* engine) {
   m_active_engine = engine;
   m_active_surface = surface;
diff --git a/shell/wayland/display.h b/shell/wayland/display.h
index 06b0f8b..ef6ea3f 100644
--- a/shell/wayland/display.h
+++ b/shell/wayland/display.h
@@ -150,6 +150,14 @@ class Display {
    */
   void AglShellDoReady() const;
 
+  /**
+   * @brief AglShellDoActivate:
+   * @return void
+   * @relation
+   * wayland, agl-shell
+   */
+  void AglShellDoActivate(const std::string& app_id, size_t index);
+
   /**
    * @brief AglShell: Set up an activation area where to display the client's
    * window
diff --git a/shell/wayland/window.h b/shell/wayland/window.h
index d5e3c77..677dbdc 100644
--- a/shell/wayland/window.h
+++ b/shell/wayland/window.h
@@ -129,6 +129,8 @@ class WaylandWindow {
     return std::pair<int32_t, int32_t>{m_geometry.width, m_geometry.height};
   }
 
+  std::shared_ptr<Display> GetDisplay() { return m_display; }
+
  private:
   size_t m_index;
   std::shared_ptr<Display> m_display;
