From f296db8ed6c75cf5b0bb5c1bc1414038f43a1ef3 Mon Sep 17 00:00:00 2001 From: Sebastian Urban Date: Wed, 8 May 2024 23:09:02 +0200 Subject: [PATCH 01/14] wayland: text input protocol uses UTF-8 encoding (#415) * wayland: text input protocol uses UTF-8 encoding The Wayland text input protocol sends text in UTF-8 encoding, but the Flutter TextInputModel expects UTF-16. This adds the missing conversion from UTF-8 to UTF-16. Signed-off-by: Sebastian Urban * Add Sebastian Urban to AUTHORS --------- Signed-off-by: Sebastian Urban --- AUTHORS | 1 + .../window/elinux_window_wayland.cc | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index d82960b5..98613147 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,3 +13,4 @@ Makoto Sato (makoto.sato@atmark-techno.com) Yunhao Tian (t123yh@outlook.com) Luke Howard Stanislav Shmarov +Sebastian Urban diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc index 78220b1d..debed756 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include "flutter/shell/platform/linux_embedded/logger.h" @@ -748,7 +750,10 @@ const zwp_text_input_v1_listener ELinuxWindowWayland::kZwpTextInputV1Listener = auto self = reinterpret_cast(data); if (self->binding_handler_delegate_ && strlen(text)) { - self->binding_handler_delegate_->OnVirtualKey(text[0]); + std::wstring_convert, char16_t> + utf8_converter; + std::u16string utf16_text = utf8_converter.from_bytes(text); + self->binding_handler_delegate_->OnVirtualKey(utf16_text[0]); } if (self->zwp_text_input_v1_) { zwp_text_input_v1_reset(self->zwp_text_input_v1_); @@ -779,7 +784,10 @@ const zwp_text_input_v1_listener ELinuxWindowWayland::kZwpTextInputV1Listener = // commit_string is notified only when the space key is pressed. auto self = reinterpret_cast(data); if (self->binding_handler_delegate_ && strlen(text)) { - self->binding_handler_delegate_->OnVirtualKey(text[0]); + std::wstring_convert, char16_t> + utf8_converter; + std::u16string utf16_text = utf8_converter.from_bytes(text); + self->binding_handler_delegate_->OnVirtualKey(utf16_text[0]); } // If there is no input data, the backspace key cannot be used, // so set dummy data. @@ -895,7 +903,10 @@ const zwp_text_input_v3_listener ELinuxWindowWayland::kZwpTextInputV3Listener = auto self = reinterpret_cast(data); if (self->binding_handler_delegate_ && strlen(text)) { - self->binding_handler_delegate_->OnVirtualKey(text[0]); + std::wstring_convert, char16_t> + utf8_converter; + std::u16string utf16_text = utf8_converter.from_bytes(text); + self->binding_handler_delegate_->OnVirtualKey(utf16_text[0]); } }, .delete_surrounding_text = [](void* data, From 97438621dbd6b5899859c052ad2ef55843373de4 Mon Sep 17 00:00:00 2001 From: Hidenori Matsubayashi Date: Sat, 18 May 2024 06:15:47 +0900 Subject: [PATCH 02/14] embedder: merge embedder.h from flutter/engine (#416) Merged from https://github.com/flutter/engine/commits/3.22.0/shell/platform/embedder/embedder.h Signed-off-by: Hidenori Matsubayashi --- .../shell/platform/embedder/embedder.h | 116 +++++++++++++++++- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/src/flutter/shell/platform/embedder/embedder.h b/src/flutter/shell/platform/embedder/embedder.h index d8adf5e7..d26e2215 100644 --- a/src/flutter/shell/platform/embedder/embedder.h +++ b/src/flutter/shell/platform/embedder/embedder.h @@ -831,6 +831,53 @@ typedef struct { }; } FlutterRendererConfig; +typedef struct { + /// The size of this struct. + /// Must be sizeof(FlutterRemoveViewResult). + size_t struct_size; + + /// True if the remove view operation succeeded. + bool removed; + + /// The |FlutterRemoveViewInfo.user_data|. + void* user_data; +} FlutterRemoveViewResult; + +/// The callback invoked by the engine when the engine has attempted to remove +/// a view. +/// +/// The |FlutterRemoveViewResult| will be deallocated once the callback returns. +typedef void (*FlutterRemoveViewCallback)( + const FlutterRemoveViewResult* /* result */); + +typedef struct { + /// The size of this struct. + /// Must be sizeof(FlutterRemoveViewInfo). + size_t struct_size; + + /// The identifier for the view to remove. + /// + /// The implicit view cannot be removed if it is enabled. + FlutterViewId view_id; + + /// A baton that is not interpreted by the engine in any way. + /// It will be given back to the embedder in |remove_view_callback|. + /// Embedder resources may be associated with this baton. + void* user_data; + + /// Called once the engine has attempted to remove the view. + /// This callback is required. + /// + /// The embedder must not destroy the underlying surface until the callback is + /// invoked with a `removed` value of `true`. + /// + /// This callback is invoked on an internal engine managed thread. + /// Embedders must re-thread if necessary. + /// + /// The |result| argument will be deallocated when the callback returns. + FlutterRemoveViewCallback remove_view_callback; +} FlutterRemoveViewInfo; + /// Display refers to a graphics hardware system consisting of a framebuffer, /// typically a monitor or a screen. This ID is unique per display and is /// stable until the Flutter application restarts. @@ -859,6 +906,8 @@ typedef struct { double physical_view_inset_left; /// The identifier of the display the view is rendering on. FlutterEngineDisplayId display_id; + /// The view that this event is describing. + int64_t view_id; } FlutterWindowMetricsEvent; /// The phase of the pointer event. @@ -1736,8 +1785,30 @@ typedef struct { /// Extra information for the backing store that the embedder may /// use during presentation. FlutterBackingStorePresentInfo* backing_store_present_info; + + // Time in nanoseconds at which this frame is scheduled to be presented. 0 if + // not known. See FlutterEngineGetCurrentTime(). + uint64_t presentation_time; } FlutterLayer; +typedef struct { + /// The size of this struct. + /// Must be sizeof(FlutterPresentViewInfo). + size_t struct_size; + + /// The identifier of the target view. + FlutterViewId view_id; + + /// The layers that should be composited onto the view. + const FlutterLayer** layers; + + /// The count of layers. + size_t layers_count; + + /// The |FlutterCompositor.user_data|. + void* user_data; +} FlutterPresentViewInfo; + typedef bool (*FlutterBackingStoreCreateCallback)( const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out, @@ -1751,13 +1822,20 @@ typedef bool (*FlutterLayersPresentCallback)(const FlutterLayer** layers, size_t layers_count, void* user_data); +/// The callback invoked when the embedder should present to a view. +/// +/// The |FlutterPresentViewInfo| will be deallocated once the callback returns. +typedef bool (*FlutterPresentViewCallback)( + const FlutterPresentViewInfo* /* present info */); + typedef struct { /// This size of this struct. Must be sizeof(FlutterCompositor). size_t struct_size; /// A baton that in not interpreted by the engine in any way. If it passed /// back to the embedder in `FlutterCompositor.create_backing_store_callback`, - /// `FlutterCompositor.collect_backing_store_callback` and - /// `FlutterCompositor.present_layers_callback` + /// `FlutterCompositor.collect_backing_store_callback`, + /// `FlutterCompositor.present_layers_callback`, and + /// `FlutterCompositor.present_view_callback`. void* user_data; /// A callback invoked by the engine to obtain a backing store for a specific /// `FlutterLayer`. @@ -1771,10 +1849,23 @@ typedef struct { /// embedder may collect any resources associated with the backing store. FlutterBackingStoreCollectCallback collect_backing_store_callback; /// Callback invoked by the engine to composite the contents of each layer - /// onto the screen. + /// onto the implicit view. + /// + /// DEPRECATED: Use |present_view_callback| to support multiple views. + /// + /// Only one of `present_layers_callback` and `present_view_callback` may be + /// provided. Providing both is an error and engine initialization will + /// terminate. FlutterLayersPresentCallback present_layers_callback; /// Avoid caching backing stores provided by this compositor. bool avoid_backing_store_cache; + /// Callback invoked by the engine to composite the contents of each layer + /// onto the specified view. + /// + /// Only one of `present_layers_callback` and `present_view_callback` may be + /// provided. Providing both is an error and engine initialization will + /// terminate. + FlutterPresentViewCallback present_view_callback; } FlutterCompositor; typedef struct { @@ -2414,6 +2505,25 @@ FLUTTER_EXPORT FlutterEngineResult FlutterEngineRunInitialized( FLUTTER_API_SYMBOL(FlutterEngine) engine); +//------------------------------------------------------------------------------ +/// @brief Removes a view. +/// +/// This is an asynchronous operation. The view's resources must not +/// be cleaned up until the |remove_view_callback| is invoked with +/// a |removed| value of `true`. +/// +/// @param[in] engine A running engine instance. +/// @param[in] info The remove view arguments. This can be deallocated +/// once |FlutterEngineRemoveView| returns, before +/// |remove_view_callback| is invoked. +/// +/// @return The result of *starting* the asynchronous operation. If +/// `kSuccess`, the |remove_view_callback| will be invoked. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterRemoveViewInfo* info); + FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, From 434d509ffefdd1b2ab023ef1ccad7bb4048bd990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Thu, 23 May 2024 23:58:38 +0000 Subject: [PATCH 03/14] Multiple seats (#417) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added the ability to have multiple seats, this allows f.e. weston screenshare and a local touch input to work at the same time. Signed-off-by: Ómar Högni Guðmarsson --- AUTHORS | 1 + .../window/elinux_window_wayland.cc | 171 ++++++++++-------- .../window/elinux_window_wayland.h | 12 +- 3 files changed, 107 insertions(+), 77 deletions(-) diff --git a/AUTHORS b/AUTHORS index 98613147..151ed913 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,3 +14,4 @@ Yunhao Tian (t123yh@outlook.com) Luke Howard Stanislav Shmarov Sebastian Urban +Ómar Högni Guðmarsson diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc index debed756..33848c83 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc @@ -289,29 +289,32 @@ const wl_seat_listener ELinuxWindowWayland::kWlSeatListener = { ELINUX_LOG(TRACE) << "wl_seat_listener.capabilities"; auto self = reinterpret_cast(data); - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !self->wl_pointer_) { - self->wl_pointer_ = wl_seat_get_pointer(seat); - wl_pointer_add_listener(self->wl_pointer_, &kWlPointerListener, self); - } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && self->wl_pointer_) { - wl_pointer_destroy(self->wl_pointer_); - self->wl_pointer_ = nullptr; + auto [iter_inputs, _] = + self->seat_inputs_map_.emplace(seat, seat_inputs()); + auto& inputs = iter_inputs->second; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !inputs.pointer) { + inputs.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(inputs.pointer, &kWlPointerListener, self); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && inputs.pointer) { + wl_pointer_destroy(inputs.pointer); + inputs.pointer = nullptr; } - if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !self->wl_touch_) { - self->wl_touch_ = wl_seat_get_touch(seat); - wl_touch_add_listener(self->wl_touch_, &kWlTouchListener, self); - } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && self->wl_touch_) { - wl_touch_destroy(self->wl_touch_); - self->wl_touch_ = nullptr; + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !inputs.touch) { + inputs.touch = wl_seat_get_touch(seat); + wl_touch_add_listener(inputs.touch, &kWlTouchListener, self); + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && inputs.touch) { + wl_touch_destroy(inputs.touch); + inputs.touch = nullptr; } - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !self->wl_keyboard_) { - self->wl_keyboard_ = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(self->wl_keyboard_, &kWlKeyboardListener, - self); - } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && self->wl_keyboard_) { - wl_keyboard_destroy(self->wl_keyboard_); - self->wl_keyboard_ = nullptr; + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !inputs.keyboard) { + inputs.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(inputs.keyboard, &kWlKeyboardListener, self); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && inputs.keyboard) { + wl_keyboard_destroy(inputs.keyboard); + inputs.keyboard = nullptr; } }, .name = [](void* data, struct wl_seat* wl_seat, const char* name) -> void { @@ -333,7 +336,7 @@ const wl_pointer_listener ELinuxWindowWayland::kWlPointerListener = { self->serial_ = serial; if (self->view_properties_.use_mouse_cursor) { - self->cursor_info_.pointer = self->wl_pointer_; + self->cursor_info_.pointer = wl_pointer; self->cursor_info_.serial = serial; } @@ -399,7 +402,14 @@ const wl_pointer_listener ELinuxWindowWayland::kWlPointerListener = { self->window_decorations_->IsMatched( self->wl_current_surface_, WindowDecoration::DecorationType::TITLE_BAR)) { - xdg_toplevel_move(self->xdg_toplevel_, self->wl_seat_, serial); + for (auto& [seat, inputs] : self->seat_inputs_map_) { + if (inputs.pointer == pointer) { + xdg_toplevel_move(self->xdg_toplevel_, seat, serial); + return; + } + } + ELINUX_LOG(TRACE) + << "Failed to find the pointer for moving the window"; return; } @@ -1043,11 +1053,8 @@ ELinuxWindowWayland::ELinuxWindowWayland( wl_compositor_(nullptr), wl_subcompositor_(nullptr), wl_current_surface_(nullptr), - wl_seat_(nullptr), - wl_pointer_(nullptr), - wl_touch_(nullptr), - wl_keyboard_(nullptr), wl_shm_(nullptr), + seat_inputs_map_(), wl_data_device_manager_(nullptr), wl_data_device_(nullptr), wl_data_offer_(nullptr), @@ -1087,34 +1094,37 @@ ELinuxWindowWayland::ELinuxWindowWayland( wl_registry_add_listener(wl_registry_, &kWlRegistryListener, this); wl_display_roundtrip(wl_display_); - if (wl_data_device_manager_ && wl_seat_) { - wl_data_device_ = wl_data_device_manager_get_data_device( - wl_data_device_manager_, wl_seat_); - wl_data_device_add_listener(wl_data_device_, &kWlDataDeviceListener, this); - } + for (auto& [seat, _] : seat_inputs_map_) { + if (wl_data_device_manager_ && seat) { + wl_data_device_ = + wl_data_device_manager_get_data_device(wl_data_device_manager_, seat); + wl_data_device_add_listener(wl_data_device_, &kWlDataDeviceListener, + this); + } - // Setup text-input protocol for onscreen keyboard inputs. - { - if (zwp_text_input_manager_v3_ && wl_seat_) { - zwp_text_input_v3_ = zwp_text_input_manager_v3_get_text_input( - zwp_text_input_manager_v3_, wl_seat_); - if (!zwp_text_input_v3_) { - ELINUX_LOG(ERROR) << "Failed to create the text input manager v3."; - return; - } - zwp_text_input_v3_add_listener(zwp_text_input_v3_, - &kZwpTextInputV3Listener, this); - } else if (zwp_text_input_manager_v1_) { - zwp_text_input_v1_ = zwp_text_input_manager_v1_create_text_input( - zwp_text_input_manager_v1_); - if (!zwp_text_input_v1_) { - ELINUX_LOG(ERROR) << "Failed to create text input manager v1."; - return; + // Setup text-input protocol for onscreen keyboard inputs. + { + if (zwp_text_input_manager_v3_ && seat) { + zwp_text_input_v3_ = zwp_text_input_manager_v3_get_text_input( + zwp_text_input_manager_v3_, seat); + if (!zwp_text_input_v3_) { + ELINUX_LOG(ERROR) << "Failed to create the text input manager v3."; + return; + } + zwp_text_input_v3_add_listener(zwp_text_input_v3_, + &kZwpTextInputV3Listener, this); + } else if (zwp_text_input_manager_v1_) { + zwp_text_input_v1_ = zwp_text_input_manager_v1_create_text_input( + zwp_text_input_manager_v1_); + if (!zwp_text_input_v1_) { + ELINUX_LOG(ERROR) << "Failed to create text input manager v1."; + return; + } + zwp_text_input_v1_add_listener(zwp_text_input_v1_, + &kZwpTextInputV1Listener, this); + } else { + // do nothing. } - zwp_text_input_v1_add_listener(zwp_text_input_v1_, - &kZwpTextInputV1Listener, this); - } else { - // do nothing. } } @@ -1190,26 +1200,29 @@ ELinuxWindowWayland::~ELinuxWindowWayland() { wl_data_device_manager_ = nullptr; } - if (wl_pointer_) { - wl_pointer_destroy(wl_pointer_); - wl_pointer_ = nullptr; - } + for (auto& [seat, inputs] : seat_inputs_map_) { + if (inputs.pointer) { + wl_pointer_destroy(inputs.pointer); + inputs.pointer = nullptr; + } - if (wl_touch_) { - wl_touch_destroy(wl_touch_); - wl_touch_ = nullptr; - } + if (inputs.touch) { + wl_touch_destroy(inputs.touch); + inputs.touch = nullptr; + } - if (wl_keyboard_) { - wl_keyboard_destroy(wl_keyboard_); - wl_keyboard_ = nullptr; - } + if (inputs.keyboard) { + wl_keyboard_destroy(inputs.keyboard); + inputs.keyboard = nullptr; + } - if (wl_seat_) { - wl_seat_destroy(wl_seat_); - wl_seat_ = nullptr; + if (seat) { + wl_seat_destroy(seat); + } } + seat_inputs_map_.clear(); + if (wl_output_) { wl_output_destroy(wl_output_); wl_output_ = nullptr; @@ -1486,7 +1499,7 @@ void ELinuxWindowWayland::DestroyRenderSurface() { void ELinuxWindowWayland::UpdateVirtualKeyboardStatus(const bool show) { // Not supported virtual keyboard. - if (!(zwp_text_input_v1_ || zwp_text_input_v3_) || !wl_seat_) { + if (!(zwp_text_input_v1_ || zwp_text_input_v3_) || seat_inputs_map_.empty()) { return; } @@ -1500,7 +1513,14 @@ void ELinuxWindowWayland::UpdateVirtualKeyboardStatus(const bool show) { void ELinuxWindowWayland::UpdateFlutterCursor(const std::string& cursor_name) { if (view_properties_.use_mouse_cursor) { - if (!wl_pointer_) { + wl_pointer* pointer = nullptr; + for (auto& [_, inputs] : seat_inputs_map_) { + if (inputs.pointer != nullptr) { + pointer = inputs.pointer; + break; + } + } + if (!pointer) { return; } if (cursor_name.compare(cursor_info_.cursor_name) == 0) { @@ -1619,9 +1639,12 @@ void ELinuxWindowWayland::WlRegistryHandler(wl_registry* wl_registry, if (!strcmp(interface, wl_seat_interface.name)) { constexpr uint32_t kMaxVersion = 4; - wl_seat_ = static_cast(wl_registry_bind( - wl_registry, name, &wl_seat_interface, std::min(kMaxVersion, version))); - wl_seat_add_listener(wl_seat_, &kWlSeatListener, this); + auto [inserted, _] = + seat_inputs_map_.emplace(static_cast(wl_registry_bind( + wl_registry, name, &wl_seat_interface, + std::min(kMaxVersion, version))), + seat_inputs()); + wl_seat_add_listener(inserted->first, &kWlSeatListener, this); return; } @@ -1826,7 +1849,8 @@ void ELinuxWindowWayland::ShowVirtualKeyboard() { } else { if (native_window_) { zwp_text_input_v1_show_input_panel(zwp_text_input_v1_); - zwp_text_input_v1_activate(zwp_text_input_v1_, wl_seat_, + zwp_text_input_v1_activate(zwp_text_input_v1_, + seat_inputs_map_.begin()->first, native_window_->Surface()); } } @@ -1837,7 +1861,8 @@ void ELinuxWindowWayland::DismissVirtualKeybaord() { zwp_text_input_v3_disable(zwp_text_input_v3_); zwp_text_input_v3_commit(zwp_text_input_v3_); } else { - zwp_text_input_v1_deactivate(zwp_text_input_v1_, wl_seat_); + zwp_text_input_v1_deactivate(zwp_text_input_v1_, + seat_inputs_map_.begin()->first); } } diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h index dcfe1bb4..a4e454c4 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h @@ -34,6 +34,13 @@ namespace { constexpr char kXcursorSizeEnvironmentKey[] = "XCURSOR_SIZE"; } // namespace +// Track input devices for each seat +struct seat_inputs { + wl_pointer* pointer = nullptr; + wl_touch* touch = nullptr; + wl_keyboard* keyboard = nullptr; +}; + class ELinuxWindowWayland : public ELinuxWindow, public WindowBindingHandler { public: ELinuxWindowWayland(FlutterDesktopViewProperties view_properties); @@ -161,12 +168,9 @@ class ELinuxWindowWayland : public ELinuxWindow, public WindowBindingHandler { wl_display* wl_display_; wl_registry* wl_registry_; wl_compositor* wl_compositor_; - wl_seat* wl_seat_; wl_output* wl_output_; wl_shm* wl_shm_; - wl_pointer* wl_pointer_; - wl_touch* wl_touch_; - wl_keyboard* wl_keyboard_; + std::unordered_map seat_inputs_map_; wl_surface* wl_cursor_surface_; xdg_wm_base* xdg_wm_base_; xdg_surface* xdg_surface_; From d73e35a3be61fd20ab82e5ecd3e8bb69fd864196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Tue, 28 May 2024 14:11:13 +0000 Subject: [PATCH 04/14] wayland: Fix pointer not being drawn on second VNC connect. (#418) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use global_remove event to clean-up seat map. Signed-off-by: Ómar Högni Guðmarsson --- .../window/elinux_window_wayland.cc | 61 +++++++++++++------ .../window/elinux_window_wayland.h | 1 + 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc index 33848c83..5bd8e0c2 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc @@ -289,15 +289,18 @@ const wl_seat_listener ELinuxWindowWayland::kWlSeatListener = { ELINUX_LOG(TRACE) << "wl_seat_listener.capabilities"; auto self = reinterpret_cast(data); - auto [iter_inputs, _] = - self->seat_inputs_map_.emplace(seat, seat_inputs()); - auto& inputs = iter_inputs->second; + auto seat_iter = self->seat_inputs_map_.find(seat); + if (seat_iter == self->seat_inputs_map_.end()) { + ELINUX_LOG(ERROR) << "Failed to find the seat"; + return; + } + auto& inputs = seat_iter->second; if ((caps & WL_SEAT_CAPABILITY_POINTER) && !inputs.pointer) { inputs.pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(inputs.pointer, &kWlPointerListener, self); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && inputs.pointer) { - wl_pointer_destroy(inputs.pointer); + wl_pointer_release(inputs.pointer); inputs.pointer = nullptr; } @@ -305,7 +308,7 @@ const wl_seat_listener ELinuxWindowWayland::kWlSeatListener = { inputs.touch = wl_seat_get_touch(seat); wl_touch_add_listener(inputs.touch, &kWlTouchListener, self); } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && inputs.touch) { - wl_touch_destroy(inputs.touch); + wl_touch_release(inputs.touch); inputs.touch = nullptr; } @@ -313,7 +316,7 @@ const wl_seat_listener ELinuxWindowWayland::kWlSeatListener = { inputs.keyboard = wl_seat_get_keyboard(seat); wl_keyboard_add_listener(inputs.keyboard, &kWlKeyboardListener, self); } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && inputs.keyboard) { - wl_keyboard_destroy(inputs.keyboard); + wl_keyboard_release(inputs.keyboard); inputs.keyboard = nullptr; } }, @@ -347,6 +350,11 @@ const wl_pointer_listener ELinuxWindowWayland::kWlPointerListener = { self->pointer_x_ = x_px; self->pointer_y_ = y_px; } + + // Force redraw even though it is the same cursor name + auto copy = self->cursor_info_.cursor_name; + self->cursor_info_.cursor_name.clear(); + self->UpdateFlutterCursor(copy); }, .leave = [](void* data, wl_pointer* pointer, @@ -1513,16 +1521,6 @@ void ELinuxWindowWayland::UpdateVirtualKeyboardStatus(const bool show) { void ELinuxWindowWayland::UpdateFlutterCursor(const std::string& cursor_name) { if (view_properties_.use_mouse_cursor) { - wl_pointer* pointer = nullptr; - for (auto& [_, inputs] : seat_inputs_map_) { - if (inputs.pointer != nullptr) { - pointer = inputs.pointer; - break; - } - } - if (!pointer) { - return; - } if (cursor_name.compare(cursor_info_.cursor_name) == 0) { return; } @@ -1644,6 +1642,7 @@ void ELinuxWindowWayland::WlRegistryHandler(wl_registry* wl_registry, wl_registry, name, &wl_seat_interface, std::min(kMaxVersion, version))), seat_inputs()); + registry_names_to_seat_ptr_.emplace(name, inserted->first); wl_seat_add_listener(inserted->first, &kWlSeatListener, this); return; } @@ -1720,7 +1719,35 @@ void ELinuxWindowWayland::WlRegistryHandler(wl_registry* wl_registry, } void ELinuxWindowWayland::WlUnRegistryHandler(wl_registry* wl_registry, - uint32_t name) {} + uint32_t name) { + // Just remove seats for now, as we don't need to do anything else. + // But there is also no interface name here. + auto seat_iter = registry_names_to_seat_ptr_.find(name); + if (seat_iter != registry_names_to_seat_ptr_.end()) { + auto seat = seat_iter->second; + auto seat_inputs_iter = seat_inputs_map_.find(seat); + if (seat_inputs_iter != seat_inputs_map_.end()) { + auto& inputs = seat_inputs_iter->second; + if (inputs.pointer) { + wl_pointer_release(inputs.pointer); + inputs.pointer = nullptr; + } + if (inputs.touch) { + wl_touch_release(inputs.touch); + inputs.touch = nullptr; + } + if (inputs.keyboard) { + wl_keyboard_release(inputs.keyboard); + inputs.keyboard = nullptr; + } + seat_inputs_map_.erase(seat_inputs_iter); + } + if (seat) { + wl_seat_destroy(seat); + } + registry_names_to_seat_ptr_.erase(name); + } +} bool ELinuxWindowWayland::LoadCursorTheme(uint32_t size) { if (!wl_shm_) { diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h index a4e454c4..8ccb84ba 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.h @@ -171,6 +171,7 @@ class ELinuxWindowWayland : public ELinuxWindow, public WindowBindingHandler { wl_output* wl_output_; wl_shm* wl_shm_; std::unordered_map seat_inputs_map_; + std::unordered_map registry_names_to_seat_ptr_; wl_surface* wl_cursor_surface_; xdg_wm_base* xdg_wm_base_; xdg_surface* xdg_surface_; From 595f7f5bb02a32eb09a3ad136a5f9736e0fa7244 Mon Sep 17 00:00:00 2001 From: "Athaariq \"Eric\" Ardhiansyah" Date: Mon, 3 Jun 2024 06:34:17 +0700 Subject: [PATCH 05/14] Add libuv support as a replacement of systemd + some CMake fixes (#420) * git: prevent accidental push on generated files Signed-off-by: Athaariq Ardhiansyah * cmake: fix wrong option usage The option() function only dedicated for boolean type. For enumerated string, better use both set(... CACHE STRING ...) and set_property(CACHE ... PROPERTY STRINGS ...). Signed-off-by: Athaariq Ardhiansyah * cmake: fix wrong string cache syntax in set() For further information, visit the documentation at https://cmake.org/cmake/help/latest/command/set.html#set-cache-entry Signed-off-by: Athaariq Ardhiansyah * cmake: warn if BUILD_ELINUX_SO is OFF but BACKEND_TYPE was modified This commit prevents other new contributors from mistakenly blame CMakeLists.txt. Therefore, I added a warning just in case they forgot to turn on BUILD_ELINUX_SO while modifying BACKEND_TYPE. Signed-off-by: Athaariq Ardhiansyah * cmake: enforce regex exactness for safety Signed-off-by: Athaariq Ardhiansyah * drm: add libuv support as systemd replacement Not all embedded systems can include systemd due to their constrained resource limit. Our requirement from libsystemd is only event loop (sd-event). Therefore, libuv is perfect as drop-in replacement of sd-event. I tested the changes on Raspberry Pi 5 with Buildroot (yes, I am working on it too) and a x86-64 laptop with Arch Linux. Both are working as intended. Interestingly, libuv makes flutter-elinux smoother on my laptop. I have no idea how to benchmark it, but it is a good sign for us. For now, I am assuming users are still relying on libsystemd. Therefore, libuv only be used if the target device has no systemd. If maintainer want to drop the systemd dependency, I suggest to deprecate it gracefully and plan to drop it later. Otherwise, it would be a breaking change. Signed-off-by: Athaariq Ardhiansyah * author: add Athaariq Ardhiansyah Signed-off-by: Athaariq Ardhiansyah * style: overhaul to comply the standard Signed-off-by: Athaariq Ardhiansyah * style: format with clang-format v14.0.6 I realized that clang-format is inconsistent between its versions. So, Arch Linux (clang v17) and Ubuntu Jammy (clang v14) will output different formatting result. For Arch Linux users, the clang14 package has no clang-format included. You need to compile the entire clang on your own until the downstream maintainer include it. Signed-off-by: Athaariq Ardhiansyah * cmake: fix error for CMake before v3.28 On some distributions with non-latest CMake, it will fail on configuration due to its incapability of substition from unset variable to an empty string. To fix this, we need to add double quotes between variables that will be tested by EQUAL, LESS, GREATER, and other related keywords. For better compatibility, use STREQUAL instead of EQUAL if possible. Signed-off-by: Athaariq Ardhiansyah --------- Signed-off-by: Athaariq Ardhiansyah --- .gitignore | 1 + AUTHORS | 1 + CMakeLists.txt | 8 +- cmake/build.cmake | 16 ++- cmake/package.cmake | 13 ++- .../include/flutter/encodable_value.h | 4 +- .../include/flutter/texture_registrar.h | 3 +- .../platform/common/json_method_codec.cc | 3 +- .../common/public/flutter_texture_registrar.h | 15 +-- .../shell/platform/common/text_range.h | 4 +- .../linux_embedded/window/elinux_window_drm.h | 107 ++++++++++++++++-- 11 files changed, 147 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 4b17e1c8..d199025c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ /build/ +/src/third_party/wayland/protocols/* \ No newline at end of file diff --git a/AUTHORS b/AUTHORS index 151ed913..368deab2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,3 +15,4 @@ Luke Howard Stanislav Shmarov Sebastian Urban Ómar Högni Guðmarsson +Athaariq Ardhiansyah diff --git a/CMakeLists.txt b/CMakeLists.txt index 0150694c..a467410a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,8 @@ set(CMAKE_CXX_STANDARD 17) project("flutter_elinux" LANGUAGES CXX C) # Build options. -option(BACKEND_TYPE "Select WAYLAND, DRM-GBM, DRM-EGLSTREAM, or X11 as the display backend type" WAYLAND) +set(BACKEND_TYPE "WAYLAND" CACHE STRING "Select WAYLAND, DRM-GBM, DRM-EGLSTREAM, or X11 as the display backend type") +set_property(CACHE BACKEND_TYPE PROPERTY STRINGS "WAYLAND" "DRM-GBM" "DRM-EGLSTREAM" "X11") # Disabled USE_DIRTY_REGION_MANAGEMENT due flicker issue. # See https://github.com/sony/flutter-embedded-linux/issues/334 option(USE_DIRTY_REGION_MANAGEMENT "Use Flutter dirty region management" OFF) @@ -24,8 +25,11 @@ option(FLUTTER_RELEASE "Build Flutter Engine with release mode" OFF) if(NOT BUILD_ELINUX_SO) # Load the user project. - set(USER_PROJECT_PATH "examples/flutter-wayland-client" CACHE STRING "") + set(USER_PROJECT_PATH "" CACHE STRING "examples/flutter-wayland-client") message("User project: ${USER_PROJECT_PATH}") + if(NOT ${BACKEND_TYPE} STREQUAL "WAYLAND") + message(WARNING "BACKEND_TYPE variable is ignored because BUILD_ELINUX_SO is OFF") + endif() include(${USER_PROJECT_PATH}/cmake/user_config.cmake) else() # Set the filename of elinux .so file diff --git a/cmake/build.cmake b/cmake/build.cmake index 7bd34f7e..ad69502b 100644 --- a/cmake/build.cmake +++ b/cmake/build.cmake @@ -249,6 +249,7 @@ target_link_libraries(${TARGET} ${LIBINPUT_LIBRARIES} ${LIBUDEV_LIBRARIES} ${LIBSYSTEMD_LIBRARIES} + ${LIBUV_LIBRARIES} ${X11_LIBRARIES} ${LIBWESTON_LIBRARIES} ${FLUTTER_EMBEDDER_LIB} @@ -256,11 +257,16 @@ target_link_libraries(${TARGET} ${USER_APP_LIBRARIES} ) -if(${BACKEND_TYPE} MATCHES "DRM-(GBM|EGLSTREAM)") -target_link_libraries(${TARGET} - PRIVATE - Threads::Threads -) +if(${BACKEND_TYPE} MATCHES "^DRM-(GBM|EGLSTREAM)$") + target_link_libraries(${TARGET} + PRIVATE + Threads::Threads + ) + + # Indicate whether libsystemd must replace libuv + if("${LIBSYSTEMD_FOUND}" STREQUAL "1") + add_definitions(-DUSE_LIBSYSTEMD) + endif() endif() set(FLUTTER_EMBEDDER_LIB "${CMAKE_CURRENT_SOURCE_DIR}/build/libflutter_engine.so") diff --git a/cmake/package.cmake b/cmake/package.cmake index 390d8113..7a2ebcaa 100644 --- a/cmake/package.cmake +++ b/cmake/package.cmake @@ -9,17 +9,26 @@ pkg_check_modules(EGL REQUIRED egl) pkg_check_modules(XKBCOMMON REQUIRED xkbcommon) # depends on backend type. -if(${BACKEND_TYPE} MATCHES "DRM-(GBM|EGLSTREAM)") +if(${BACKEND_TYPE} MATCHES "^DRM-(GBM|EGLSTREAM)$") # DRM backend pkg_check_modules(DRM REQUIRED libdrm) pkg_check_modules(LIBINPUT REQUIRED libinput) pkg_check_modules(LIBUDEV REQUIRED libudev) - pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd) + pkg_check_modules(LIBSYSTEMD libsystemd) + pkg_check_modules(LIBUV libuv) if(${BACKEND_TYPE} STREQUAL "DRM-GBM") pkg_check_modules(GBM REQUIRED gbm) endif() set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) + # Check if either systemd or libuv exist + if((NOT "${LIBSYSTEMD_FOUND}" STREQUAL "1") AND (NOT "${LIBUV_FOUND}" STREQUAL "1")) + message(FATAL_ERROR + "${BACKEND_TYPE} backend requires either libsystemd or libuv, but + they're not exist.") + elseif(("${LIBSYSTEMD_FOUND}" STREQUAL "1") AND ("${LIBUV_FOUND}" STREQUAL "1")) + message("!! NOTICE: libsystemd found, libuv won't be used.") + endif() elseif(${BACKEND_TYPE} STREQUAL "X11") pkg_check_modules(X11 REQUIRED x11) else() diff --git a/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h b/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h index 3a191205..bf243fde 100644 --- a/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h +++ b/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h @@ -70,7 +70,9 @@ class CustomEncodableValue { #if defined(FLUTTER_ENABLE_RTTI) && FLUTTER_ENABLE_RTTI // Passthrough to std::any's type(). - const std::type_info& type() const noexcept { return value_.type(); } + const std::type_info& type() const noexcept { + return value_.type(); + } #endif // This operator exists only to provide a stable ordering for use as a diff --git a/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h b/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h index 4abe19d9..0777e528 100644 --- a/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h +++ b/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h @@ -107,7 +107,8 @@ class GpuSurfaceTexture { // The available texture variants. // Only PixelBufferTexture is currently implemented. // Other variants are expected to be added in the future. -typedef std::variant TextureVariant; +typedef std::variant + TextureVariant; // An object keeping track of external textures. // diff --git a/src/flutter/shell/platform/common/json_method_codec.cc b/src/flutter/shell/platform/common/json_method_codec.cc index cc4ffec6..9ccb79c8 100644 --- a/src/flutter/shell/platform/common/json_method_codec.cc +++ b/src/flutter/shell/platform/common/json_method_codec.cc @@ -104,7 +104,8 @@ JsonMethodCodec::EncodeErrorEnvelopeInternal( rapidjson::Document envelope(rapidjson::kArrayType); auto& allocator = envelope.GetAllocator(); envelope.PushBack(rapidjson::Value(error_code.c_str(), allocator), allocator); - envelope.PushBack(rapidjson::Value(error_message.c_str(), allocator), allocator); + envelope.PushBack(rapidjson::Value(error_message.c_str(), allocator), + allocator); rapidjson::Value details_value; if (error_details) { details_value.CopyFrom(*error_details, allocator); diff --git a/src/flutter/shell/platform/common/public/flutter_texture_registrar.h b/src/flutter/shell/platform/common/public/flutter_texture_registrar.h index cc0dbd2d..fb564382 100644 --- a/src/flutter/shell/platform/common/public/flutter_texture_registrar.h +++ b/src/flutter/shell/platform/common/public/flutter_texture_registrar.h @@ -76,7 +76,8 @@ typedef struct { size_t width; // Height of the EGLImage. size_t height; - // An optional callback that gets invoked when the |egl_image| can be released. + // An optional callback that gets invoked when the |egl_image| can be + // released. void (*release_callback)(void* release_context); // Opaque data passed to |release_callback|. void* release_context; @@ -142,12 +143,12 @@ typedef const FlutterDesktopGpuSurfaceDescriptor* ( size_t height, void* user_data); -typedef const FlutterDesktopEGLImage* ( - *FlutterDesktopEGLImageTextureCallback)(size_t width, - size_t height, - void* egl_display, - void* egl_context, - void* user_data); +typedef const FlutterDesktopEGLImage* (*FlutterDesktopEGLImageTextureCallback)( + size_t width, + size_t height, + void* egl_display, + void* egl_context, + void* user_data); // An object used to configure pixel buffer textures. typedef struct { diff --git a/src/flutter/shell/platform/common/text_range.h b/src/flutter/shell/platform/common/text_range.h index a7b3760b..6a0deff6 100644 --- a/src/flutter/shell/platform/common/text_range.h +++ b/src/flutter/shell/platform/common/text_range.h @@ -7,7 +7,7 @@ #include -//#include "flutter/fml/logging.h" +// #include "flutter/fml/logging.h" namespace flutter { @@ -66,7 +66,7 @@ class TextRange { // // Asserts that the range is of length 0. size_t position() const { - //FML_DCHECK(base_ == extent_); + // FML_DCHECK(base_ == extent_); return extent_; } diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h b/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h index 8b6c0767..0bbb883a 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h @@ -8,9 +8,14 @@ #include #include #include -#include #include +#ifdef USE_LIBSYSTEMD +#include +#else +#include +#endif + #include #include #include @@ -61,6 +66,7 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { return; } +#ifdef USE_LIBSYSTEMD ret = sd_event_new(&libinput_event_loop_); if (ret < 0) { ELINUX_LOG(ERROR) << "Failed to create libinput event loop."; @@ -74,20 +80,46 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { libinput_event_loop_ = sd_event_unref(libinput_event_loop_); return; } +#else // #ifdef USE_LIBSYSTEMD + ret = uv_loop_init(&main_loop_); + if (ret) { + ELINUX_LOG(ERROR) << "Failed to create main event loop."; + return; + } + ret = uv_poll_init(&main_loop_, &libinput_event_loop_, + libinput_get_fd(libinput_)); + if (ret) { + ELINUX_LOG(ERROR) << "Failed to create libinput event loop."; + return; + } + libinput_event_loop_.data = this; + ret = uv_poll_start(&libinput_event_loop_, + UV_READABLE | UV_DISCONNECT | UV_PRIORITIZED, + OnLibinputEvent); + if (ret) { + ELINUX_LOG(ERROR) << "Failed to listen for user input."; + return; + } +#endif // #ifdef USE_LIBSYSTEMD } ~ELinuxWindowDrm() { +#ifdef USE_LIBSYSTEMD if (udev_drm_event_loop_) { sd_event_unref(udev_drm_event_loop_); } +#endif if (udev_monitor_) { udev_monitor_unref(udev_monitor_); } +#ifdef USE_LIBSYSTEMD if (libinput_event_loop_) { sd_event_unref(libinput_event_loop_); } +#endif + libinput_unref(libinput_); display_valid_ = false; } @@ -103,9 +135,13 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { // |FlutterWindowBindingHandler| bool DispatchEvent() override { +#ifdef USE_LIBSYSTEMD constexpr uint64_t kMaxWaitTime = 0; sd_event_run(libinput_event_loop_, kMaxWaitTime); sd_event_run(udev_drm_event_loop_, kMaxWaitTime); +#else + uv_run(&main_loop_, UV_RUN_NOWAIT); +#endif return true; } @@ -192,10 +228,14 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } // |FlutterWindowBindingHandler| - uint16_t GetRotationDegree() const override { return current_rotation_; } + uint16_t GetRotationDegree() const override { + return current_rotation_; + } // |FlutterWindowBindingHandler| - double GetDpiScale() override { return current_scale_; } + double GetDpiScale() override { + return current_scale_; + } // |FlutterWindowBindingHandler| PhysicalWindowBounds GetPhysicalWindowBounds() override { @@ -203,7 +243,9 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } // |FlutterWindowBindingHandler| - int32_t GetFrameRate() override { return 60000; } + int32_t GetFrameRate() override { + return 60000; + } // |FlutterWindowBindingHandler| void UpdateFlutterCursor(const std::string& cursor_name) override { @@ -218,7 +260,9 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } // |FlutterWindowBindingHandler| - std::string GetClipboardData() override { return clipboard_data_; } + std::string GetClipboardData() override { + return clipboard_data_; + } // |FlutterWindowBindingHandler| void SetClipboardData(const std::string& data) override { @@ -290,6 +334,7 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } udev_unref(udev); +#ifdef USE_LIBSYSTEMD if (sd_event_new(&udev_drm_event_loop_) < 0) { ELINUX_LOG(ERROR) << "Failed to create udev drm event loop."; return false; @@ -301,6 +346,22 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { ELINUX_LOG(ERROR) << "Failed to listen for udev drm event."; return false; } +#else // #ifdef USE_LIBSYSTEMD + if (uv_poll_init(&main_loop_, &udev_drm_event_loop_, + udev_monitor_get_fd(udev_monitor_))) { + ELINUX_LOG(ERROR) << "Failed to create udev drm event loop."; + return false; + } + + udev_drm_event_loop_.data = this; + + if (uv_poll_start(&udev_drm_event_loop_, + UV_READABLE | UV_DISCONNECT | UV_PRIORITIZED, + OnUdevDrmEvent)) { + ELINUX_LOG(ERROR) << "Failed to listen for udev drm event."; + return false; + } +#endif // #ifdef USE_LIBSYSTEMD if (udev_monitor_enable_receiving(udev_monitor_) < 0) { ELINUX_LOG(ERROR) << "Failed to enable udev monitor receiving."; @@ -310,6 +371,7 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { return true; } +#ifdef USE_LIBSYSTEMD static int OnUdevDrmEvent(sd_event_source* source, int fd, uint32_t revents, @@ -320,6 +382,15 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { ELINUX_LOG(ERROR) << "Failed to receive udev device."; return -1; } +#else + static void OnUdevDrmEvent(uv_poll_t* handle, int status, int events) { + auto self = reinterpret_cast(handle->data); + auto device = udev_monitor_receive_device(self->udev_monitor_); + if (!device) { + ELINUX_LOG(ERROR) << "Failed to receive udev device."; + return; + } +#endif if (self->IsUdevEventHotplug(*device) && self->native_window_->ConfigureDisplay(self->current_rotation_)) { @@ -343,7 +414,10 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } udev_device_unref(device); + +#ifdef USE_LIBSYSTEMD return 0; +#endif } bool IsUdevEventHotplug(udev_device& device) { @@ -368,6 +442,7 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { return std::strcmp(value, kPropertyOn) == 0; } +#ifdef USE_LIBSYSTEMD static int OnLibinputEvent(sd_event_source* source, int fd, uint32_t revents, @@ -378,6 +453,15 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { ELINUX_LOG(ERROR) << "Failed to dispatch libinput events."; return -ret; } +#else + static void OnLibinputEvent(uv_poll_t* handle, int uv_status, int uv_events) { + auto self = reinterpret_cast(handle->data); + auto ret = libinput_dispatch(self->libinput_); + if (ret < 0) { + ELINUX_LOG(ERROR) << "Failed to dispatch libinput events."; + return; + } +#endif auto previous_pointer_x = self->pointer_x_; auto previous_pointer_y = self->pointer_y_; @@ -435,7 +519,9 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { self->native_window_->MoveCursor(self->pointer_x_, self->pointer_y_); } +#ifdef USE_LIBSYSTEMD return 0; +#endif } void OnDeviceAdded(libinput_event* event) { @@ -716,15 +802,22 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { bool display_valid_; bool is_pending_cursor_add_event_; - sd_event* libinput_event_loop_; libinput* libinput_; std::unordered_map> libinput_devices_; int libinput_pointer_devices_ = 0; - sd_event* udev_drm_event_loop_ = nullptr; udev_monitor* udev_monitor_ = nullptr; std::optional drm_device_id_; + +#ifdef USE_LIBSYSTEMD + sd_event* libinput_event_loop_; + sd_event* udev_drm_event_loop_ = nullptr; +#else + uv_loop_t main_loop_; + uv_poll_t libinput_event_loop_; + uv_poll_t udev_drm_event_loop_; +#endif }; } // namespace flutter From 30b3d4aa78f5a14714caabf755f69a6afa9602a1 Mon Sep 17 00:00:00 2001 From: Anton Sakhon <58438890+Kofhein@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:35:58 +0300 Subject: [PATCH 06/14] Added marshalling to main thread messages sent from native side (#422) * Added marshalling to main thread messages sent from native side --------- Co-authored-by: Anton Sakhon --- AUTHORS | 1 + .../client_wrapper/core_implementations.cc | 13 ++++--- .../common/public/flutter_messenger.h | 3 +- .../platform/linux_embedded/flutter_elinux.cc | 34 +++++++++++++------ 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/AUTHORS b/AUTHORS index 368deab2..85298807 100644 --- a/AUTHORS +++ b/AUTHORS @@ -16,3 +16,4 @@ Stanislav Shmarov Sebastian Urban Ómar Högni Guðmarsson Athaariq Ardhiansyah +Anton Sakhon diff --git a/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc b/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc index 3aae66ca..8110a0c3 100644 --- a/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc +++ b/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc @@ -66,6 +66,11 @@ BinaryMessengerImpl::BinaryMessengerImpl( BinaryMessengerImpl::~BinaryMessengerImpl() = default; +void CaptureCleaner(void* lambda) { + auto& cleanup = *reinterpret_cast*>(lambda); + cleanup(); +} + void BinaryMessengerImpl::Send(const std::string& channel, const uint8_t* message, size_t message_size, @@ -89,10 +94,10 @@ void BinaryMessengerImpl::Send(const std::string& channel, }; bool result = FlutterDesktopMessengerSendWithReply( messenger_, channel.c_str(), message, message_size, message_reply, - captures); - if (!result) { - delete captures; - } + captures, [](void* captures_data) { + auto captures = reinterpret_cast(captures_data); + delete captures; + }); } void BinaryMessengerImpl::SetMessageHandler(const std::string& channel, diff --git a/src/flutter/shell/platform/common/public/flutter_messenger.h b/src/flutter/shell/platform/common/public/flutter_messenger.h index 87c33beb..bb182cdf 100644 --- a/src/flutter/shell/platform/common/public/flutter_messenger.h +++ b/src/flutter/shell/platform/common/public/flutter_messenger.h @@ -63,7 +63,8 @@ FLUTTER_EXPORT bool FlutterDesktopMessengerSendWithReply( const uint8_t* message, const size_t message_size, const FlutterDesktopBinaryReply reply, - void* user_data); + void* user_data, + void (*cleanup)(void* captures_data)); // Sends a reply to a FlutterDesktopMessage for the given response handle. // diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc b/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc index d3c3dee3..fd3754a4 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc @@ -199,22 +199,36 @@ void FlutterDesktopPluginRegistrarSetDestructionHandler( registrar->engine->SetPluginRegistrarDestructionCallback(callback); } -bool FlutterDesktopMessengerSendWithReply(FlutterDesktopMessengerRef messenger, - const char* channel, - const uint8_t* message, - const size_t message_size, - const FlutterDesktopBinaryReply reply, - void* user_data) { - return messenger->GetEngine()->SendPlatformMessage( - channel, message, message_size, reply, user_data); +bool FlutterDesktopMessengerSendWithReply( + FlutterDesktopMessengerRef messenger, + const char* channel, + const uint8_t* message, + const size_t message_size, + const FlutterDesktopBinaryReply reply, + void* user_data, + void (*cleanup)(void* captures_data)) { + // As we pass data to lambda and it's pointers we need to make sure that we + // send valid data + std::string channel_copy(channel); + std::vector message_copy(message, message + message_size); + + messenger->GetEngine()->task_runner()->PostTask([=]() { + if (!messenger->GetEngine()->SendPlatformMessage( + channel_copy.c_str(), message_copy.data(), message_copy.size(), + reply, user_data) && + user_data) { + cleanup(user_data); + } + }); + return true; } bool FlutterDesktopMessengerSend(FlutterDesktopMessengerRef messenger, const char* channel, const uint8_t* message, const size_t message_size) { - return FlutterDesktopMessengerSendWithReply(messenger, channel, message, - message_size, nullptr, nullptr); + return FlutterDesktopMessengerSendWithReply( + messenger, channel, message, message_size, nullptr, nullptr, nullptr); } void FlutterDesktopMessengerSendResponse( From 63b57ca74991ce6782736a1adb8b63928797272f Mon Sep 17 00:00:00 2001 From: Anton Sakhon <58438890+Kofhein@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:47:21 +0300 Subject: [PATCH 07/14] Removed unused variable (#423) --- .../platform/common/client_wrapper/core_implementations.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc b/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc index 8110a0c3..983feb3e 100644 --- a/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc +++ b/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc @@ -92,7 +92,8 @@ void BinaryMessengerImpl::Send(const std::string& channel, captures->reply(data, data_size); delete captures; }; - bool result = FlutterDesktopMessengerSendWithReply( + + FlutterDesktopMessengerSendWithReply( messenger_, channel.c_str(), message, message_size, message_reply, captures, [](void* captures_data) { auto captures = reinterpret_cast(captures_data); From 9dc1156ca709a54ea07ce5873f8d7f6357e98c9c Mon Sep 17 00:00:00 2001 From: barribarrier <98932277+barribarrier@users.noreply.github.com> Date: Fri, 20 Sep 2024 07:17:30 -0400 Subject: [PATCH 08/14] Specify DRM connector with FLUTTER_DRM_CONNECTOR (#425) * Specify DRM connector with FLUTTER_DRM_CONNECTOR * Update AUTHORS --------- Co-authored-by: barri --- AUTHORS | 1 + .../window/native_window_drm.cc | 76 +++++++++++++++++++ .../linux_embedded/window/native_window_drm.h | 4 + 3 files changed, 81 insertions(+) diff --git a/AUTHORS b/AUTHORS index 85298807..6a816263 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,3 +17,4 @@ Sebastian Urban Ómar Högni Guðmarsson Athaariq Ardhiansyah Anton Sakhon +Bari Rao diff --git a/src/flutter/shell/platform/linux_embedded/window/native_window_drm.cc b/src/flutter/shell/platform/linux_embedded/window/native_window_drm.cc index 59a29021..73023fd2 100644 --- a/src/flutter/shell/platform/linux_embedded/window/native_window_drm.cc +++ b/src/flutter/shell/platform/linux_embedded/window/native_window_drm.cc @@ -14,6 +14,31 @@ #include "flutter/shell/platform/linux_embedded/surface/cursor_data.h" namespace flutter { +namespace { +constexpr char kFlutterDrmConnectorEnvironmentKey[] = "FLUTTER_DRM_CONNECTOR"; +static const std::unordered_map connector_names = { + {DRM_MODE_CONNECTOR_Unknown, "Unknown"}, + {DRM_MODE_CONNECTOR_VGA, "VGA"}, + {DRM_MODE_CONNECTOR_DVII, "DVI-I"}, + {DRM_MODE_CONNECTOR_DVID, "DVI-D"}, + {DRM_MODE_CONNECTOR_DVIA, "DVI-A"}, + {DRM_MODE_CONNECTOR_Composite, "Composite"}, + {DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO"}, + {DRM_MODE_CONNECTOR_LVDS, "LVDS"}, + {DRM_MODE_CONNECTOR_Component, "Component"}, + {DRM_MODE_CONNECTOR_9PinDIN, "DIN"}, + {DRM_MODE_CONNECTOR_DisplayPort, "DP"}, + {DRM_MODE_CONNECTOR_HDMIA, "HDMI-A"}, + {DRM_MODE_CONNECTOR_HDMIB, "HDMI-B"}, + {DRM_MODE_CONNECTOR_TV, "TV"}, + {DRM_MODE_CONNECTOR_eDP, "eDP"}, + {DRM_MODE_CONNECTOR_VIRTUAL, "Virtual"}, + {DRM_MODE_CONNECTOR_DSI, "DSI"}, + {DRM_MODE_CONNECTOR_DPI, "DPI"}, + {DRM_MODE_CONNECTOR_WRITEBACK, "Writeback"}, + {DRM_MODE_CONNECTOR_SPI, "SPI"}, + {DRM_MODE_CONNECTOR_USB, "USB"}}; +} // namespace NativeWindowDrm::NativeWindowDrm(const char* device_filename, const uint16_t rotation, @@ -104,11 +129,62 @@ bool NativeWindowDrm::ConfigureDisplay(const uint16_t rotation) { return true; } +std::string NativeWindowDrm::GetConnectorName(uint32_t connector_type, + uint32_t connector_type_id) { + auto it = connector_names.find(connector_type); + std::string name = it != connector_names.end() ? it->second : "Unknown"; + return name + "-" + std::to_string(connector_type_id); +} + +drmModeConnectorPtr NativeWindowDrm::GetConnectorByName( + drmModeResPtr resources, + const char* connector_name) { + for (int i = 0; i < resources->count_connectors; i++) { + auto connector = drmModeGetConnector(drm_device_, resources->connectors[i]); + if (!connector) { + continue; + } + auto other_connector_name = GetConnectorName(connector->connector_type, + connector->connector_type_id); + if (connector_name == other_connector_name) { + return connector; + } + drmModeFreeConnector(connector); + } + return nullptr; +} + drmModeConnectorPtr NativeWindowDrm::FindConnector(drmModeResPtr resources) { + auto connector_name = std::getenv(kFlutterDrmConnectorEnvironmentKey); + if (connector_name && connector_name[0] != '\0') { + auto connector = GetConnectorByName(resources, connector_name); + if (!connector) { + ELINUX_LOG(ERROR) << "Couldn't find connector with name " + << connector_name; + return nullptr; + } + + if (connector->connection == DRM_MODE_CONNECTED) { + ELINUX_LOG(DEBUG) << "Using connector " << connector_name; + return connector; + } else { + ELINUX_LOG(ERROR) << "Connector " << connector_name + << " is not connected."; + drmModeFreeConnector(connector); + return nullptr; + } + } + for (int i = 0; i < resources->count_connectors; i++) { auto connector = drmModeGetConnector(drm_device_, resources->connectors[i]); + if (!connector) { + continue; + } // pick the first connected connector if (connector->connection == DRM_MODE_CONNECTED) { + auto other_connetor_name = GetConnectorName(connector->connector_type, + connector->connector_type_id); + ELINUX_LOG(DEBUG) << "Using connector " << other_connetor_name; return connector; } drmModeFreeConnector(connector); diff --git a/src/flutter/shell/platform/linux_embedded/window/native_window_drm.h b/src/flutter/shell/platform/linux_embedded/window/native_window_drm.h index bae9ec06..3d72f7ac 100644 --- a/src/flutter/shell/platform/linux_embedded/window/native_window_drm.h +++ b/src/flutter/shell/platform/linux_embedded/window/native_window_drm.h @@ -39,6 +39,10 @@ class NativeWindowDrm : public NativeWindow { bool enable_impeller) = 0; protected: + std::string GetConnectorName(uint32_t connector_type, + uint32_t connector_type_id); + drmModeConnectorPtr GetConnectorByName(drmModeResPtr resources, + const char* connector_name); drmModeConnectorPtr FindConnector(drmModeResPtr resources); drmModeEncoder* FindEncoder(drmModeRes* resources, From 9d0ff07b5e1cccbead808ddff0f13f1c5ceeae52 Mon Sep 17 00:00:00 2001 From: barribarrier <98932277+barribarrier@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:06:07 -0400 Subject: [PATCH 09/14] Destroy textures on raster thread (#427) * Destroy textures on raster thread Signed-off-by: Bari Rao * Fix format Signed-off-by: Bari Rao --------- Signed-off-by: Bari Rao --- src/flutter/fml/closure.h | 77 +++++++++++++++++++ src/flutter/fml/macros.h | 44 +++++++++++ .../client_wrapper/core_implementations.cc | 27 ++++++- .../include/flutter/texture_registrar.h | 9 ++- .../client_wrapper/texture_registrar_impl.h | 4 + .../common/public/flutter_texture_registrar.h | 12 +-- .../platform/linux_embedded/flutter_elinux.cc | 15 +++- .../linux_embedded/flutter_elinux_engine.cc | 20 +++++ .../linux_embedded/flutter_elinux_engine.h | 3 + .../flutter_elinux_texture_registrar.cc | 30 +++++--- .../flutter_elinux_texture_registrar.h | 4 +- 11 files changed, 219 insertions(+), 26 deletions(-) create mode 100644 src/flutter/fml/closure.h create mode 100644 src/flutter/fml/macros.h diff --git a/src/flutter/fml/closure.h b/src/flutter/fml/closure.h new file mode 100644 index 00000000..264a95f7 --- /dev/null +++ b/src/flutter/fml/closure.h @@ -0,0 +1,77 @@ +// 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. + +#ifndef FLUTTER_FML_CLOSURE_H_ +#define FLUTTER_FML_CLOSURE_H_ + +#include + +#include "flutter/fml/macros.h" + +namespace fml { + +using closure = std::function; + +//------------------------------------------------------------------------------ +/// @brief Wraps a closure that is invoked in the destructor unless +/// released by the caller. +/// +/// This is especially useful in dealing with APIs that return a +/// resource by accepting ownership of a sub-resource and a closure +/// that releases that resource. When such APIs are chained, each +/// link in the chain must check that the next member in the chain +/// has accepted the resource. If not, it must invoke the closure +/// eagerly. Not doing this results in a resource leak in the +/// erroneous case. Using this wrapper, the closure can be released +/// once the next call in the chain has successfully accepted +/// ownership of the resource. If not, the closure gets invoked +/// automatically at the end of the scope. This covers the cases +/// where there are early returns as well. +/// +class ScopedCleanupClosure final { + public: + ScopedCleanupClosure() = default; + + ScopedCleanupClosure(ScopedCleanupClosure&& other) { + closure_ = other.Release(); + } + + ScopedCleanupClosure& operator=(ScopedCleanupClosure&& other) { + closure_ = other.Release(); + return *this; + } + + explicit ScopedCleanupClosure(const fml::closure& closure) + : closure_(closure) {} + + ~ScopedCleanupClosure() { Reset(); } + + fml::closure SetClosure(const fml::closure& closure) { + auto old_closure = closure_; + closure_ = closure; + return old_closure; + } + + fml::closure Release() { + fml::closure closure = closure_; + closure_ = nullptr; + return closure; + } + + void Reset() { + if (closure_) { + closure_(); + closure_ = nullptr; + } + } + + private: + fml::closure closure_; + + FML_DISALLOW_COPY_AND_ASSIGN(ScopedCleanupClosure); +}; + +} // namespace fml + +#endif // FLUTTER_FML_CLOSURE_H_ diff --git a/src/flutter/fml/macros.h b/src/flutter/fml/macros.h new file mode 100644 index 00000000..7de4ddf1 --- /dev/null +++ b/src/flutter/fml/macros.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef FLUTTER_FML_MACROS_H_ +#define FLUTTER_FML_MACROS_H_ + +#ifndef FML_USED_ON_EMBEDDER + +#define FML_EMBEDDER_ONLY [[deprecated]] + +#else // FML_USED_ON_EMBEDDER + +#define FML_EMBEDDER_ONLY + +#endif // FML_USED_ON_EMBEDDER + +#define FML_DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete + +#define FML_DISALLOW_ASSIGN(TypeName) \ + TypeName& operator=(const TypeName&) = delete + +#define FML_DISALLOW_MOVE(TypeName) \ + TypeName(TypeName&&) = delete; \ + TypeName& operator=(TypeName&&) = delete + +#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete + +#define FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName(TypeName&&) = delete; \ + TypeName& operator=(const TypeName&) = delete; \ + TypeName& operator=(TypeName&&) = delete + +#define FML_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName) + +#define FML_FRIEND_TEST(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test + +#endif // FLUTTER_FML_MACROS_H_ diff --git a/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc b/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc index 983feb3e..ec0d6dd9 100644 --- a/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc +++ b/src/flutter/shell/platform/common/client_wrapper/core_implementations.cc @@ -210,9 +210,32 @@ bool TextureRegistrarImpl::MarkTextureFrameAvailable(int64_t texture_id) { texture_registrar_ref_, texture_id); } +void TextureRegistrarImpl::UnregisterTexture(int64_t texture_id, + std::function callback) { + if (callback == nullptr) { + FlutterDesktopTextureRegistrarUnregisterExternalTexture( + texture_registrar_ref_, texture_id, nullptr, nullptr); + return; + } + + struct Captures { + std::function callback; + }; + auto captures = new Captures(); + captures->callback = std::move(callback); + FlutterDesktopTextureRegistrarUnregisterExternalTexture( + texture_registrar_ref_, texture_id, + [](void* opaque) { + auto captures = reinterpret_cast(opaque); + captures->callback(); + delete captures; + }, + captures); +} + bool TextureRegistrarImpl::UnregisterTexture(int64_t texture_id) { - return FlutterDesktopTextureRegistrarUnregisterExternalTexture( - texture_registrar_ref_, texture_id); + UnregisterTexture(texture_id, nullptr); + return true; } } // namespace flutter diff --git a/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h b/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h index 0777e528..9b13025f 100644 --- a/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h +++ b/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h @@ -128,8 +128,13 @@ class TextureRegistrar { // the callback that was provided upon creating the texture. virtual bool MarkTextureFrameAvailable(int64_t texture_id) = 0; - // Unregisters an existing Texture object. - // Textures must not be unregistered while they're in use. + // Asynchronously unregisters an existing texture object. + // Upon completion, the optional |callback| gets invoked. + virtual void UnregisterTexture(int64_t texture_id, + std::function callback) = 0; + + // Unregisters an existing texture object. + // DEPRECATED: Use UnregisterTexture(texture_id, optional_callback) instead. virtual bool UnregisterTexture(int64_t texture_id) = 0; }; diff --git a/src/flutter/shell/platform/common/client_wrapper/texture_registrar_impl.h b/src/flutter/shell/platform/common/client_wrapper/texture_registrar_impl.h index df51f6dd..bd01839d 100644 --- a/src/flutter/shell/platform/common/client_wrapper/texture_registrar_impl.h +++ b/src/flutter/shell/platform/common/client_wrapper/texture_registrar_impl.h @@ -27,6 +27,10 @@ class TextureRegistrarImpl : public TextureRegistrar { // |flutter::TextureRegistrar| bool MarkTextureFrameAvailable(int64_t texture_id) override; + // |flutter::TextureRegistrar| + void UnregisterTexture(int64_t texture_id, + std::function callback) override; + // |flutter::TextureRegistrar| bool UnregisterTexture(int64_t texture_id) override; diff --git a/src/flutter/shell/platform/common/public/flutter_texture_registrar.h b/src/flutter/shell/platform/common/public/flutter_texture_registrar.h index fb564382..a56c3107 100644 --- a/src/flutter/shell/platform/common/public/flutter_texture_registrar.h +++ b/src/flutter/shell/platform/common/public/flutter_texture_registrar.h @@ -195,13 +195,15 @@ FLUTTER_EXPORT int64_t FlutterDesktopTextureRegistrarRegisterExternalTexture( FlutterDesktopTextureRegistrarRef texture_registrar, const FlutterDesktopTextureInfo* info); -// Unregisters an existing texture from the Flutter engine for a |texture_id|. -// Returns true on success or false if the specified texture doesn't exist. +// Asynchronously unregisters the texture identified by |texture_id| from the +// Flutter engine. +// An optional |callback| gets invoked upon completion. // This function can be called from any thread. -// However, textures must not be unregistered while they're in use. -FLUTTER_EXPORT bool FlutterDesktopTextureRegistrarUnregisterExternalTexture( +FLUTTER_EXPORT void FlutterDesktopTextureRegistrarUnregisterExternalTexture( FlutterDesktopTextureRegistrarRef texture_registrar, - int64_t texture_id); + int64_t texture_id, + void (*callback)(void* user_data), + void* user_data); // Marks that a new texture frame is available for a given |texture_id|. // Returns true on success or false if the specified texture doesn't exist. diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc b/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc index fd3754a4..2d3587f3 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux.cc @@ -284,11 +284,18 @@ int64_t FlutterDesktopTextureRegistrarRegisterExternalTexture( ->RegisterTexture(texture_info); } -bool FlutterDesktopTextureRegistrarUnregisterExternalTexture( +void FlutterDesktopTextureRegistrarUnregisterExternalTexture( FlutterDesktopTextureRegistrarRef texture_registrar, - int64_t texture_id) { - return TextureRegistrarFromHandle(texture_registrar) - ->UnregisterTexture(texture_id); + int64_t texture_id, + void (*callback)(void* user_data), + void* user_data) { + auto registrar = TextureRegistrarFromHandle(texture_registrar); + if (callback) { + registrar->UnregisterTexture( + texture_id, [callback, user_data]() { callback(user_data); }); + return; + } + registrar->UnregisterTexture(texture_id); } bool FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable( diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc index 9e49c9e4..c09bca5e 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc @@ -425,6 +425,26 @@ bool FlutterELinuxEngine::MarkExternalTextureFrameAvailable( engine_, texture_id) == kSuccess); } +bool FlutterELinuxEngine::PostRasterThreadTask(fml::closure callback) { + struct Captures { + fml::closure callback; + }; + auto captures = new Captures(); + captures->callback = std::move(callback); + if (embedder_api_.PostRenderThreadTask( + engine_, + [](void* opaque) { + auto captures = reinterpret_cast(opaque); + captures->callback(); + delete captures; + }, + captures) == kSuccess) { + return true; + } + delete captures; + return false; +} + void FlutterELinuxEngine::OnVsync(uint64_t last_frame_time_nanos, uint64_t vsync_interval_time_nanos) { uint64_t current_time_nanos = embedder_api_.GetCurrentTime(); diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h index b705941e..4bcc7f10 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h @@ -120,6 +120,9 @@ class FlutterELinuxEngine { // given |texture_id|. bool MarkExternalTextureFrameAvailable(int64_t texture_id); + // Posts the given callback onto the raster thread. + bool PostRasterThreadTask(fml::closure callback); + // Notifies the engine about the vsync event. void OnVsync(uint64_t last_frame_time_nanos, uint64_t vsync_interval_time_nanos); diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.cc b/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.cc index be4899fe..66a603c3 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.cc +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.cc @@ -72,20 +72,28 @@ int64_t FlutterELinuxTextureRegistrar::EmplaceTexture( return texture_id; } -bool FlutterELinuxTextureRegistrar::UnregisterTexture(int64_t texture_id) { - { - std::lock_guard lock(map_mutex_); - auto it = textures_.find(texture_id); - if (it == textures_.end()) { - return false; - } - textures_.erase(it); - } - +void FlutterELinuxTextureRegistrar::UnregisterTexture(int64_t texture_id, + fml::closure callback) { engine_->task_runner()->RunNowOrPostTask([engine = engine_, texture_id]() { engine->UnregisterExternalTexture(texture_id); }); - return true; + + bool posted = engine_->PostRasterThreadTask([this, texture_id, callback]() { + { + std::lock_guard lock(map_mutex_); + auto it = textures_.find(texture_id); + if (it != textures_.end()) { + textures_.erase(it); + } + } + if (callback) { + callback(); + } + }); + + if (!posted && callback) { + callback(); + } } bool FlutterELinuxTextureRegistrar::MarkTextureFrameAvailable( diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h b/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h index 7ad57a2f..8770ad57 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h @@ -9,6 +9,7 @@ #include #include +#include "flutter/fml/closure.h" #include "flutter/shell/platform/common/public/flutter_texture_registrar.h" #include "flutter/shell/platform/linux_embedded/external_texture.h" @@ -28,8 +29,7 @@ class FlutterELinuxTextureRegistrar { int64_t RegisterTexture(const FlutterDesktopTextureInfo* texture_info); // Attempts to unregister the texture identified by |texture_id|. - // Returns true if the texture was successfully unregistered. - bool UnregisterTexture(int64_t texture_id); + void UnregisterTexture(int64_t texture_id, fml::closure callback = nullptr); // Notifies the engine about a new frame being available. // Returns true on success. From a938f2b2064bb25b58c5207f6f5c3dfb9972fadf Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Wed, 30 Oct 2024 21:35:12 +1100 Subject: [PATCH 10/14] Ensure FlutterDesktopViewControllerState declares default destructor (#428) types containing std::unique_ptr to incomplete types require a destructor to be defined as the size is unavailable, otherwise the following error is raised at compile time: /usr/lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/unique_ptr.h:97:16: error: invalid application of 'sizeof' to an incomplete type 'flutter::FlutterELinuxView' 91 | _GLIBCXX23_CONSTEXPR 92 | void 93 | operator()(_Tp* __ptr) const | `- note: in instantiation of member function 'std::default_delete::operator()' requested here 94 | { 95 | static_assert(!is_void<_Tp>::value, 96 | "can't delete pointer to incomplete type"); 97 | static_assert(sizeof(_Tp)>0, | `- error: invalid application of 'sizeof' to an incomplete type 'flutter::FlutterELinuxView' 98 | "can't delete pointer to incomplete type"); 99 | delete __ptr; Signed-off-by: Luke Howard --- .../shell/platform/linux_embedded/flutter_elinux_state.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h b/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h index 8248f7e8..aea43c4c 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h @@ -34,6 +34,8 @@ struct FlutterELinuxView; struct FlutterDesktopViewControllerState { // The view that backs this state object. std::unique_ptr view; + + ~FlutterDesktopViewControllerState() = default; }; // Wrapper to distinguish the plugin registrar ref from the engine ref given out From 679d3612e4a6a65ade648a98581a48c4bd5713e4 Mon Sep 17 00:00:00 2001 From: Hidenori Date: Sat, 14 Dec 2024 05:10:34 +0900 Subject: [PATCH 11/14] linux_embedded: fix github actions' failure (#431) This change fixes the CI failure due to code format warnings. Signed-off-by: Hidenori Matsubayashi --- .../linux_embedded/flutter_elinux_engine.cc | 10 ++++------ .../plugins/platform_views_plugin.cc | 5 ++--- .../linux_embedded/surface/context_egl.cc | 20 +++++++++---------- .../linux_embedded/window/elinux_window_drm.h | 16 ++++----------- .../window/elinux_window_wayland.cc | 10 +++++----- .../linux_embedded/window/native_window.h | 2 +- 6 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc index c09bca5e..45a922e2 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.cc @@ -371,8 +371,7 @@ void FlutterELinuxEngine::HandlePlatformMessage( auto message = ConvertToDesktopMessage(*engine_message); - message_dispatcher_->HandleMessage( - message, [this] {}, [this] {}); + message_dispatcher_->HandleMessage(message, [this] {}, [this] {}); } void FlutterELinuxEngine::ReloadSystemFonts() { @@ -398,10 +397,9 @@ void FlutterELinuxEngine::SendSystemLocales() { // Convert the locale list to the locale pointer list that must be provided. std::vector flutter_locale_list; flutter_locale_list.reserve(flutter_locales.size()); - std::transform( - flutter_locales.begin(), flutter_locales.end(), - std::back_inserter(flutter_locale_list), - [](const auto& arg) -> const auto* { return &arg; }); + std::transform(flutter_locales.begin(), flutter_locales.end(), + std::back_inserter(flutter_locale_list), + [](const auto& arg) -> const auto* { return &arg; }); auto result = embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(), flutter_locale_list.size()); if (result != kSuccess) { diff --git a/src/flutter/shell/platform/linux_embedded/plugins/platform_views_plugin.cc b/src/flutter/shell/platform/linux_embedded/plugins/platform_views_plugin.cc index 81b29462..fb2a513e 100644 --- a/src/flutter/shell/platform/linux_embedded/plugins/platform_views_plugin.cc +++ b/src/flutter/shell/platform/linux_embedded/plugins/platform_views_plugin.cc @@ -250,9 +250,8 @@ void PlatformViewsPlugin::PlatformViewsOffset( auto view_id = LookupEncodableMap(arguments, kIdKey); auto view_top = LookupEncodableMap(arguments, kTopKey); auto view_left = LookupEncodableMap(arguments, kLeftKey); - ELINUX_LOG(DEBUG) << "Offset the platform view: " - << "id = " << view_id << ", top = " << view_top - << ", left = " << view_left; + ELINUX_LOG(DEBUG) << "Offset the platform view: " << "id = " << view_id + << ", top = " << view_top << ", left = " << view_left; if (platform_views_.find(view_id) == platform_views_.end()) { result->Error("Couldn't find the view id in the arguments"); return; diff --git a/src/flutter/shell/platform/linux_embedded/surface/context_egl.cc b/src/flutter/shell/platform/linux_embedded/surface/context_egl.cc index ac87ab10..855687c2 100644 --- a/src/flutter/shell/platform/linux_embedded/surface/context_egl.cc +++ b/src/flutter/shell/platform/linux_embedded/surface/context_egl.cc @@ -15,7 +15,7 @@ ContextEgl::ContextEgl(std::unique_ptr environment, : environment_(std::move(environment)), config_(nullptr) { EGLint config_count = 0; const EGLint attribs[] = { - // clang-format off + // clang-format off EGL_SURFACE_TYPE, egl_surface_type, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, @@ -27,10 +27,10 @@ ContextEgl::ContextEgl(std::unique_ptr environment, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_NONE - // clang-format on + // clang-format on }; const EGLint impeller_config_attributes[] = { - // clang-format off + // clang-format off EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, @@ -42,10 +42,10 @@ ContextEgl::ContextEgl(std::unique_ptr environment, EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES, 4, EGL_NONE - // clang-format on + // clang-format on }; const EGLint impeller_config_attributes_no_msaa[] = { - // clang-format off + // clang-format off EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, @@ -55,7 +55,7 @@ ContextEgl::ContextEgl(std::unique_ptr environment, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8, EGL_NONE - // clang-format on + // clang-format on }; if (enable_impeller) { @@ -136,8 +136,8 @@ std::unique_ptr ContextEgl::CreateOffscreenSurface( EGLSurface surface = eglCreatePbufferSurface(environment_->Display(), config_, attribs); if (surface == EGL_NO_SURFACE) { - ELINUX_LOG(WARNING) << "Failed to create EGL off-screen surface." - << "(" << get_egl_error_cause() << ")"; + ELINUX_LOG(WARNING) << "Failed to create EGL off-screen surface." << "(" + << get_egl_error_cause() << ")"; } #else // eglCreatePbufferSurface isn't supported on both Wayland and GBM. @@ -146,8 +146,8 @@ std::unique_ptr ContextEgl::CreateOffscreenSurface( EGLSurface surface = eglCreateWindowSurface( environment_->Display(), config_, window->WindowOffscreen(), attribs); if (surface == EGL_NO_SURFACE) { - ELINUX_LOG(WARNING) << "Failed to create EGL off-screen surface." - << "(" << get_egl_error_cause() << ")"; + ELINUX_LOG(WARNING) << "Failed to create EGL off-screen surface." << "(" + << get_egl_error_cause() << ")"; } #endif return std::make_unique(surface, environment_->Display(), diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h b/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h index 0bbb883a..fa1fb9e9 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_drm.h @@ -228,14 +228,10 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } // |FlutterWindowBindingHandler| - uint16_t GetRotationDegree() const override { - return current_rotation_; - } + uint16_t GetRotationDegree() const override { return current_rotation_; } // |FlutterWindowBindingHandler| - double GetDpiScale() override { - return current_scale_; - } + double GetDpiScale() override { return current_scale_; } // |FlutterWindowBindingHandler| PhysicalWindowBounds GetPhysicalWindowBounds() override { @@ -243,9 +239,7 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } // |FlutterWindowBindingHandler| - int32_t GetFrameRate() override { - return 60000; - } + int32_t GetFrameRate() override { return 60000; } // |FlutterWindowBindingHandler| void UpdateFlutterCursor(const std::string& cursor_name) override { @@ -260,9 +254,7 @@ class ELinuxWindowDrm : public ELinuxWindow, public WindowBindingHandler { } // |FlutterWindowBindingHandler| - std::string GetClipboardData() override { - return clipboard_data_; - } + std::string GetClipboardData() override { return clipboard_data_; } // |FlutterWindowBindingHandler| void SetClipboardData(const std::string& data) override { diff --git a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc index 5bd8e0c2..6467bf94 100644 --- a/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc +++ b/src/flutter/shell/platform/linux_embedded/window/elinux_window_wayland.cc @@ -697,11 +697,11 @@ const wl_output_listener ELinuxWindowWayland::kWlOutputListener = { self->request_redraw_ = true; } if (self->view_properties_.height > height) { - ELINUX_LOG(WARNING) << "Requested height size(" - << self->view_properties_.height << ") " - << "is larger than display size(" << height - << ")" - ", clipping"; + ELINUX_LOG(WARNING) + << "Requested height size(" << self->view_properties_.height + << ") " << "is larger than display size(" << height + << ")" + ", clipping"; self->view_properties_.height = height; self->request_redraw_ = true; } diff --git a/src/flutter/shell/platform/linux_embedded/window/native_window.h b/src/flutter/shell/platform/linux_embedded/window/native_window.h index a224b79a..57c40362 100644 --- a/src/flutter/shell/platform/linux_embedded/window/native_window.h +++ b/src/flutter/shell/platform/linux_embedded/window/native_window.h @@ -56,7 +56,7 @@ class NativeWindow { // Swaps frame buffers. This API performs processing only for the DRM-GBM // backend. It is prepared to make the interface common. - virtual void SwapBuffers(){/* do nothing. */}; + virtual void SwapBuffers() { /* do nothing. */ }; protected: EGLNativeWindowType window_; From 61b90d507d68b1db2ec891bf06537def68fc3ab8 Mon Sep 17 00:00:00 2001 From: Hidenori Date: Sat, 14 Dec 2024 05:16:58 +0900 Subject: [PATCH 12/14] embedder: merge embedder.h from flutter/engine (#430) Merged from https://github.com/flutter/engine/commits/3.27.0/shell/platform/embedder/embedder.h Signed-off-by: Hidenori Matsubayashi --- .../shell/platform/embedder/embedder.h | 273 +++++++++++++++--- 1 file changed, 232 insertions(+), 41 deletions(-) diff --git a/src/flutter/shell/platform/embedder/embedder.h b/src/flutter/shell/platform/embedder/embedder.h index d26e2215..9e9b875a 100644 --- a/src/flutter/shell/platform/embedder/embedder.h +++ b/src/flutter/shell/platform/embedder/embedder.h @@ -162,6 +162,8 @@ typedef enum { kFlutterSemanticsActionMoveCursorBackwardByWord = 1 << 20, /// Replace the current text in the text field. kFlutterSemanticsActionSetText = 1 << 21, + /// Request that the respective focusable widget gain input focus. + kFlutterSemanticsActionFocus = 1 << 22, } FlutterSemanticsAction; /// The set of properties that may be associated with a semantics node. @@ -302,6 +304,9 @@ typedef enum { /// Specifies an OpenGL frame-buffer target type. Framebuffers are specified /// using the FlutterOpenGLFramebuffer struct. kFlutterOpenGLTargetTypeFramebuffer, + /// Specifies an OpenGL on-screen surface target type. Surfaces are specified + /// using the FlutterOpenGLSurface struct. + kFlutterOpenGLTargetTypeSurface, } FlutterOpenGLTargetType; /// A pixel format to be used for software rendering. @@ -385,9 +390,14 @@ typedef struct { } FlutterOpenGLTexture; typedef struct { - /// The target of the color attachment of the frame-buffer. For example, - /// GL_TEXTURE_2D or GL_RENDERBUFFER. In case of ambiguity when dealing with - /// Window bound frame-buffers, 0 may be used. + /// The format of the color attachment of the frame-buffer. For example, + /// GL_RGBA8. + /// + /// In case of ambiguity when dealing with Window bound frame-buffers, 0 may + /// be used. + /// + /// @bug This field is incorrectly named as "target" when it actually + /// refers to a format. uint32_t target; /// The name of the framebuffer. @@ -401,6 +411,62 @@ typedef struct { VoidCallback destruction_callback; } FlutterOpenGLFramebuffer; +typedef bool (*FlutterOpenGLSurfaceCallback)(void* /* user data */, + bool* /* opengl state changed */); + +typedef struct { + /// The size of this struct. Must be sizeof(FlutterOpenGLSurface). + size_t struct_size; + + /// User data to be passed to the make_current, clear_current and + /// destruction callbacks. + void* user_data; + + /// Callback invoked (on an engine-managed thread) that asks the embedder to + /// make the surface current. + /// + /// Should return true if the operation succeeded, false if the surface could + /// not be made current and rendering should be cancelled. + /// + /// The second parameter 'opengl state changed' should be set to true if + /// any OpenGL API state is different than before this callback was called. + /// In that case, Flutter will invalidate the internal OpenGL API state cache, + /// which is a somewhat expensive operation. + /// + /// @attention required. (non-null) + FlutterOpenGLSurfaceCallback make_current_callback; + + /// Callback invoked (on an engine-managed thread) when the current surface + /// can be cleared. + /// + /// Should return true if the operation succeeded, false if an error ocurred. + /// That error will be logged but otherwise not handled by the engine. + /// + /// The second parameter 'opengl state changed' is the same as with the + /// @ref make_current_callback. + /// + /// The embedder might clear the surface here after it was previously made + /// current. That's not required however, it's also possible to clear it in + /// the destruction callback. There's no way to signal OpenGL state + /// changes in the destruction callback though. + /// + /// @attention required. (non-null) + FlutterOpenGLSurfaceCallback clear_current_callback; + + /// Callback invoked (on an engine-managed thread) that asks the embedder to + /// collect the surface. + /// + /// @attention required. (non-null) + VoidCallback destruction_callback; + + /// The surface format. + /// + /// Allowed values: + /// - GL_RGBA8 + /// - GL_BGRA8_EXT + uint32_t format; +} FlutterOpenGLSurface; + typedef bool (*BoolCallback)(void* /* user data */); typedef FlutterTransformation (*TransformationCallback)(void* /* user data */); typedef uint32_t (*UIntCallback)(void* /* user data */); @@ -831,6 +897,86 @@ typedef struct { }; } FlutterRendererConfig; +/// Display refers to a graphics hardware system consisting of a framebuffer, +/// typically a monitor or a screen. This ID is unique per display and is +/// stable until the Flutter application restarts. +typedef uint64_t FlutterEngineDisplayId; + +typedef struct { + /// The size of this struct. Must be sizeof(FlutterWindowMetricsEvent). + size_t struct_size; + /// Physical width of the window. + size_t width; + /// Physical height of the window. + size_t height; + /// Scale factor for the physical screen. + double pixel_ratio; + /// Horizontal physical location of the left side of the window on the screen. + size_t left; + /// Vertical physical location of the top of the window on the screen. + size_t top; + /// Top inset of window. + double physical_view_inset_top; + /// Right inset of window. + double physical_view_inset_right; + /// Bottom inset of window. + double physical_view_inset_bottom; + /// Left inset of window. + double physical_view_inset_left; + /// The identifier of the display the view is rendering on. + FlutterEngineDisplayId display_id; + /// The view that this event is describing. + int64_t view_id; +} FlutterWindowMetricsEvent; + +typedef struct { + /// The size of this struct. + /// Must be sizeof(FlutterAddViewResult). + size_t struct_size; + + /// True if the add view operation succeeded. + bool added; + + /// The |FlutterAddViewInfo.user_data|. + void* user_data; +} FlutterAddViewResult; + +/// The callback invoked by the engine when the engine has attempted to add a +/// view. +/// +/// The |FlutterAddViewResult| is only guaranteed to be valid during this +/// callback. +typedef void (*FlutterAddViewCallback)(const FlutterAddViewResult* result); + +typedef struct { + /// The size of this struct. + /// Must be sizeof(FlutterAddViewInfo). + size_t struct_size; + + /// The identifier for the view to add. This must be unique. + FlutterViewId view_id; + + /// The view's properties. + /// + /// The metric's |view_id| must match this struct's |view_id|. + const FlutterWindowMetricsEvent* view_metrics; + + /// A baton that is not interpreted by the engine in any way. It will be given + /// back to the embedder in |add_view_callback|. Embedder resources may be + /// associated with this baton. + void* user_data; + + /// Called once the engine has attempted to add the view. This callback is + /// required. + /// + /// The embedder/app must not use the view until the callback is invoked with + /// an `added` value of `true`. + /// + /// This callback is invoked on an internal engine managed thread. Embedders + /// must re-thread if necessary. + FlutterAddViewCallback add_view_callback; +} FlutterAddViewInfo; + typedef struct { /// The size of this struct. /// Must be sizeof(FlutterRemoveViewResult). @@ -846,7 +992,8 @@ typedef struct { /// The callback invoked by the engine when the engine has attempted to remove /// a view. /// -/// The |FlutterRemoveViewResult| will be deallocated once the callback returns. +/// The |FlutterRemoveViewResult| is only guaranteed to be valid during this +/// callback. typedef void (*FlutterRemoveViewCallback)( const FlutterRemoveViewResult* /* result */); @@ -878,38 +1025,6 @@ typedef struct { FlutterRemoveViewCallback remove_view_callback; } FlutterRemoveViewInfo; -/// Display refers to a graphics hardware system consisting of a framebuffer, -/// typically a monitor or a screen. This ID is unique per display and is -/// stable until the Flutter application restarts. -typedef uint64_t FlutterEngineDisplayId; - -typedef struct { - /// The size of this struct. Must be sizeof(FlutterWindowMetricsEvent). - size_t struct_size; - /// Physical width of the window. - size_t width; - /// Physical height of the window. - size_t height; - /// Scale factor for the physical screen. - double pixel_ratio; - /// Horizontal physical location of the left side of the window on the screen. - size_t left; - /// Vertical physical location of the top of the window on the screen. - size_t top; - /// Top inset of window. - double physical_view_inset_top; - /// Right inset of window. - double physical_view_inset_right; - /// Bottom inset of window. - double physical_view_inset_bottom; - /// Left inset of window. - double physical_view_inset_left; - /// The identifier of the display the view is rendering on. - FlutterEngineDisplayId display_id; - /// The view that this event is describing. - int64_t view_id; -} FlutterWindowMetricsEvent; - /// The phase of the pointer event. typedef enum { kCancel, @@ -1482,7 +1597,7 @@ typedef void (*FlutterUpdateSemanticsCallback2)( /// An update to whether a message channel has a listener set or not. typedef struct { - // The size of the struct. Must be sizeof(FlutterChannelUpdate). + /// The size of the struct. Must be sizeof(FlutterChannelUpdate). size_t struct_size; /// The name of the channel. const char* channel; @@ -1563,6 +1678,9 @@ typedef struct { /// A framebuffer for Flutter to render into. The embedder must ensure that /// the framebuffer is complete. FlutterOpenGLFramebuffer framebuffer; + /// A surface for Flutter to render into. Basically a wrapper around + /// a closure that'll be called when the surface should be made current. + FlutterOpenGLSurface surface; }; } FlutterOpenGLBackingStore; @@ -1584,6 +1702,7 @@ typedef struct { } FlutterSoftwareBackingStore; typedef struct { + /// The size of this struct. Must be sizeof(FlutterSoftwareBackingStore2). size_t struct_size; /// A pointer to the raw bytes of the allocation described by this software /// backing store. @@ -1731,6 +1850,9 @@ typedef struct { size_t struct_size; /// The size of the render target the engine expects to render into. FlutterSize size; + /// The identifier for the view that the engine will use this backing store to + /// render into. + FlutterViewId view_id; } FlutterBackingStoreConfig; typedef enum { @@ -1754,6 +1876,7 @@ typedef struct { /// Contains additional information about the backing store provided /// during presentation to the embedder. typedef struct { + /// The size of this struct. Must be sizeof(FlutterBackingStorePresentInfo). size_t struct_size; /// The area of the backing store that contains Flutter contents. Pixels @@ -1844,18 +1967,26 @@ typedef struct { /// `FlutterBackingStore::struct_size` when specifying a new backing store to /// the engine. This only matters if the embedder expects to be used with /// engines older than the version whose headers it used during compilation. + /// + /// The callback should return true if the operation was successful. FlutterBackingStoreCreateCallback create_backing_store_callback; /// A callback invoked by the engine to release the backing store. The /// embedder may collect any resources associated with the backing store. + /// + /// The callback should return true if the operation was successful. FlutterBackingStoreCollectCallback collect_backing_store_callback; /// Callback invoked by the engine to composite the contents of each layer /// onto the implicit view. /// - /// DEPRECATED: Use |present_view_callback| to support multiple views. + /// DEPRECATED: Use `present_view_callback` to support multiple views. + /// If this callback is provided, `FlutterEngineAddView` and + /// `FlutterEngineRemoveView` should not be used. /// /// Only one of `present_layers_callback` and `present_view_callback` may be /// provided. Providing both is an error and engine initialization will /// terminate. + /// + /// The callback should return true if the operation was successful. FlutterLayersPresentCallback present_layers_callback; /// Avoid caching backing stores provided by this compositor. bool avoid_backing_store_cache; @@ -1865,6 +1996,8 @@ typedef struct { /// Only one of `present_layers_callback` and `present_view_callback` may be /// provided. Providing both is an error and engine initialization will /// terminate. + /// + /// The callback should return true if the operation was successful. FlutterPresentViewCallback present_view_callback; } FlutterCompositor; @@ -1905,7 +2038,7 @@ typedef const FlutterLocale* (*FlutterComputePlatformResolvedLocaleCallback)( size_t /* Number of locales*/); typedef struct { - /// This size of this struct. Must be sizeof(FlutterDisplay). + /// The size of this struct. Must be sizeof(FlutterEngineDisplay). size_t struct_size; FlutterEngineDisplayId display_id; @@ -2175,6 +2308,10 @@ typedef struct { /// `update_semantics_callback`, and /// `update_semantics_callback2` may be provided; the others /// should be set to null. + /// + /// This callback is incompatible with multiple views. If this + /// callback is provided, `FlutterEngineAddView` and + /// `FlutterEngineRemoveView` should not be used. FlutterUpdateSemanticsNodeCallback update_semantics_node_callback; /// The legacy callback invoked by the engine in order to give the embedder /// the chance to respond to updates to semantics custom actions from the Dart @@ -2191,6 +2328,10 @@ typedef struct { /// `update_semantics_callback`, and /// `update_semantics_callback2` may be provided; the others /// should be set to null. + /// + /// This callback is incompatible with multiple views. If this + /// callback is provided, `FlutterEngineAddView` and + /// `FlutterEngineRemoveView` should not be used. FlutterUpdateSemanticsCustomActionCallback update_semantics_custom_action_callback; /// Path to a directory used to store data that is cached across runs of a @@ -2340,6 +2481,10 @@ typedef struct { /// `update_semantics_callback`, and /// `update_semantics_callback2` may be provided; the others /// must be set to null. + /// + /// This callback is incompatible with multiple views. If this + /// callback is provided, `FlutterEngineAddView` and + /// `FlutterEngineRemoveView` should not be used. FlutterUpdateSemanticsCallback update_semantics_callback; /// The callback invoked by the engine in order to give the embedder the @@ -2505,12 +2650,50 @@ FLUTTER_EXPORT FlutterEngineResult FlutterEngineRunInitialized( FLUTTER_API_SYMBOL(FlutterEngine) engine); +//------------------------------------------------------------------------------ +/// @brief Adds a view. +/// +/// This is an asynchronous operation. The view should not be used +/// until the |info.add_view_callback| is invoked with an |added| +/// value of true. The embedder should prepare resources in advance +/// but be ready to clean up on failure. +/// +/// A frame is scheduled if the operation succeeds. +/// +/// The callback is invoked on a thread managed by the engine. The +/// embedder should re-thread if needed. +/// +/// Attempting to add the implicit view will fail and will return +/// kInvalidArguments. Attempting to add a view with an already +/// existing view ID will fail, and |info.add_view_callback| will be +/// invoked with an |added| value of false. +/// +/// @param[in] engine A running engine instance. +/// @param[in] info The add view arguments. This can be deallocated +/// once |FlutterEngineAddView| returns, before +/// |add_view_callback| is invoked. +/// +/// @return The result of *starting* the asynchronous operation. If +/// `kSuccess`, the |add_view_callback| will be invoked. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterAddViewInfo* info); + //------------------------------------------------------------------------------ /// @brief Removes a view. /// /// This is an asynchronous operation. The view's resources must not -/// be cleaned up until the |remove_view_callback| is invoked with -/// a |removed| value of `true`. +/// be cleaned up until |info.remove_view_callback| is invoked with +/// a |removed| value of true. +/// +/// The callback is invoked on a thread managed by the engine. The +/// embedder should re-thread if needed. +/// +/// Attempting to remove the implicit view will fail and will return +/// kInvalidArguments. Attempting to remove a view with a +/// non-existent view ID will fail, and |info.remove_view_callback| +/// will be invoked with a |removed| value of false. /// /// @param[in] engine A running engine instance. /// @param[in] info The remove view arguments. This can be deallocated @@ -3194,6 +3377,12 @@ typedef FlutterEngineResult (*FlutterEngineSetNextFrameCallbackFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, VoidCallback callback, void* user_data); +typedef FlutterEngineResult (*FlutterEngineAddViewFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterAddViewInfo* info); +typedef FlutterEngineResult (*FlutterEngineRemoveViewFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterRemoveViewInfo* info); /// Function-pointer-based versions of the APIs above. typedef struct { @@ -3240,6 +3429,8 @@ typedef struct { FlutterEngineNotifyDisplayUpdateFnPtr NotifyDisplayUpdate; FlutterEngineScheduleFrameFnPtr ScheduleFrame; FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback; + FlutterEngineAddViewFnPtr AddView; + FlutterEngineRemoveViewFnPtr RemoveView; } FlutterEngineProcTable; //------------------------------------------------------------------------------ From 4e74babc7d46fcd5614b1b4ca7e5a1519c430c06 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Mon, 10 Feb 2025 20:37:40 +1100 Subject: [PATCH 13/14] Mark FlutterELinuxEngine, FlutterELinuxView and FlutterELinuxTextureRegistrar as unsafe references for importing into Swift, consolidating Swift annotation macro wrappers into flutter_export.h. This patch allows the importing of C++ engine APIs into Swift, using the Swift C++ bridging layer. Signed-off-by: Luke Howard --- .../client_wrapper/include/flutter/texture_registrar.h | 2 +- .../shell/platform/common/public/flutter_export.h | 9 +++++++++ .../shell/platform/common/public/flutter_messenger.h | 8 ++++---- .../platform/common/public/flutter_plugin_registrar.h | 2 +- .../platform/linux_embedded/flutter_elinux_engine.h | 6 ++++-- .../shell/platform/linux_embedded/flutter_elinux_state.h | 6 ------ .../linux_embedded/flutter_elinux_texture_registrar.h | 2 +- .../shell/platform/linux_embedded/flutter_elinux_view.h | 2 +- .../platform/linux_embedded/public/flutter_elinux.h | 4 ++-- 9 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h b/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h index 9b13025f..43f1147c 100644 --- a/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h +++ b/src/flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h @@ -136,7 +136,7 @@ class TextureRegistrar { // Unregisters an existing texture object. // DEPRECATED: Use UnregisterTexture(texture_id, optional_callback) instead. virtual bool UnregisterTexture(int64_t texture_id) = 0; -}; +} SWIFT_UNSAFE_REFERENCE; } // namespace flutter diff --git a/src/flutter/shell/platform/common/public/flutter_export.h b/src/flutter/shell/platform/common/public/flutter_export.h index 38cac85b..5fe8c593 100644 --- a/src/flutter/shell/platform/common/public/flutter_export.h +++ b/src/flutter/shell/platform/common/public/flutter_export.h @@ -25,4 +25,13 @@ #endif // FLUTTER_DESKTOP_LIBRARY +#if __has_include() +#include +#else +#define SWIFT_UNSAFE_REFERENCE +#define SWIFT_SHARED_REFERENCE(_retain, _release) +#define SWIFT_RETURNS_RETAINED +#define SWIFT_RETURNS_UNRETAINED +#endif + #endif // FLUTTER_SHELL_PLATFORM_COMMON_PUBLIC_FLUTTER_EXPORT_H_ diff --git a/src/flutter/shell/platform/common/public/flutter_messenger.h b/src/flutter/shell/platform/common/public/flutter_messenger.h index bb182cdf..d06385ca 100644 --- a/src/flutter/shell/platform/common/public/flutter_messenger.h +++ b/src/flutter/shell/platform/common/public/flutter_messenger.h @@ -93,8 +93,8 @@ FLUTTER_EXPORT void FlutterDesktopMessengerSetCallback( // Operation is thread-safe. // // See also: |FlutterDesktopMessengerRelease| -FLUTTER_EXPORT FlutterDesktopMessengerRef -FlutterDesktopMessengerAddRef(FlutterDesktopMessengerRef messenger); +FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopMessengerAddRef( + FlutterDesktopMessengerRef messenger) SWIFT_RETURNS_RETAINED; // Decrements the reference count for the |messenger|. // @@ -124,8 +124,8 @@ FLUTTER_EXPORT bool FlutterDesktopMessengerIsAvailable( // Returns the |messenger| value. // // See also: |FlutterDesktopMessengerUnlock| -FLUTTER_EXPORT FlutterDesktopMessengerRef -FlutterDesktopMessengerLock(FlutterDesktopMessengerRef messenger); +FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopMessengerLock( + FlutterDesktopMessengerRef messenger) SWIFT_RETURNS_UNRETAINED; // Unlocks the `FlutterDesktopMessengerRef`. // diff --git a/src/flutter/shell/platform/common/public/flutter_plugin_registrar.h b/src/flutter/shell/platform/common/public/flutter_plugin_registrar.h index b0847069..23a13b6d 100644 --- a/src/flutter/shell/platform/common/public/flutter_plugin_registrar.h +++ b/src/flutter/shell/platform/common/public/flutter_plugin_registrar.h @@ -26,7 +26,7 @@ typedef void (*FlutterDesktopOnPluginRegistrarDestroyed)( // Returns the engine messenger associated with this registrar. FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopPluginRegistrarGetMessenger( - FlutterDesktopPluginRegistrarRef registrar); + FlutterDesktopPluginRegistrarRef registrar) SWIFT_RETURNS_UNRETAINED; // Returns the texture registrar associated with this registrar. FLUTTER_EXPORT FlutterDesktopTextureRegistrarRef diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h index 4bcc7f10..c7eeee4d 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_engine.h @@ -71,7 +71,9 @@ class FlutterELinuxEngine { // Sets switches member to the given switches. void SetSwitches(const std::vector& switches); - FlutterDesktopMessengerRef messenger() { return messenger_.get(); } + FlutterDesktopMessengerRef messenger() SWIFT_RETURNS_UNRETAINED { + return messenger_.get(); + } IncomingMessageDispatcher* message_dispatcher() { return message_dispatcher_.get(); @@ -195,7 +197,7 @@ class FlutterELinuxEngine { std::unique_ptr vsync_waiter_; bool enable_impeller_ = false; -}; +} SWIFT_UNSAFE_REFERENCE; } // namespace flutter diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h b/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h index aea43c4c..061f1e87 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_state.h @@ -9,12 +9,6 @@ #include #include -#if __has_include() -#include -#else -#define SWIFT_SHARED_REFERENCE(_retain, _release) -#endif - #include "flutter/shell/platform/common/client_wrapper/include/flutter/plugin_registrar.h" #include "flutter/shell/platform/common/incoming_message_dispatcher.h" #include "flutter/shell/platform/embedder/embedder.h" diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h b/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h index 8770ad57..6e75cb00 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_texture_registrar.h @@ -56,7 +56,7 @@ class FlutterELinuxTextureRegistrar { std::mutex map_mutex_; int64_t EmplaceTexture(std::unique_ptr texture); -}; +} SWIFT_UNSAFE_REFERENCE; }; // namespace flutter diff --git a/src/flutter/shell/platform/linux_embedded/flutter_elinux_view.h b/src/flutter/shell/platform/linux_embedded/flutter_elinux_view.h index 08740eff..0d6ed8d5 100644 --- a/src/flutter/shell/platform/linux_embedded/flutter_elinux_view.h +++ b/src/flutter/shell/platform/linux_embedded/flutter_elinux_view.h @@ -318,7 +318,7 @@ class FlutterELinuxView : public WindowBindingHandlerDelegate { // Current view rotation (FlutterTransformation). FlutterTransformation view_rotation_transformation_ = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; -}; +} SWIFT_UNSAFE_REFERENCE; } // namespace flutter diff --git a/src/flutter/shell/platform/linux_embedded/public/flutter_elinux.h b/src/flutter/shell/platform/linux_embedded/public/flutter_elinux.h index 2597aa47..eacb5269 100644 --- a/src/flutter/shell/platform/linux_embedded/public/flutter_elinux.h +++ b/src/flutter/shell/platform/linux_embedded/public/flutter_elinux.h @@ -216,8 +216,8 @@ FLUTTER_EXPORT FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( FlutterDesktopPluginRegistrarRef registrar); // Returns the messenger associated with the engine. -FLUTTER_EXPORT FlutterDesktopMessengerRef -FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine); +FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopEngineGetMessenger( + FlutterDesktopEngineRef engine) SWIFT_RETURNS_UNRETAINED; // Returns the texture registrar associated with the engine. FLUTTER_EXPORT FlutterDesktopTextureRegistrarRef From 846588a8cf1e6708fb3a5f39a21e18e32004de7a Mon Sep 17 00:00:00 2001 From: Takahiro Okayama Date: Tue, 18 Mar 2025 11:19:47 +0900 Subject: [PATCH 14/14] Update contributing policy to be the same as flutter-elinux. --- CONTRIBUTING.md | 2 ++ README.md | 7 ------- 2 files changed, 2 insertions(+), 7 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4f2807de --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,2 @@ +# Contributing to flutter-embedded-linux +Welcome to this project. We welcome all your contribution and feedback. If you've never submitted code before, you must add your (or your organization's) name and contact info to the [AUTHORS](./AUTHORS) file. diff --git a/README.md b/README.md index cd06c6b8..c5c6bd64 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,3 @@ Documentation for this software can be found at [Wiki](https://github.com/sony/f ## Supported platforms This embedder supports x64 and Arm64 (aarch64, ARMv8) architectures on Linux which supports either Wayland backend or DRM backend. See [Support status](https://github.com/sony/flutter-elinux/wiki/Support-status) for details. - -## Contributing -**Now, we cannot accept any Pull Request (PR).** Because We are building a system (e.g. CLA) to accept PRs, so please wait for a while the system is getting ready! However, we are always welcome to report bugs and request new features by creating issues. - -With the assumption, our final goal of this software openly is to be merged this embedder into [Flutter Engine](https://github.com/flutter/engine) after getting feedbacks. And [Google CLA](https://cla.developers.google.com/about/google-corporate) will be required when we do that in the future. Therefore, we cannot easily accept an external PR. However, you can free to create issues for reporting bugs and requesting new features. - -See also: [Contributing to the Flutter engine](https://github.com/flutter/engine/blob/master/CONTRIBUTING.md)