1 // Copyright 2021 Samsung Electronics. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/ozone/platform/efl/efl_window.h"
7 #include "base/base_switches.h"
8 #include "base/command_line.h"
9 #include "base/functional/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_refptr.h"
12 #include "base/notreached.h"
13 #include "ui/aura/window_tree_host_platform.h"
14 #include "ui/base/cursor/platform_cursor.h"
15 #include "ui/events/ozone/events_ozone.h"
16 #include "ui/ozone/platform/efl/efl_event_handler.h"
17 #include "ui/ozone/platform/efl/efl_input_method_context.h"
18 #include "ui/ozone/platform/efl/efl_platform_event_source.h"
19 #include "ui/ozone/platform/efl/efl_screen.h"
20 #include "ui/ozone/platform/efl/efl_window_manager.h"
22 #if BUILDFLAG(IS_TIZEN_TV)
23 #include <cursor_module.h>
26 #if defined(TIZEN_VIDEO_HOLE)
27 #include "tizen_src/chromium_impl/media/base/tizen/video_plane_controller.h"
34 #if defined(USE_WAYLAND)
35 Ecore_Wl2_Window_Type GetEcoreWl2WindowType(PlatformWindowType type) {
36 Ecore_Wl2_Window_Type window_type = ECORE_WL2_WINDOW_TYPE_NONE;
39 case PlatformWindowType::kWindow:
40 window_type = ECORE_WL2_WINDOW_TYPE_TOPLEVEL;
42 case PlatformWindowType::kPopup:
43 case PlatformWindowType::kMenu:
44 case PlatformWindowType::kTooltip:
45 case PlatformWindowType::kBubble:
46 window_type = ECORE_WL2_WINDOW_TYPE_UTILITY;
48 case PlatformWindowType::kDrag:
49 window_type = ECORE_WL2_WINDOW_TYPE_DND;
52 window_type = ECORE_WL2_WINDOW_TYPE_NONE;
59 bool g_evas_init = false;
63 Ecore_Evas* prepared_ee = nullptr;
65 #if BUILDFLAG(IS_TIZEN_TV)
66 bool EflWindow::is_cursor_initialized_ = false;
70 Ecore_Evas* EflWindow::CreateEvasObject(const gfx::Rect& bounds) {
71 #if defined(USE_WAYLAND)
72 return ecore_evas_new("wayland_egl", 0, 0, bounds.width(), bounds.height(),
75 return ecore_evas_new("opengl_x11", 0, 0, bounds.width(), bounds.height(),
81 void EflWindow::SetPreparedEvasObject(Ecore_Evas* ee) {
85 EflWindow::EflWindow(PlatformWindowDelegate* delegate,
86 PlatformWindowInitProperties properties,
87 EflWindowManager* manager)
88 : delegate_(delegate), window_manager_(manager) {
90 Initialize(properties);
93 EflWindow::~EflWindow() {
94 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
96 if (EflWindow* window = window_manager_->GetWindow(events_overlay_))
97 window_manager_->RemoveWindow(events_overlay_);
99 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
100 switches::kEnableOffscreenRendering))
103 #if defined(USE_WAYLAND)
104 if (wl2_egl_window_) {
105 ecore_wl2_egl_window_destroy(wl2_egl_window_);
106 wl2_egl_window_ = nullptr;
110 ecore_evas_free(ee_);
112 #if defined(TIZEN_VIDEO_HOLE)
113 media::VideoPlaneController::SetSharedVideoWindowHandle(
114 nullptr, media::VideoPlaneController::RenderingMode::ONSCREEN);
118 void EflWindow::Show(bool inactive) {
119 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
120 switches::kEnableOffscreenRendering))
124 evas_object_show(events_overlay_);
125 #if defined(USE_WAYLAND)
126 ecore_wl2_window_show(wl_window_);
128 ecore_evas_show(ee_);
130 // Request to redraw window surface when showing. If not, a black screen will
132 delegate_->OnDamageRect(bounds_);
135 void EflWindow::Hide() {
136 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
137 switches::kEnableOffscreenRendering))
141 evas_object_hide(events_overlay_);
142 #if defined(USE_WAYLAND)
143 ecore_wl2_window_hide(wl_window_);
145 ecore_evas_hide(ee_);
148 void EflWindow::Close() {
149 delegate_->OnClosed();
152 bool EflWindow::IsVisible() const {
153 return ecore_evas_visibility_get(ee_);
156 void EflWindow::PrepareForShutdown() {
160 void EflWindow::SetBoundsInPixels(const gfx::Rect& bounds) {
161 bool origin_changed = bounds_.origin() != bounds.origin();
163 delegate_->OnBoundsChanged({origin_changed});
165 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
166 switches::kEnableOffscreenRendering)) {
170 int rotation = ecore_evas_rotation_get(ee_);
171 LOG(INFO) << " bounds " << bounds.ToString() << " rotation " << rotation;
173 #if defined(USE_WAYLAND)
174 ecore_wl2_window_rotation_geometry_set(wl_window_, rotation, bounds.x(),
175 bounds.y(), bounds.width(),
178 if (wl2_egl_window_) {
179 ecore_wl2_egl_window_resize_with_rotation(wl2_egl_window_, bounds.x(),
180 bounds.y(), bounds.width(),
185 ecore_evas_resize(ee_, bounds.width(), bounds.height());
189 gfx::Rect EflWindow::GetBoundsInPixels() const {
193 void EflWindow::SetBoundsInDIP(const gfx::Rect& bounds) {
194 SetBoundsInPixels(delegate_->ConvertRectToPixels(bounds));
197 gfx::Rect EflWindow::GetBoundsInDIP() const {
198 return delegate_->ConvertRectToDIP(bounds_);
201 void EflWindow::SetTitle(const std::u16string& title) {
205 void EflWindow::SetCapture() {
207 window_manager_->GrabLocatedEvents(this);
211 void EflWindow::ReleaseCapture() {
213 window_manager_->UngrabLocatedEvents(this);
217 bool EflWindow::HasCapture() const {
218 return window_manager_->located_events_grabber() == this;
221 void EflWindow::SetFullscreen(bool fullscreen, int64_t target_display_id) {
222 // Check if we need to fullscreen the window or not.
223 if (fullscreen != IsFullscreen()) {
224 state_ = fullscreen ? PlatformWindowState::kFullScreen
225 : PlatformWindowState::kNormal;
226 #if defined(USE_WAYLAND)
227 ecore_wl2_window_fullscreen_set(wl_window_, fullscreen);
232 bool EflWindow::IsFullscreen() {
233 #if defined(USE_WAYLAND)
234 return ecore_wl2_window_fullscreen_get(wl_window_);
240 void EflWindow::Maximize() {
244 void EflWindow::Minimize() {
248 void EflWindow::Restore() {
252 PlatformWindowState EflWindow::GetPlatformWindowState() const {
256 void EflWindow::Activate() {
260 void EflWindow::Deactivate() {
264 void EflWindow::SetUseNativeFrame(bool use_native_frame) {
268 bool EflWindow::ShouldUseNativeFrame() const {
273 void EflWindow::SetCursor(scoped_refptr<PlatformCursor> cursor) {
277 void EflWindow::MoveCursorTo(const gfx::Point& location) {
281 void EflWindow::ConfineCursorToBounds(const gfx::Rect& bounds) {
285 void EflWindow::SetRestoredBoundsInDIP(const gfx::Rect& bounds) {
286 restored_bounds_ = delegate_->ConvertRectToPixels(bounds);
289 gfx::Rect EflWindow::GetRestoredBoundsInDIP() const {
290 return delegate_->ConvertRectToDIP(restored_bounds_);
293 void EflWindow::SetWindowIcons(const gfx::ImageSkia& window_icon,
294 const gfx::ImageSkia& app_icon) {
298 void EflWindow::SizeConstraintsChanged() {
302 bool EflWindow::CanDispatchEvent(const PlatformEvent& event) {
306 void EflWindow::UpdateFocus(bool focused) {
307 if (has_focus_ == focused)
310 LOG(INFO) << "EflWindow Focus:" << focused;
311 has_focus_ = focused;
313 if (events_overlay_) {
314 evas_object_focus_set(events_overlay_, focused);
320 // At any point of time only one window can have focus.
321 // Set set other windows' focus to false.
322 window_manager_->UpdateWindowFocus(this, focused);
325 uint32_t EflWindow::DispatchEvent(const PlatformEvent& native_event) {
326 Event* event = static_cast<Event*>(native_event);
328 if (event->IsLocatedEvent()) {
329 auto* event_grabber = window_manager_->located_events_grabber();
331 // The origin of the submenu event needs to be changed to the root menu.
332 efl_event_handler_->ConvertOriginToTarget(event_grabber,
333 event->AsLocatedEvent());
334 event_grabber->DispatchEventToDelegate(event);
335 return POST_DISPATCH_STOP_PROPAGATION;
339 return DispatchEventToDelegate(event);
342 void EflWindow::OnWindowLostCapture() {
343 delegate_->OnLostCapture();
346 uint32_t EflWindow::DispatchEventToDelegate(const PlatformEvent& event) {
347 bool handled = DispatchEventFromNativeUiEvent(
348 event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
349 base::Unretained(delegate_)));
350 return handled ? POST_DISPATCH_STOP_PROPAGATION : POST_DISPATCH_NONE;
353 void EflWindow::Initialize(const PlatformWindowInitProperties& properties) {
354 bounds_ = properties.bounds;
355 opacity_ = properties.opacity;
356 type_ = properties.type;
358 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
359 switches::kEnableOffscreenRendering)) {
360 delegate_->OnAcceleratedWidgetAvailable(gfx::kNullAcceleratedWidget);
371 LOG(INFO) << "Use prepared ee instance";
373 prepared_ee = nullptr;
375 ee_ = CreateEvasObject(bounds_);
377 LOG(ERROR) << "Failed to create ecore evas.";
382 evas_ = ecore_evas_get(ee_);
384 // Evas engine creates it's own egl window and egl surface to render.
385 // Eventually additional GEM memory is created for rendering. Enable evas
386 // manual rendering to prevent this symptom.
387 ecore_evas_manual_render_set(ee_, EINA_TRUE);
389 #if defined(USE_WAYLAND)
390 wl_window_ = ecore_evas_wayland2_window_get(ee_);
392 LOG(ERROR) << "Failed to get Wl2 window";
395 wl2_egl_window_ = ecore_wl2_egl_window_create(wl_window_, bounds_.width(),
397 if (!wl2_egl_window_) {
398 LOG(ERROR) << "Failed to create wl2 egl window";
402 #if defined(TIZEN_VIDEO_HOLE)
403 // Hand a window handle to enable video hole in OnscreenRendering mode.
404 media::VideoPlaneController::SetSharedVideoWindowHandle(
405 wl_window_, media::VideoPlaneController::RenderingMode::ONSCREEN);
408 void* egl_window = ecore_wl2_egl_window_native_get(wl2_egl_window_);
410 LOG(ERROR) << "Failed to get native egl window";
414 delegate_->OnAcceleratedWidgetAvailable(
415 reinterpret_cast<uintptr_t>(egl_window));
417 if (type_ != PlatformWindowType::kWindow) {
418 ecore_wl2_window_type_set(wl_window_, GetEcoreWl2WindowType(type_));
419 ecore_wl2_window_position_set(wl_window_, bounds_.x(), bounds_.y());
422 delegate_->OnAcceleratedWidgetAvailable(ecore_evas_window_get(ee_));
425 // Initialize efl event handler for main window
426 events_overlay_ = evas_object_rectangle_add(ecore_evas_get(ee_));
427 evas_object_color_set(events_overlay_, 0, 0, 0, 255);
428 evas_object_resize(events_overlay_, bounds_.width(), bounds_.height());
430 InitializeEventHandler();
432 window_manager_->AddWindow(this, events_overlay_);
434 #if BUILDFLAG(IS_TIZEN_TV)
438 if (type_ == PlatformWindowType::kWindow) {
439 EflScreen::UpdateDisplayInfo(ee_);
443 #if BUILDFLAG(IS_TIZEN_TV)
444 void EflWindow::CreateMouseCursor() {
445 if (is_cursor_initialized_)
448 Ecore_Wl2_Display* wl2_display = ecore_wl2_connected_display_get(NULL);
449 struct wl_display* display = ecore_wl2_display_get(wl2_display);
450 Eina_Iterator* globals = ecore_wl2_display_globals_get(wl2_display);
451 struct wl_registry* registry = ecore_wl2_display_registry_get(wl2_display);
452 struct wl_seat* seat =
453 ecore_wl2_input_seat_get(ecore_wl2_input_default_input_get(wl2_display));
455 const char* t_cursor = "tizen_cursor";
456 Ecore_Wl2_Global* global;
457 EINA_ITERATOR_FOREACH(globals, global) {
458 if (!strncmp(global->interface, t_cursor, strlen(t_cursor)))
459 if (!CursorModule_Initialize(display, registry, seat, global->id))
460 LOG(ERROR) << "CursorModule_Initialize() Failed!";
462 eina_iterator_free(globals);
464 struct wl_surface* const surface =
465 (struct wl_surface*)(ecore_wl2_window_native_surface_get(
466 ecore_evas_wayland2_window_get(ee_)));
469 if (!Cursor_Set_Config(surface, TIZEN_CURSOR_CONFIG_CURSOR_AVAILABLE, NULL))
470 LOG(ERROR) << "Cursor_Set_Config() Failed!";
472 CursorModule_Finalize();
473 is_cursor_initialized_ = true;
477 void EflWindow::InitializeEventHandler() {
478 // Set activation true on window to capture key events.
479 delegate_->OnActivationChanged(true /*active*/);
481 // Set focus active for a newly opened window.
482 #if !defined(BUILD_CHROME)
483 #if BUILDFLAG(IS_TIZEN_TV)
484 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
485 switches::kEnableOffscreenRendering))
489 efl_event_handler_ = std::make_unique<EflEventHandler>(this);
490 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
493 void EflWindow::EnsureIMContextEfl() {
494 // Make sure EflInputMethodContext is created, if not already.
495 static_cast<aura::WindowTreeHostPlatform*>(delegate_)->GetInputMethod();
497 if (efl_input_method_context_)
498 efl_input_method_context_->CreateIMContextEfl(this);
501 void EflWindow::ResetEventHandler() {
502 delegate_->OnActivationChanged(false /*active*/);
503 #if BUILDFLAG(IS_TIZEN_TV)
504 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
505 switches::kEnableOffscreenRendering))
508 efl_event_handler_.reset();
509 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
513 void EflWindow::SetEventsOverlayForOffscreen(Evas_Object* events_overlay) {
514 if (events_overlay_ == events_overlay)
517 if (EflWindow* window = window_manager_->GetWindow(events_overlay)) {
518 // Sometimes multiple RWHVs will be created for the same webpage which
519 // results in multiple EflWindows creation for the same events_overlay_.
520 // If |events_overlay_| already had an associated EflWindow, then
521 // deregister it and register the newly created one.
522 window->ResetEventHandler();
523 window_manager_->RemoveWindow(events_overlay);
527 window_manager_->RemoveWindow(events_overlay_);
529 events_overlay_ = events_overlay;
530 evas_ = evas_object_evas_get(events_overlay_);
531 ee_ = ecore_evas_ecore_evas_get(evas_);
533 window_manager_->AddWindow(this, events_overlay_);
535 InitializeEventHandler();
537 window_manager_->AddWindow(this, events_overlay_);
539 #if BUILDFLAG(IS_TIZEN)
540 // Create IMContextEfl only after event handler is instantiated, because
541 // content layer gets IMContextEfl through EflEventHandler.
542 EnsureIMContextEfl();