From bd822e1e139c4ff9cc51d4d99860938237587504 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 4 Dec 2023 18:33:16 +0200 Subject: Add support wl_output version 4 and agl_shell v8 This adds support for wl_output version 4 that allows to match an output name to a string that can be passed using gRPC APIs. With this it allows events being sent out by the compositor which the shell client can react upon. This can be seen as an intermediary step until flutter-homescreen from ICS gains (direct) support for gRPC to handle activation that way. Bug-AGL: SPEC-5004 Change-Id: I143c9dbbc044720c3dee642177d3ae175bfa9a75 Signed-off-by: Marius Vlad --- ...splay-Add-support-for-wl_output-version-4.patch | 117 +++++ ...splay-Add-support-for-agl_shell-version-8.patch | 477 +++++++++++++++++++++ .../toyota/flutter-auto_aglflutter.inc | 2 + 3 files changed, 596 insertions(+) create mode 100644 meta-agl-flutter/recipes-graphics/toyota/files/0001-display-Add-support-for-wl_output-version-4.patch create mode 100644 meta-agl-flutter/recipes-graphics/toyota/files/0002-display-Add-support-for-agl_shell-version-8.patch diff --git a/meta-agl-flutter/recipes-graphics/toyota/files/0001-display-Add-support-for-wl_output-version-4.patch b/meta-agl-flutter/recipes-graphics/toyota/files/0001-display-Add-support-for-wl_output-version-4.patch new file mode 100644 index 00000000..6b1b3e4c --- /dev/null +++ b/meta-agl-flutter/recipes-graphics/toyota/files/0001-display-Add-support-for-wl_output-version-4.patch @@ -0,0 +1,117 @@ +From 10d1d855a0ce4557cb710e73e3e7c9ab0dd0e734 Mon Sep 17 00:00:00 2001 +From: Marius Vlad +Date: Mon, 4 Dec 2023 14:16:36 +0200 +Subject: [PATCH 1/2] display: Add support for wl_output version 4 + +This allows support for wl_output.name and wl_output.desc be sent out +by the compositor that supports it. + +Signed-off-by: Marius Vlad +--- + shell/wayland/display.cc | 34 ++++++++++++++++++++++++++++++---- + shell/wayland/display.h | 28 ++++++++++++++++++++++++++++ + 2 files changed, 58 insertions(+), 4 deletions(-) + +diff --git a/shell/wayland/display.cc b/shell/wayland/display.cc +index 8e309ef..3ee814a 100644 +--- a/shell/wayland/display.cc ++++ b/shell/wayland/display.cc +@@ -191,9 +191,16 @@ void Display::registry_handle_global(void* data, + auto oi = std::make_shared(); + std::fill_n(oi.get(), 1, output_info_t{}); + oi->global_id = name; +- oi->output = static_cast( +- wl_registry_bind(registry, name, &wl_output_interface, +- std::min(static_cast(2), version))); ++ // be compat with v2 as well ++ if (version >= WL_OUTPUT_NAME_SINCE_VERSION && ++ version >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) ++ oi->output = static_cast( ++ wl_registry_bind(registry, name, &wl_output_interface, ++ std::min(static_cast(4), version))); ++ else ++ oi->output = static_cast( ++ wl_registry_bind(registry, name, &wl_output_interface, ++ std::min(static_cast(2), version))); + wl_output_add_listener(oi->output, &output_listener, oi.get()); + SPDLOG_DEBUG("Wayland: Output [{}]", d->m_all_outputs.size()); + d->m_all_outputs.push_back(oi); +@@ -299,9 +306,28 @@ void Display::display_handle_done(void* data, + oi->done = true; + } + ++void Display::display_handle_name(void* data, ++ struct wl_output* /* wl_output */, ++ const char* name) { ++ auto* oi = static_cast(data); ++ oi->name = std::string(name); ++} ++ ++void Display::display_handle_desc(void* data, ++ struct wl_output* /* wl_output */, ++ const char* desc) { ++ auto* oi = static_cast(data); ++ oi->desc = std::string(desc); ++} ++ + const struct wl_output_listener Display::output_listener = { + display_handle_geometry, display_handle_mode, display_handle_done, +- display_handle_scale}; ++ display_handle_scale ++#if defined(WL_OUTPUT_NAME_SINCE_VERSION) && \ ++ defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) ++ , display_handle_name, display_handle_desc ++#endif ++}; + + void Display::shm_format(void* /* data */, + struct wl_shm* /* wl_shm */, +diff --git a/shell/wayland/display.h b/shell/wayland/display.h +index cc3f4be..a0756f0 100644 +--- a/shell/wayland/display.h ++++ b/shell/wayland/display.h +@@ -329,6 +329,8 @@ class Display { + int32_t scale; + MAYBE_UNUSED bool done; + int transform; ++ std::string name; ++ std::string desc; + } output_info_t; + + struct pointer_event { +@@ -520,6 +522,32 @@ class Display { + */ + static void display_handle_done(void* data, struct wl_output* wl_output); + ++ /** ++ * @brief Set the display output name ++ * @param[in,out] data Data of type output_info_t* ++ * @param[in] wl_output No use ++ * @param[in] output_name Display name ++ * @return void ++ * @relation ++ * wayland - since @v4 of wl_output ++ */ ++ static void display_handle_name(void* data, ++ struct wl_output* wl_output, ++ const char* output_name); ++ ++ /** ++ * @brief Set the display description ++ * @param[in,out] data Data of type output_info_t* ++ * @param[in] wl_output No use ++ * @param[in] desc_name Display description name ++ * @return void ++ * @relation ++ * wayland - since @v4 of wl_output ++ */ ++ static void display_handle_desc(void* data, ++ struct wl_output* wl_output, ++ const char* desc_name); ++ + static const struct wl_shm_listener shm_listener; + + /** +-- +2.35.1 + diff --git a/meta-agl-flutter/recipes-graphics/toyota/files/0002-display-Add-support-for-agl_shell-version-8.patch b/meta-agl-flutter/recipes-graphics/toyota/files/0002-display-Add-support-for-agl_shell-version-8.patch new file mode 100644 index 00000000..79281176 --- /dev/null +++ b/meta-agl-flutter/recipes-graphics/toyota/files/0002-display-Add-support-for-agl_shell-version-8.patch @@ -0,0 +1,477 @@ +From d44f07a0c2cc410414bfd7b338ee071c17422a0a Mon Sep 17 00:00:00 2001 +From: Marius Vlad +Date: Mon, 4 Dec 2023 18:17:00 +0200 +Subject: [PATCH 2/2] display: Add support for agl_shell version 8 + +Signed-off-by: Marius Vlad +--- + shell/wayland/display.cc | 156 ++++++++++++++++++++++-- + shell/wayland/display.h | 58 +++++++++ + third_party/agl/protocol/agl-shell.xml | 160 ++++++++++++++++++++++++- + 3 files changed, 366 insertions(+), 8 deletions(-) + +diff --git a/shell/wayland/display.cc b/shell/wayland/display.cc +index 3ee814a..aba050a 100644 +--- a/shell/wayland/display.cc ++++ b/shell/wayland/display.cc +@@ -220,7 +220,7 @@ void Display::registry_handle_global(void* data, + if (version >= 2) { + d->m_agl.shell = static_cast( + wl_registry_bind(registry, name, &agl_shell_interface, +- std::min(static_cast(4), version))); ++ std::min(static_cast(8), version))); + agl_shell_add_listener(d->m_agl.shell, &agl_shell_listener, data); + } else { + d->m_agl.shell = static_cast( +@@ -980,6 +980,148 @@ void Display::agl_shell_bound_fail(void* data, struct agl_shell* shell) { + d->m_agl.bound_ok = false; + } + ++void Display::addAppToStack(std::string app_id) { ++ if (app_id == "homescreen") ++ return; ++ ++ bool found_app = false; ++ for (auto& i : apps_stack) { ++ if (i == app_id) { ++ found_app = true; ++ break; ++ } ++ } ++ ++ if (!found_app) { ++ apps_stack.push_back(app_id); ++ } else { ++ // fixme ++ } ++} ++ ++int Display::find_output_by_name(std::string output_name) { ++ int index = 0; ++ for (auto& i : m_all_outputs) { ++ if (i->name == output_name) { ++ return index; ++ } ++ index++; ++ } ++ ++ return -1; ++} ++ ++void Display::activateApp(std::string app_id) { ++ int default_output_index = 0; ++ ++ FML_LOG(INFO) << "got app_id " << app_id; ++ ++ // search for a pending application which might have a different output ++ auto iter = pending_app_list.begin(); ++ bool found_pending_app = false; ++ while (iter != pending_app_list.end()) { ++ auto app_to_search = iter->first; ++ FML_LOG(INFO) << "searching for " << app_to_search; ++ ++ if (app_to_search == app_id) { ++ found_pending_app = true; ++ break; ++ } ++ ++ iter++; ++ } ++ ++ if (found_pending_app) { ++ auto output_name = iter->second; ++ default_output_index = find_output_by_name(output_name); ++ ++ FML_LOG(INFO) << "Found app_id " << app_id << " at all"; ++ ++ if (default_output_index < 0) { ++ // try with remoting-remote-X which is the streaming ++ std::string new_remote_output = "remoting-" + output_name; ++ ++ default_output_index = find_output_by_name(new_remote_output); ++ if (default_output_index < 0) { ++ FML_LOG(INFO) << "Not activating app_id " << app_id << " at all"; ++ return; ++ } ++ } ++ ++ pending_app_list.erase(iter); ++ } ++ ++ FML_LOG(INFO) << "Activating app_id " << app_id << " on output " ++ << default_output_index; ++ agl_shell_activate_app(m_agl.shell, app_id.c_str(), ++ m_all_outputs[default_output_index]->output); ++ wl_display_flush(m_display); ++} ++ ++void Display::deactivateApp(std::string app_id) { ++ for (auto& i : apps_stack) { ++ if (i == app_id) { ++ // remove it from apps_stack ++ apps_stack.remove(i); ++ if (!apps_stack.empty()) ++ activateApp(apps_stack.back()); ++ break; ++ } ++ } ++} ++ ++void Display::processAppStatusEvent(const char* app_id, ++ const std::string event_type) { ++ if (!m_agl.shell) ++ return; ++ ++ if (event_type == "started") { ++ activateApp(std::string(app_id)); ++ } else if (event_type == "terminated") { ++ deactivateApp(std::string(app_id)); ++ } else if (event_type == "deactivated") { ++ // not handled ++ } ++} ++ ++void Display::agl_shell_app_on_output(void* data, ++ struct agl_shell* agl_shell, ++ const char* app_id, ++ const char* output_name) { ++ auto* d = static_cast(data); ++ ++ FML_LOG(INFO) << "Gove event app_on_out app_id " << app_id << " output name " ++ << output_name; ++ ++ // a couple of use-cases, if there is no app_id in the app_list then it ++ // means this is a request to map the application, from the start to a ++ // different output that the default one. We'd get an ++ // AGL_SHELL_APP_STATE_STARTED which will handle activation. ++ // ++ // if there's an app_id then it means we might have gotten an event to ++ // move the application to another output; so we'd need to process it ++ // by explicitly calling processAppStatusEvent() which would ultimately ++ // activate the application on other output. We'd have to pick-up the ++ // last activated window and activate the default output. ++ // ++ // finally if the outputs are identical probably that's an user-error - ++ // but the compositor won't activate it again, so we don't handle that. ++ std::pair new_pending_app = ++ std::pair(std::string(app_id), std::string(output_name)); ++ d->pending_app_list.push_back(new_pending_app); ++ ++ auto iter = d->apps_stack.begin(); ++ while (iter != d->apps_stack.end()) { ++ if (*iter == std::string(app_id)) { ++ FML_LOG(INFO) << "Gove event to move " << app_id << " to another output " ++ << output_name; ++ d->processAppStatusEvent(app_id, std::string("started")); ++ break; ++ } ++ iter++; ++ } ++} ++ + void Display::agl_shell_app_state(void* data, + struct agl_shell* /* agl_shell */, + const char* app_id, +@@ -991,12 +1133,7 @@ void Display::agl_shell_app_state(void* data, + FML_DLOG(INFO) << "Got AGL_SHELL_APP_STATE_STARTED for app_id " << app_id; + + if (d->m_agl.shell) { +- // we always assume the first output advertised by the wl_output +- // interface +- unsigned int default_output_index = 0; +- +- agl_shell_activate_app(d->m_agl.shell, app_id, +- d->m_all_outputs[default_output_index]->output); ++ d->processAppStatusEvent(app_id, std::string("started")); + } + + break; +@@ -1007,6 +1144,10 @@ void Display::agl_shell_app_state(void* data, + case AGL_SHELL_APP_STATE_ACTIVATED: + FML_DLOG(INFO) << "Got AGL_SHELL_APP_STATE_ACTIVATED for app_id " + << app_id; ++ d->addAppToStack(std::string(app_id)); ++ break; ++ case AGL_SHELL_APP_STATE_DEACTIVATED: ++ d->processAppStatusEvent(app_id, std::string("deactivated")); + break; + default: + break; +@@ -1017,6 +1158,7 @@ const struct agl_shell_listener Display::agl_shell_listener = { + .bound_ok = agl_shell_bound_ok, + .bound_fail = agl_shell_bound_fail, + .app_state = agl_shell_app_state, ++ .app_on_output = agl_shell_app_on_output, + }; + + void Display::ivi_wm_surface_visibility(void* /* data */, +diff --git a/shell/wayland/display.h b/shell/wayland/display.h +index a0756f0..b919047 100644 +--- a/shell/wayland/display.h ++++ b/shell/wayland/display.h +@@ -18,6 +18,7 @@ + #pragma once + + #include ++#include + #include + #include + #include +@@ -271,6 +272,44 @@ class Display { + */ + std::pair GetVideoModeSize(uint32_t index); + ++ /** ++ * @brief deactivate/hide the application pointed by app_id ++ * @param[in] app_id the app_id ++ * @relation ++ * agl_shell ++ */ ++ void deactivateApp(std::string app_id); ++ /** ++ * @brief activate/show the application pointed by app_id ++ * @param[in] app_id the app_id ++ * @relation ++ * agl_shell ++ */ ++ void activateApp(std::string app_id); ++ /** ++ * @brief Add app_id to a list of list applications ++ * @param[in] app_id the app_id ++ * @relation ++ * agl_shell ++ */ ++ void addAppToStack(std::string app_id); ++ /** ++ * @brief Helper to retrieve the output using its output_name ++ * @param[in] output_name a std::string representing the output ++ * @retval an integer that can used to get the proper output ++ * @relation ++ * agl_sell ++ */ ++ int find_output_by_name(std::string output_name); ++ /** ++ * @brief helper to process the application status ++ * @param[in] app_id an array of char ++ * @param[in] event_type a std::string representing the type of event (started/stopped/terminated) ++ * @relation ++ * agl_shell ++ */ ++ void processAppStatusEvent(const char* app_id, const std::string event_type); ++ + private: + std::shared_ptr m_flutter_engine; + +@@ -300,6 +339,9 @@ class Display { + uint32_t version = 0; + } m_agl; + ++ std::list apps_stack; ++ std::list> pending_app_list; ++ + struct ivi_shell { + struct ivi_application* application = nullptr; + struct ivi_wm* ivi_wm = nullptr; +@@ -982,6 +1024,22 @@ class Display { + const char* app_id, + uint32_t state); + ++ /** ++ * @brief AGL app_app_on_output event ++ * @param[in,out] data Data of type Display ++ * @param[in] shell No use ++ * @param[in] app_id the application id for which this event was sent ++ * @param[in] state the state: CREATED/TERMINATED/ACTIVATED/DEACTIVATED ++ * @return void ++ * @relation ++ * wayland, agl-shell ++ * @note Do nothing ++ */ ++ static void agl_shell_app_on_output(void* data, ++ struct agl_shell* agl_shell, ++ const char* app_id, ++ const char* output_name); ++ + static const struct agl_shell_listener agl_shell_listener; + + /** +diff --git a/third_party/agl/protocol/agl-shell.xml b/third_party/agl/protocol/agl-shell.xml +index bf5ab02..e010a80 100644 +--- a/third_party/agl/protocol/agl-shell.xml ++++ b/third_party/agl/protocol/agl-shell.xml +@@ -22,7 +22,7 @@ + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +- ++ + + Starting with version 2 of the protocol, the client is required to wait + for the 'bound_ok' or 'bound_fail' events in order to proceed further. +@@ -200,5 +200,163 @@ + + + ++ ++ ++ ++ Ask the compositor to hide the toplevel window for window ++ management purposes. Depending on the window role, this request ++ will either display the previously active window (or the background ++ in case there's no previously active surface) or temporarily (or ++ until a 'activate_app' is called upon) hide the surface. ++ ++ All the surfaces are identifiable by using the app_id, and no actions ++ are taken in case the app_id is not/was not present. ++ ++ See xdg_toplevel.set_app_id from the xdg-shell protocol for a ++ description of app_id. ++ ++ ++ ++ ++ ++ ++ Makes the application identified by app_id as floating. If the ++ application's window is already mapped, in a maximized, normal state, ++ it would transition to the float state. ++ ++ For applications that want to modify their own state, this request ++ must be done before the initial surface commit in order to take effect. ++ ++ If the application is already in floating state, this request wouldn't ++ do anything. ++ ++ There's no persistence of this request, once the application terminated ++ you'll to issue this request again for that particular app_id. ++ ++ The x, and y values would be initial position of the window where the ++ window surface will be placed. ++ ++ See xdg_toplevel.set_app_id from the xdg-shell protocol for a ++ description of app_id. ++ ++ ++ ++ ++ ++ ++ ++ ++ Returns the application identified by app_id as it was in the normal state. ++ This is useful to come back from other states to the maximized state, the ++ normal state applications are started. ++ ++ ++ ++ ++ ++ ++ Makes the application identified by app_id as fullscreen. If the ++ application's window is already mapped, in a maximized, normal state, ++ it would transition to the fullscreen state. ++ ++ For applications that want to modify their own state, this request ++ must be done before the initial surface commit in order to take effect. ++ ++ If the application is already in fullscreen state, this request wouldn't ++ do anything. ++ ++ There's no persistence of this request, once the application terminated ++ you'll to issue this request again for that particular app_id. ++ ++ See xdg_toplevel.set_app_id from the xdg-shell protocol for a ++ description of app_id. ++ ++ ++ ++ ++ ++ ++ This would allow the compositor to place an application on a particular ++ output, if that output is indeed available. This can happen before ++ application is started which would make the application start on that ++ particular output. If the application is already started it would ++ move the application to that output. ++ ++ There's no persistence of this request, once the application terminated ++ you'll need to issue this request again for that particular app_id. ++ ++ See xdg_toplevel.set_app_id from the xdg-shell protocol for a ++ description of app_id. ++ ++ ++ ++ ++ ++ ++ ++ Clients can use this event to be notified when an application ++ wants to be displayed on a certain output. This event is sent in ++ response to the set_app_output request. ++ ++ See xdg_toplevel.set_app_id from the xdg-shell protocol for a ++ description of app_id. ++ ++ ++ ++ ++ ++ ++ ++ ++ This interface allows another client bind to the agl_shell interface, ++ while there's another shell client already present. ++ ++ The client should first bind to this interface and then inform the ++ compositor with the 'doas_shell_client' request and it wants to bind to ++ the agl_shell interface. The client is still expected, if using a new ++ version of the agl_shell interface, to wait for the 'bound_ok' and ++ 'bound_fail' events before issueing any other requests/events. ++ ++ Note that this interface has its limitations, and the compositor would ++ still refuse the act for 'set_panel' or 'set_background' requests ++ of the agl_shell interface if there's already a client that used them. ++ ++ Any other requests or events should be delievered and handled as it would ++ a client bound to the agl_shell interface. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Call the destructor once you're ready with agl_shell_ext interface. ++ This would reset the state and would make any requests made ++ on the agl_shell interface be terminated. The client would need ++ to bind again the agl_shell_ext and issue a 'doas_shell_client' ++ request. ++ ++ ++ ++ ++ ++ Prior to binding to agl_shell interface, this request would inform ++ the compositor that it wants to gain access the agl_shell interface. ++ The client is expected to wait for 'doas_shell_client_done' event and ++ check for a successful status before going further with binding to ++ the agl_shell interface. ++ ++ ++ ++ ++ ++ The client should check the status event to verify that the ++ compositor was able to handle the request. ++ ++ ++ + + +-- +2.35.1 + diff --git a/meta-agl-flutter/recipes-graphics/toyota/flutter-auto_aglflutter.inc b/meta-agl-flutter/recipes-graphics/toyota/flutter-auto_aglflutter.inc index 7ba85a34..e074b522 100644 --- a/meta-agl-flutter/recipes-graphics/toyota/flutter-auto_aglflutter.inc +++ b/meta-agl-flutter/recipes-graphics/toyota/flutter-auto_aglflutter.inc @@ -1,3 +1,5 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI:append:use-nxp-bsp = " file://0001-Disable-on_frame_base_surface-wl_surface_commit.patch" +SRC_URI:append: = " file://0001-display-Add-support-for-wl_output-version-4.patch \ + file://0002-display-Add-support-for-agl_shell-version-8.patch" -- cgit 1.2.3-korg