1 From d44f07a0c2cc410414bfd7b338ee071c17422a0a Mon Sep 17 00:00:00 2001
2 From: Marius Vlad <marius.vlad@collabora.com>
3 Date: Mon, 4 Dec 2023 18:17:00 +0200
4 Subject: [PATCH 2/2] display: Add support for agl_shell version 8
6 Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
8 shell/wayland/display.cc | 156 ++++++++++++++++++++++--
9 shell/wayland/display.h | 58 +++++++++
10 third_party/agl/protocol/agl-shell.xml | 160 ++++++++++++++++++++++++-
11 3 files changed, 366 insertions(+), 8 deletions(-)
13 diff --git a/shell/wayland/display.cc b/shell/wayland/display.cc
14 index 3ee814a..aba050a 100644
15 --- a/shell/wayland/display.cc
16 +++ b/shell/wayland/display.cc
17 @@ -220,7 +220,7 @@ void Display::registry_handle_global(void* data,
19 d->m_agl.shell = static_cast<struct agl_shell*>(
20 wl_registry_bind(registry, name, &agl_shell_interface,
21 - std::min(static_cast<uint32_t>(4), version)));
22 + std::min(static_cast<uint32_t>(8), version)));
23 agl_shell_add_listener(d->m_agl.shell, &agl_shell_listener, data);
25 d->m_agl.shell = static_cast<struct agl_shell*>(
26 @@ -980,6 +980,148 @@ void Display::agl_shell_bound_fail(void* data, struct agl_shell* shell) {
27 d->m_agl.bound_ok = false;
30 +void Display::addAppToStack(std::string app_id) {
31 + if (app_id == "homescreen")
34 + bool found_app = false;
35 + for (auto& i : apps_stack) {
43 + apps_stack.push_back(app_id);
49 +int Display::find_output_by_name(std::string output_name) {
51 + for (auto& i : m_all_outputs) {
52 + if (i->name == output_name) {
61 +void Display::activateApp(std::string app_id) {
62 + int default_output_index = 0;
64 + FML_LOG(INFO) << "got app_id " << app_id;
66 + // search for a pending application which might have a different output
67 + auto iter = pending_app_list.begin();
68 + bool found_pending_app = false;
69 + while (iter != pending_app_list.end()) {
70 + auto app_to_search = iter->first;
71 + FML_LOG(INFO) << "searching for " << app_to_search;
73 + if (app_to_search == app_id) {
74 + found_pending_app = true;
81 + if (found_pending_app) {
82 + auto output_name = iter->second;
83 + default_output_index = find_output_by_name(output_name);
85 + FML_LOG(INFO) << "Found app_id " << app_id << " at all";
87 + if (default_output_index < 0) {
88 + // try with remoting-remote-X which is the streaming
89 + std::string new_remote_output = "remoting-" + output_name;
91 + default_output_index = find_output_by_name(new_remote_output);
92 + if (default_output_index < 0) {
93 + FML_LOG(INFO) << "Not activating app_id " << app_id << " at all";
98 + pending_app_list.erase(iter);
101 + FML_LOG(INFO) << "Activating app_id " << app_id << " on output "
102 + << default_output_index;
103 + agl_shell_activate_app(m_agl.shell, app_id.c_str(),
104 + m_all_outputs[default_output_index]->output);
105 + wl_display_flush(m_display);
108 +void Display::deactivateApp(std::string app_id) {
109 + for (auto& i : apps_stack) {
111 + // remove it from apps_stack
112 + apps_stack.remove(i);
113 + if (!apps_stack.empty())
114 + activateApp(apps_stack.back());
120 +void Display::processAppStatusEvent(const char* app_id,
121 + const std::string event_type) {
125 + if (event_type == "started") {
126 + activateApp(std::string(app_id));
127 + } else if (event_type == "terminated") {
128 + deactivateApp(std::string(app_id));
129 + } else if (event_type == "deactivated") {
134 +void Display::agl_shell_app_on_output(void* data,
135 + struct agl_shell* agl_shell,
136 + const char* app_id,
137 + const char* output_name) {
138 + auto* d = static_cast<Display*>(data);
140 + FML_LOG(INFO) << "Gove event app_on_out app_id " << app_id << " output name "
143 + // a couple of use-cases, if there is no app_id in the app_list then it
144 + // means this is a request to map the application, from the start to a
145 + // different output that the default one. We'd get an
146 + // AGL_SHELL_APP_STATE_STARTED which will handle activation.
148 + // if there's an app_id then it means we might have gotten an event to
149 + // move the application to another output; so we'd need to process it
150 + // by explicitly calling processAppStatusEvent() which would ultimately
151 + // activate the application on other output. We'd have to pick-up the
152 + // last activated window and activate the default output.
154 + // finally if the outputs are identical probably that's an user-error -
155 + // but the compositor won't activate it again, so we don't handle that.
156 + std::pair new_pending_app =
157 + std::pair(std::string(app_id), std::string(output_name));
158 + d->pending_app_list.push_back(new_pending_app);
160 + auto iter = d->apps_stack.begin();
161 + while (iter != d->apps_stack.end()) {
162 + if (*iter == std::string(app_id)) {
163 + FML_LOG(INFO) << "Gove event to move " << app_id << " to another output "
165 + d->processAppStatusEvent(app_id, std::string("started"));
172 void Display::agl_shell_app_state(void* data,
173 struct agl_shell* /* agl_shell */,
175 @@ -991,12 +1133,7 @@ void Display::agl_shell_app_state(void* data,
176 FML_DLOG(INFO) << "Got AGL_SHELL_APP_STATE_STARTED for app_id " << app_id;
178 if (d->m_agl.shell) {
179 - // we always assume the first output advertised by the wl_output
181 - unsigned int default_output_index = 0;
183 - agl_shell_activate_app(d->m_agl.shell, app_id,
184 - d->m_all_outputs[default_output_index]->output);
185 + d->processAppStatusEvent(app_id, std::string("started"));
189 @@ -1007,6 +1144,10 @@ void Display::agl_shell_app_state(void* data,
190 case AGL_SHELL_APP_STATE_ACTIVATED:
191 FML_DLOG(INFO) << "Got AGL_SHELL_APP_STATE_ACTIVATED for app_id "
193 + d->addAppToStack(std::string(app_id));
195 + case AGL_SHELL_APP_STATE_DEACTIVATED:
196 + d->processAppStatusEvent(app_id, std::string("deactivated"));
200 @@ -1017,6 +1158,7 @@ const struct agl_shell_listener Display::agl_shell_listener = {
201 .bound_ok = agl_shell_bound_ok,
202 .bound_fail = agl_shell_bound_fail,
203 .app_state = agl_shell_app_state,
204 + .app_on_output = agl_shell_app_on_output,
207 void Display::ivi_wm_surface_visibility(void* /* data */,
208 diff --git a/shell/wayland/display.h b/shell/wayland/display.h
209 index a0756f0..b919047 100644
210 --- a/shell/wayland/display.h
211 +++ b/shell/wayland/display.h
220 @@ -271,6 +272,44 @@ class Display {
222 std::pair<int32_t, int32_t> GetVideoModeSize(uint32_t index);
225 + * @brief deactivate/hide the application pointed by app_id
226 + * @param[in] app_id the app_id
230 + void deactivateApp(std::string app_id);
232 + * @brief activate/show the application pointed by app_id
233 + * @param[in] app_id the app_id
237 + void activateApp(std::string app_id);
239 + * @brief Add app_id to a list of list applications
240 + * @param[in] app_id the app_id
244 + void addAppToStack(std::string app_id);
246 + * @brief Helper to retrieve the output using its output_name
247 + * @param[in] output_name a std::string representing the output
248 + * @retval an integer that can used to get the proper output
252 + int find_output_by_name(std::string output_name);
254 + * @brief helper to process the application status
255 + * @param[in] app_id an array of char
256 + * @param[in] event_type a std::string representing the type of event (started/stopped/terminated)
260 + void processAppStatusEvent(const char* app_id, const std::string event_type);
263 std::shared_ptr<Engine> m_flutter_engine;
265 @@ -300,6 +339,9 @@ class Display {
266 uint32_t version = 0;
269 + std::list<std::string> apps_stack;
270 + std::list<std::pair<const std::string, const std::string>> pending_app_list;
273 struct ivi_application* application = nullptr;
274 struct ivi_wm* ivi_wm = nullptr;
275 @@ -982,6 +1024,22 @@ class Display {
280 + * @brief AGL app_app_on_output event
281 + * @param[in,out] data Data of type Display
282 + * @param[in] shell No use
283 + * @param[in] app_id the application id for which this event was sent
284 + * @param[in] state the state: CREATED/TERMINATED/ACTIVATED/DEACTIVATED
287 + * wayland, agl-shell
290 + static void agl_shell_app_on_output(void* data,
291 + struct agl_shell* agl_shell,
292 + const char* app_id,
293 + const char* output_name);
295 static const struct agl_shell_listener agl_shell_listener;
298 diff --git a/third_party/agl/protocol/agl-shell.xml b/third_party/agl/protocol/agl-shell.xml
299 index bf5ab02..e010a80 100644
300 --- a/third_party/agl/protocol/agl-shell.xml
301 +++ b/third_party/agl/protocol/agl-shell.xml
303 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
304 DEALINGS IN THE SOFTWARE.
306 - <interface name="agl_shell" version="4">
307 + <interface name="agl_shell" version="8">
308 <description summary="user interface for Automotive Grade Linux platform">
309 Starting with version 2 of the protocol, the client is required to wait
310 for the 'bound_ok' or 'bound_fail' events in order to proceed further.
311 @@ -200,5 +200,163 @@
312 <arg name="width" type="int" summary="width of rectangle"/>
313 <arg name="height" type="int" summary="height of rectangle"/>
316 + <request name="deactivate_app" since="5">
317 + <description summary="de-activate/hide window identified by app_id">
318 + Ask the compositor to hide the toplevel window for window
319 + management purposes. Depending on the window role, this request
320 + will either display the previously active window (or the background
321 + in case there's no previously active surface) or temporarily (or
322 + until a 'activate_app' is called upon) hide the surface.
324 + All the surfaces are identifiable by using the app_id, and no actions
325 + are taken in case the app_id is not/was not present.
327 + See xdg_toplevel.set_app_id from the xdg-shell protocol for a
328 + description of app_id.
330 + <arg name="app_id" type="string"/>
333 + <request name="set_app_float" since="6">
334 + <description summary="set the window identified by app_id as float">
335 + Makes the application identified by app_id as floating. If the
336 + application's window is already mapped, in a maximized, normal state,
337 + it would transition to the float state.
339 + For applications that want to modify their own state, this request
340 + must be done before the initial surface commit in order to take effect.
342 + If the application is already in floating state, this request wouldn't
345 + There's no persistence of this request, once the application terminated
346 + you'll to issue this request again for that particular app_id.
348 + The x, and y values would be initial position of the window where the
349 + window surface will be placed.
351 + See xdg_toplevel.set_app_id from the xdg-shell protocol for a
352 + description of app_id.
354 + <arg name="app_id" type="string"/>
355 + <arg name="x" type="int" summary="x position"/>
356 + <arg name="y" type="int" summary="y position"/>
359 + <request name="set_app_normal" since="6">
360 + <description summary="set the window identified by app_id as normally started">
361 + Returns the application identified by app_id as it was in the normal state.
362 + This is useful to come back from other states to the maximized state, the
363 + normal state applications are started.
365 + <arg name="app_id" type="string"/>
368 + <request name="set_app_fullscreen" since="7">
369 + <description summary="">
370 + Makes the application identified by app_id as fullscreen. If the
371 + application's window is already mapped, in a maximized, normal state,
372 + it would transition to the fullscreen state.
374 + For applications that want to modify their own state, this request
375 + must be done before the initial surface commit in order to take effect.
377 + If the application is already in fullscreen state, this request wouldn't
380 + There's no persistence of this request, once the application terminated
381 + you'll to issue this request again for that particular app_id.
383 + See xdg_toplevel.set_app_id from the xdg-shell protocol for a
384 + description of app_id.
386 + <arg name="app_id" type="string"/>
389 + <request name="set_app_output" since="8">
390 + <description summary="Assign an application to a particular output">
391 + This would allow the compositor to place an application on a particular
392 + output, if that output is indeed available. This can happen before
393 + application is started which would make the application start on that
394 + particular output. If the application is already started it would
395 + move the application to that output.
397 + There's no persistence of this request, once the application terminated
398 + you'll need to issue this request again for that particular app_id.
400 + See xdg_toplevel.set_app_id from the xdg-shell protocol for a
401 + description of app_id.
403 + <arg name="app_id" type="string"/>
404 + <arg name="output" type="object" interface="wl_output"/>
407 + <event name="app_on_output" since="8">
408 + <description summary="Event sent as a reponse to set_app_output">
409 + Clients can use this event to be notified when an application
410 + wants to be displayed on a certain output. This event is sent in
411 + response to the set_app_output request.
413 + See xdg_toplevel.set_app_id from the xdg-shell protocol for a
414 + description of app_id.
416 + <arg name="app_id" type="string"/>
417 + <arg name="output_name" type="string"/>
421 + <interface name="agl_shell_ext" version="1">
422 + <description summary="extended user interface for Automotive Grade Linux platform">
423 + This interface allows another client bind to the agl_shell interface,
424 + while there's another shell client already present.
426 + The client should first bind to this interface and then inform the
427 + compositor with the 'doas_shell_client' request and it wants to bind to
428 + the agl_shell interface. The client is still expected, if using a new
429 + version of the agl_shell interface, to wait for the 'bound_ok' and
430 + 'bound_fail' events before issueing any other requests/events.
432 + Note that this interface has its limitations, and the compositor would
433 + still refuse the act for 'set_panel' or 'set_background' requests
434 + of the agl_shell interface if there's already a client that used them.
436 + Any other requests or events should be delievered and handled as it would
437 + a client bound to the agl_shell interface.
440 + <enum name="doas_shell_client_status">
441 + <entry name="success" value="0"/>
442 + <entry name="failed" value="1"/>
445 + <request name="destroy" type="destructor">
446 + <description summary="destroys the factory object">
447 + Call the destructor once you're ready with agl_shell_ext interface.
448 + This would reset the state and would make any requests made
449 + on the agl_shell interface be terminated. The client would need
450 + to bind again the agl_shell_ext and issue a 'doas_shell_client'
455 + <request name="doas_shell_client">
456 + <description summary="Informs the compositor it wants to bind to the
457 + agl_shell interface">
458 + Prior to binding to agl_shell interface, this request would inform
459 + the compositor that it wants to gain access the agl_shell interface.
460 + The client is expected to wait for 'doas_shell_client_done' event and
461 + check for a successful status before going further with binding to
462 + the agl_shell interface.
466 + <event name="doas_done">
467 + <description summary="event sent as a reply to doas_shell_client">
468 + The client should check the status event to verify that the
469 + compositor was able to handle the request.
471 + <arg name="status" type="uint" enum="doas_shell_client_status"/>