[M120 Migration][VD] Fix some focus issues for offscreen mode
[platform/framework/web/chromium-efl.git] / tizen_src / chromium_impl / ui / ozone / platform / efl / efl_window.cc
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.
4
5 #include "ui/ozone/platform/efl/efl_window.h"
6
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"
21
22 #if BUILDFLAG(IS_TIZEN_TV)
23 #include <cursor_module.h>
24 #endif
25
26 #if defined(TIZEN_VIDEO_HOLE)
27 #include "tizen_src/chromium_impl/media/base/tizen/video_plane_controller.h"
28 #endif
29
30 namespace ui {
31
32 namespace {
33
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;
37
38   switch (type) {
39     case PlatformWindowType::kWindow:
40       window_type = ECORE_WL2_WINDOW_TYPE_TOPLEVEL;
41       break;
42     case PlatformWindowType::kPopup:
43     case PlatformWindowType::kMenu:
44     case PlatformWindowType::kTooltip:
45     case PlatformWindowType::kBubble:
46       window_type = ECORE_WL2_WINDOW_TYPE_UTILITY;
47       break;
48     case PlatformWindowType::kDrag:
49       window_type = ECORE_WL2_WINDOW_TYPE_DND;
50       break;
51     default:
52       window_type = ECORE_WL2_WINDOW_TYPE_NONE;
53   }
54
55   return window_type;
56 }
57 #endif
58
59 bool g_evas_init = false;
60
61 }  // namespace
62
63 Ecore_Evas* prepared_ee = nullptr;
64
65 #if BUILDFLAG(IS_TIZEN_TV)
66 bool EflWindow::is_cursor_initialized_ = false;
67 #endif
68
69 // static
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(),
73                         nullptr);
74 #else
75   return ecore_evas_new("opengl_x11", 0, 0, bounds.width(), bounds.height(),
76                         nullptr);
77 #endif
78 }
79
80 // static
81 void EflWindow::SetPreparedEvasObject(Ecore_Evas* ee) {
82   prepared_ee = ee;
83 }
84
85 EflWindow::EflWindow(PlatformWindowDelegate* delegate,
86                      PlatformWindowInitProperties properties,
87                      EflWindowManager* manager)
88     : delegate_(delegate), window_manager_(manager) {
89   LOG(INFO) << this;
90   Initialize(properties);
91 }
92
93 EflWindow::~EflWindow() {
94   PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
95
96   if (EflWindow* window = window_manager_->GetWindow(events_overlay_))
97     window_manager_->RemoveWindow(events_overlay_);
98
99   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
100           switches::kEnableOffscreenRendering))
101     return;
102
103 #if defined(USE_WAYLAND)
104   if (wl2_egl_window_) {
105     ecore_wl2_egl_window_destroy(wl2_egl_window_);
106     wl2_egl_window_ = nullptr;
107   }
108 #endif
109   if (ee_)
110     ecore_evas_free(ee_);
111
112 #if defined(TIZEN_VIDEO_HOLE)
113   media::VideoPlaneController::SetSharedVideoWindowHandle(
114       nullptr, media::VideoPlaneController::RenderingMode::ONSCREEN);
115 #endif
116 }
117
118 void EflWindow::Show(bool inactive) {
119   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
120           switches::kEnableOffscreenRendering))
121     return;
122
123   if (events_overlay_)
124     evas_object_show(events_overlay_);
125 #if defined(USE_WAYLAND)
126   ecore_wl2_window_show(wl_window_);
127 #endif
128   ecore_evas_show(ee_);
129
130   // Request to redraw window surface when showing. If not, a black screen will
131   // appear.
132   delegate_->OnDamageRect(bounds_);
133 }
134
135 void EflWindow::Hide() {
136   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
137           switches::kEnableOffscreenRendering))
138     return;
139
140   if (events_overlay_)
141     evas_object_hide(events_overlay_);
142 #if defined(USE_WAYLAND)
143   ecore_wl2_window_hide(wl_window_);
144 #endif
145   ecore_evas_hide(ee_);
146 }
147
148 void EflWindow::Close() {
149   delegate_->OnClosed();
150 }
151
152 bool EflWindow::IsVisible() const {
153   return ecore_evas_visibility_get(ee_);
154 }
155
156 void EflWindow::PrepareForShutdown() {
157   NOTIMPLEMENTED();
158 }
159
160 void EflWindow::SetBoundsInPixels(const gfx::Rect& bounds) {
161   bool origin_changed = bounds_.origin() != bounds.origin();
162   bounds_ = bounds;
163   delegate_->OnBoundsChanged({origin_changed});
164
165   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
166           switches::kEnableOffscreenRendering)) {
167     return;
168   }
169
170   int rotation = ecore_evas_rotation_get(ee_);
171   LOG(INFO) << " bounds " << bounds.ToString() << " rotation " << rotation;
172
173 #if defined(USE_WAYLAND)
174   ecore_wl2_window_rotation_geometry_set(wl_window_, rotation, bounds.x(),
175                                          bounds.y(), bounds.width(),
176                                          bounds.height());
177
178   if (wl2_egl_window_) {
179     ecore_wl2_egl_window_resize_with_rotation(wl2_egl_window_, bounds.x(),
180                                               bounds.y(), bounds.width(),
181                                               bounds.height(), 0);
182   }
183 #else
184   if (ee_)
185     ecore_evas_resize(ee_, bounds.width(), bounds.height());
186 #endif
187 }
188
189 gfx::Rect EflWindow::GetBoundsInPixels() const {
190   return bounds_;
191 }
192
193 void EflWindow::SetBoundsInDIP(const gfx::Rect& bounds) {
194   SetBoundsInPixels(delegate_->ConvertRectToPixels(bounds));
195 }
196
197 gfx::Rect EflWindow::GetBoundsInDIP() const {
198   return delegate_->ConvertRectToDIP(bounds_);
199 }
200
201 void EflWindow::SetTitle(const std::u16string& title) {
202   NOTIMPLEMENTED();
203 }
204
205 void EflWindow::SetCapture() {
206   if (!HasCapture()) {
207     window_manager_->GrabLocatedEvents(this);
208   }
209 }
210
211 void EflWindow::ReleaseCapture() {
212   if (HasCapture()) {
213     window_manager_->UngrabLocatedEvents(this);
214   }
215 }
216
217 bool EflWindow::HasCapture() const {
218   return window_manager_->located_events_grabber() == this;
219 }
220
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);
228 #endif
229   }
230 }
231
232 bool EflWindow::IsFullscreen() {
233 #if defined(USE_WAYLAND)
234   return ecore_wl2_window_fullscreen_get(wl_window_);
235 #endif
236
237   return false;
238 }
239
240 void EflWindow::Maximize() {
241   NOTIMPLEMENTED();
242 }
243
244 void EflWindow::Minimize() {
245   NOTIMPLEMENTED();
246 }
247
248 void EflWindow::Restore() {
249   NOTIMPLEMENTED();
250 }
251
252 PlatformWindowState EflWindow::GetPlatformWindowState() const {
253   return state_;
254 }
255
256 void EflWindow::Activate() {
257   NOTIMPLEMENTED();
258 }
259
260 void EflWindow::Deactivate() {
261   NOTIMPLEMENTED();
262 }
263
264 void EflWindow::SetUseNativeFrame(bool use_native_frame) {
265   NOTIMPLEMENTED();
266 }
267
268 bool EflWindow::ShouldUseNativeFrame() const {
269   NOTIMPLEMENTED();
270   return false;
271 }
272
273 void EflWindow::SetCursor(scoped_refptr<PlatformCursor> cursor) {
274   NOTIMPLEMENTED();
275 }
276
277 void EflWindow::MoveCursorTo(const gfx::Point& location) {
278   NOTIMPLEMENTED();
279 }
280
281 void EflWindow::ConfineCursorToBounds(const gfx::Rect& bounds) {
282   NOTIMPLEMENTED();
283 }
284
285 void EflWindow::SetRestoredBoundsInDIP(const gfx::Rect& bounds) {
286   restored_bounds_ = delegate_->ConvertRectToPixels(bounds);
287 }
288
289 gfx::Rect EflWindow::GetRestoredBoundsInDIP() const {
290   return delegate_->ConvertRectToDIP(restored_bounds_);
291 }
292
293 void EflWindow::SetWindowIcons(const gfx::ImageSkia& window_icon,
294                                const gfx::ImageSkia& app_icon) {
295   NOTIMPLEMENTED();
296 }
297
298 void EflWindow::SizeConstraintsChanged() {
299   NOTIMPLEMENTED();
300 }
301
302 bool EflWindow::CanDispatchEvent(const PlatformEvent& event) {
303   return has_focus_;
304 }
305
306 void EflWindow::UpdateFocus(bool focused) {
307   if (has_focus_ == focused)
308     return;
309
310   LOG(INFO) << "EflWindow Focus:" << focused;
311   has_focus_ = focused;
312
313   if (events_overlay_) {
314     evas_object_focus_set(events_overlay_, focused);
315   }
316
317   if (!focused)
318     return;
319
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);
323 }
324
325 uint32_t EflWindow::DispatchEvent(const PlatformEvent& native_event) {
326   Event* event = static_cast<Event*>(native_event);
327
328   if (event->IsLocatedEvent()) {
329     auto* event_grabber = window_manager_->located_events_grabber();
330     if (event_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;
336     }
337   }
338
339   return DispatchEventToDelegate(event);
340 }
341
342 void EflWindow::OnWindowLostCapture() {
343   delegate_->OnLostCapture();
344 }
345
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;
351 }
352
353 void EflWindow::Initialize(const PlatformWindowInitProperties& properties) {
354   bounds_ = properties.bounds;
355   opacity_ = properties.opacity;
356   type_ = properties.type;
357
358   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
359           switches::kEnableOffscreenRendering)) {
360     delegate_->OnAcceleratedWidgetAvailable(gfx::kNullAcceleratedWidget);
361     return;
362   }
363
364   if (!g_evas_init) {
365     ecore_evas_init();
366     evas_init();
367     g_evas_init = true;
368   }
369
370   if (prepared_ee) {
371     LOG(INFO) << "Use prepared ee instance";
372     ee_ = prepared_ee;
373     prepared_ee = nullptr;
374   } else {
375     ee_ = CreateEvasObject(bounds_);
376     if (!ee_) {
377       LOG(ERROR) << "Failed to create ecore evas.";
378       return;
379     }
380   }
381
382   evas_ = ecore_evas_get(ee_);
383
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);
388
389 #if defined(USE_WAYLAND)
390   wl_window_ = ecore_evas_wayland2_window_get(ee_);
391   if (!wl_window_) {
392     LOG(ERROR) << "Failed to get Wl2 window";
393     return;
394   }
395   wl2_egl_window_ = ecore_wl2_egl_window_create(wl_window_, bounds_.width(),
396                                                 bounds_.height());
397   if (!wl2_egl_window_) {
398     LOG(ERROR) << "Failed to create wl2 egl window";
399     return;
400   }
401
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);
406 #endif
407
408   void* egl_window = ecore_wl2_egl_window_native_get(wl2_egl_window_);
409   if (!egl_window) {
410     LOG(ERROR) << "Failed to get native egl window";
411     return;
412   }
413
414   delegate_->OnAcceleratedWidgetAvailable(
415       reinterpret_cast<uintptr_t>(egl_window));
416
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());
420   }
421 #else
422   delegate_->OnAcceleratedWidgetAvailable(ecore_evas_window_get(ee_));
423 #endif
424
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());
429
430   InitializeEventHandler();
431
432   window_manager_->AddWindow(this, events_overlay_);
433
434 #if BUILDFLAG(IS_TIZEN_TV)
435   CreateMouseCursor();
436 #endif
437
438   if (type_ == PlatformWindowType::kWindow) {
439     EflScreen::UpdateDisplayInfo(ee_);
440   }
441 }
442
443 #if BUILDFLAG(IS_TIZEN_TV)
444 void EflWindow::CreateMouseCursor() {
445   if (is_cursor_initialized_)
446     return;
447
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));
454
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!";
461   }
462   eina_iterator_free(globals);
463
464   struct wl_surface* const surface =
465       (struct wl_surface*)(ecore_wl2_window_native_surface_get(
466           ecore_evas_wayland2_window_get(ee_)));
467   ecore_wl2_sync();
468
469   if (!Cursor_Set_Config(surface, TIZEN_CURSOR_CONFIG_CURSOR_AVAILABLE, NULL))
470     LOG(ERROR) << "Cursor_Set_Config() Failed!";
471
472   CursorModule_Finalize();
473   is_cursor_initialized_ = true;
474 }
475 #endif
476
477 void EflWindow::InitializeEventHandler() {
478   // Set activation true on window to capture key events.
479   delegate_->OnActivationChanged(true /*active*/);
480
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))
486 #endif
487     UpdateFocus(true);
488 #endif
489   efl_event_handler_ = std::make_unique<EflEventHandler>(this);
490   PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
491 }
492
493 void EflWindow::EnsureIMContextEfl() {
494   // Make sure EflInputMethodContext is created, if not already.
495   static_cast<aura::WindowTreeHostPlatform*>(delegate_)->GetInputMethod();
496
497   if (efl_input_method_context_)
498     efl_input_method_context_->CreateIMContextEfl(this);
499 }
500
501 void EflWindow::ResetEventHandler() {
502   delegate_->OnActivationChanged(false /*active*/);
503 #if BUILDFLAG(IS_TIZEN_TV)
504   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
505           switches::kEnableOffscreenRendering))
506 #endif
507     UpdateFocus(false);
508   efl_event_handler_.reset();
509   PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
510 }
511
512 // Offscreen
513 void EflWindow::SetEventsOverlayForOffscreen(Evas_Object* events_overlay) {
514   if (events_overlay_ == events_overlay)
515     return;
516
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);
524   }
525
526   ResetEventHandler();
527   window_manager_->RemoveWindow(events_overlay_);
528
529   events_overlay_ = events_overlay;
530   evas_ = evas_object_evas_get(events_overlay_);
531   ee_ = ecore_evas_ecore_evas_get(evas_);
532
533   window_manager_->AddWindow(this, events_overlay_);
534
535   InitializeEventHandler();
536
537   window_manager_->AddWindow(this, events_overlay_);
538
539 #if BUILDFLAG(IS_TIZEN)
540   // Create IMContextEfl only after event handler is instantiated, because
541   // content layer gets IMContextEfl through EflEventHandler.
542   EnsureIMContextEfl();
543 #endif
544 }
545
546 }  // namespace ui