- add sources.
[platform/framework/web/crosswalk.git] / src / ozone / impl / ozone_display.cc
1 // Copyright 2013 Intel Corporation. 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 "ozone/impl/ozone_display.h"
6
7 #include <map>
8 #include <string>
9
10 #include "base/native_library.h"
11 #include "content/child/child_process.h"
12 #include "ozone/impl/desktop_screen_wayland.h"
13 #include "ozone/impl/event_factory_wayland.h"
14 #include "ozone/impl/ipc/browser_process_dispatcher_delegate.h"
15 #include "ozone/impl/ipc/child_process_observer.h"
16 #include "ozone/impl/ipc/display_channel.h"
17 #include "ozone/impl/ipc/display_channel_host.h"
18 #include "ozone/impl/ipc/gpu_process_dispatcher_delegate.h"
19 #include "ozone/wayland/dispatcher.h"
20 #include "ozone/wayland/display.h"
21 #include "ozone/wayland/egl/egl_window.h"
22 #include "ozone/wayland/screen.h"
23 #include "ozone/wayland/window.h"
24
25 namespace ozonewayland {
26
27 OzoneDisplay* OzoneDisplay::instance_ = NULL;
28
29 OzoneDisplay* OzoneDisplay::GetInstance() {
30   return instance_;
31 }
32
33 OzoneDisplay::OzoneDisplay() : state_(UnInitialized),
34     desktop_screen_(NULL),
35     dispatcher_(NULL),
36     display_(NULL),
37     child_process_observer_(NULL),
38     channel_(NULL),
39     host_(NULL),
40     e_factory_(NULL),
41     spec_(NULL),
42     kMaxDisplaySize_(20) {
43   instance_ = this;
44 }
45
46 OzoneDisplay::~OzoneDisplay() {
47   Terminate();
48   instance_ = NULL;
49 }
50
51 const char* OzoneDisplay::DefaultDisplaySpec() {
52   if (spec_[0] == '\0')
53     NOTREACHED() <<
54         "OutputHandleMode should come from Wayland compositor first";
55
56   return spec_;
57 }
58
59 gfx::Screen* OzoneDisplay::CreateDesktopScreen() {
60   if (!desktop_screen_) {
61     desktop_screen_ = new DesktopScreenWayland;
62     LookAheadOutputGeometry();
63   }
64
65   return desktop_screen_;
66 }
67
68 gfx::SurfaceFactoryOzone::HardwareState OzoneDisplay::InitializeHardware() {
69   if (state_ & Initialized)
70     return initialized_state_;
71
72   state_ |= Initialized;
73   display_ = new WaylandDisplay(WaylandDisplay::RegisterAsNeeded);
74   initialized_state_ =
75       display_->display() ? gfx::SurfaceFactoryOzone::INITIALIZED
76                           : gfx::SurfaceFactoryOzone::FAILED;
77
78   if (initialized_state_ != gfx::SurfaceFactoryOzone::INITIALIZED)
79     LOG(ERROR) << "OzoneDisplay failed to initialize hardware";
80   else if (!content::ChildProcess::current()) {
81     // In the multi-process mode, DisplayChannel (in GPU process side) is in
82     // charge of establishing an IPC channel with DisplayChannelHost (in
83     // Browser Process side). At this moment the GPU process is still
84     // initializing though, so DisplayChannel cannot establish the connection
85     // and need to delay this to later. Therefore post a task to GpuChildThread
86     // and let DisplayChannel handle this right after the GPU process is
87     // initialized.
88     base::MessageLoop::current()->message_loop_proxy()->PostTask(
89         FROM_HERE, base::Bind(&OzoneDisplay::DelayedInitialization, this));
90   }
91
92   return initialized_state_;
93 }
94
95 intptr_t OzoneDisplay::GetNativeDisplay() {
96   return (intptr_t)display_->display();
97 }
98
99 void OzoneDisplay::ShutdownHardware() {
100   Terminate();
101 }
102
103 gfx::AcceleratedWidget OzoneDisplay::GetAcceleratedWidget() {
104   static int opaque_handle = 0;
105   // Ensure dispatcher is initialized.
106   if (!dispatcher_)
107     InitializeDispatcher();
108
109   opaque_handle++;
110   CreateWidget(opaque_handle);
111
112   return (gfx::AcceleratedWidget)opaque_handle;
113 }
114
115 gfx::AcceleratedWidget OzoneDisplay::RealizeAcceleratedWidget(
116     gfx::AcceleratedWidget w) {
117   // Dispatcher should be already initialized unless we are in gpu process side.
118   // Initialize dispatcher and start polling for wayland events.
119   if (!dispatcher_)
120     InitializeDispatcher(display_->GetDisplayFd());
121
122   WaylandWindow* widget = GetWidget(w);
123   DCHECK(widget);
124   widget->RealizeAcceleratedWidget();
125   return (gfx::AcceleratedWidget)widget->egl_window();
126 }
127
128 bool OzoneDisplay::LoadEGLGLES2Bindings(
129     gfx::SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library,
130     gfx::SurfaceFactoryOzone::SetGLGetProcAddressProcCallback setprocaddress) {
131   std::string error;
132   base::NativeLibrary gles_library = base::LoadNativeLibrary(
133     base::FilePath("libGLESv2.so.2"), &error);
134
135   if (!gles_library) {
136     LOG(WARNING) << "Failed to load GLES library: " << error;
137     return false;
138   }
139
140   base::NativeLibrary egl_library = base::LoadNativeLibrary(
141     base::FilePath("libEGL.so.1"), &error);
142
143   if (!egl_library) {
144     LOG(WARNING) << "Failed to load EGL library: " << error;
145     base::UnloadNativeLibrary(gles_library);
146     return false;
147   }
148
149   GLGetProcAddressProc get_proc_address =
150       reinterpret_cast<GLGetProcAddressProc>(
151           base::GetFunctionPointerFromNativeLibrary(
152               egl_library, "eglGetProcAddress"));
153
154   if (!get_proc_address) {
155     LOG(ERROR) << "eglGetProcAddress not found.";
156     base::UnloadNativeLibrary(egl_library);
157     base::UnloadNativeLibrary(gles_library);
158     return false;
159   }
160
161   setprocaddress.Run(get_proc_address);
162   add_gl_library.Run(egl_library);
163   add_gl_library.Run(gles_library);
164   return true;
165 }
166
167 bool OzoneDisplay::AttemptToResizeAcceleratedWidget(gfx::AcceleratedWidget w,
168                                                     const gfx::Rect& bounds) {
169   if (host_) {
170     host_->SendWidgetState(w, Resize, bounds.width(), bounds.height());
171     return true;
172   }
173
174   DCHECK(display_);
175   WaylandWindow* window = GetWidget(w);
176   DCHECK(window);
177
178   return window->SetBounds(bounds);
179 }
180
181 gfx::VSyncProvider* OzoneDisplay::GetVSyncProvider(gfx::AcceleratedWidget w) {
182   return 0;
183 }
184
185 bool OzoneDisplay::SchedulePageFlip(gfx::AcceleratedWidget w) {
186   return true;
187 }
188
189 const int32* OzoneDisplay::GetEGLSurfaceProperties(const int32* desired_list) {
190   return EGLWindow::GetEGLConfigAttribs();
191 }
192
193 void OzoneDisplay::WillDestroyCurrentMessageLoop() {
194   DCHECK(base::MessageLoop::current());
195   dispatcher_->SetActive(false);
196
197   if (child_process_observer_)
198     child_process_observer_->WillDestroyCurrentMessageLoop();
199
200   if (e_factory_)
201     e_factory_->WillDestroyCurrentMessageLoop();
202
203   base::MessageLoop::current()->RemoveDestructionObserver(this);
204 }
205
206 DesktopScreenWayland* OzoneDisplay::GetPrimaryScreen() const {
207   // TODO(kalyan): For now always return DesktopScreen. Needs proper fixing
208   // after multi screen support is added.
209   return desktop_screen_;
210 }
211
212 void OzoneDisplay::SetWidgetState(gfx::AcceleratedWidget w,
213                                   WidgetState state,
214                                   unsigned width,
215                                   unsigned height) {
216   if (host_)
217     host_->SendWidgetState(w, state, width, height);
218   else
219     OnWidgetStateChanged(w, state, width, height);
220 }
221
222 void OzoneDisplay::OnWidgetStateChanged(gfx::AcceleratedWidget w,
223                                        WidgetState state,
224                                        unsigned width,
225                                        unsigned height) {
226   switch (state) {
227     case Create:
228     {
229       CreateWidget(w);
230       break;
231     }
232     case FullScreen:
233     {
234       WaylandWindow* widget = GetWidget(w);
235       widget->ToggleFullscreen();
236       widget->SetBounds(gfx::Rect(0, 0, width, height));
237       break;
238     }
239     case Maximized:
240     {
241       WaylandWindow* widget = GetWidget(w);
242       widget->Maximize();
243       break;
244     }
245     case Minimized:
246     {
247       WaylandWindow* widget = GetWidget(w);
248       widget->Minimize();
249       break;
250     }
251     case Restore:
252     {
253       WaylandWindow* widget = GetWidget(w);
254       widget->Restore();
255       break;
256     }
257     case Active:
258       NOTIMPLEMENTED();
259       break;
260     case InActive:
261       NOTIMPLEMENTED();
262       break;
263     case Show:
264       NOTIMPLEMENTED();
265       break;
266     case Hide:
267       NOTIMPLEMENTED();
268       break;
269     case Resize:
270       AttemptToResizeAcceleratedWidget(w, gfx::Rect(0, 0, width, height));
271       break;
272     case Destroyed:
273     {
274       display_->DestroyWindow(w);
275       const std::map<unsigned, WaylandWindow*> widget_map =
276           display_->GetWindowList();
277
278       if (!widget_map.size()) {
279         if (e_factory_)
280           WillDestroyCurrentMessageLoop();
281         else
282           dispatcher_->SetActive(false);
283       }
284       break;
285     }
286     default:
287       break;
288   }
289 }
290
291 void OzoneDisplay::SetWidgetTitle(gfx::AcceleratedWidget w,
292                                   const string16& title) {
293   if (host_)
294     host_->SendWidgetTitle(w, title);
295   else
296     OnWidgetTitleChanged(w, title);
297 }
298
299 void OzoneDisplay::OnWidgetTitleChanged(gfx::AcceleratedWidget w,
300                                   const string16& title) {
301   WaylandWindow* widget = GetWidget(w);
302   DCHECK(widget);
303   widget->SetWindowTitle(title);
304 }
305
306 void OzoneDisplay::SetWidgetAttributes(gfx::AcceleratedWidget widget,
307                                        gfx::AcceleratedWidget parent,
308                                        unsigned x,
309                                        unsigned y,
310                                        WidgetType type) {
311   if (host_)
312     host_->SendWidgetAttributes(widget, parent, x, y, type);
313   else
314     OnWidgetAttributesChanged(widget, parent, x, y, type);
315 }
316
317 void OzoneDisplay::OnWidgetAttributesChanged(gfx::AcceleratedWidget widget,
318                                              gfx::AcceleratedWidget parent,
319                                              unsigned x,
320                                              unsigned y,
321                                              WidgetType type) {
322   WaylandWindow* window = GetWidget(widget);
323   WaylandWindow* parent_window = GetWidget(parent);
324   DCHECK(window);
325   switch (type) {
326   case Window:
327     window->SetShellAttributes(WaylandWindow::TOPLEVEL);
328     break;
329   case WindowFrameLess:
330     NOTIMPLEMENTED();
331     break;
332   case Transient:
333     DCHECK(parent_window);
334     window->SetShellAttributes(WaylandWindow::TRANSIENT,
335                                parent_window->ShellSurface(),
336                                x,
337                                y);
338     break;
339   default:
340     break;
341   }
342 }
343
344 void OzoneDisplay::SetWindowChangeObserver(WindowChangeObserver* observer) {
345   DCHECK(dispatcher_);
346   dispatcher_->SetWindowChangeObserver(observer);
347 }
348
349 void OzoneDisplay::DelayedInitialization(OzoneDisplay* display) {
350   display->channel_ = new OzoneDisplayChannel();
351   display->channel_->Register();
352 }
353
354 void OzoneDisplay::EstablishChannel() {
355   if (state_ & ChannelConnected)
356     return;
357
358   if (!host_)
359     host_ = new OzoneDisplayChannelHost();
360
361   host_->EstablishChannel();
362   state_ |= ChannelConnected;
363 }
364
365 void OzoneDisplay::OnChannelEstablished() {
366   state_ |= ChannelConnected;
367 }
368
369 void OzoneDisplay::OnChannelHostDestroyed() {
370   state_ &= ~ChannelConnected;
371   host_ = NULL;
372 }
373
374 void OzoneDisplay::OnOutputSizeChanged(WaylandScreen* screen,
375                                        int width,
376                                        int height) {
377   if (!(state_ & Initialized))
378     return;
379
380   if (screen != display_->PrimaryScreen()) {
381     NOTIMPLEMENTED() << "Multiple screens are not implemented";
382     return;
383   }
384
385   if (channel_ && (state_ & ChannelConnected))
386     dispatcher_->OutputSizeChanged(width, height);
387   else
388     OnOutputSizeChanged(width, height);
389 }
390
391 void OzoneDisplay::OnOutputSizeChanged(unsigned width, unsigned height) {
392   if (spec_)
393     base::snprintf(spec_, kMaxDisplaySize_, "%dx%d*2", width, height);
394   if (desktop_screen_)
395     desktop_screen_->SetGeometry(gfx::Rect(0, 0, width, height));
396 }
397
398 WaylandWindow* OzoneDisplay::CreateWidget(unsigned w) {
399   DCHECK((!display_ && host_) || (display_ && !host_));
400   WaylandWindow* window = NULL;
401   if (display_)
402     return display_->CreateAcceleratedSurface(w);
403   else
404     host_->SendWidgetState(w, Create, 0, 0);
405
406   return window;
407 }
408
409 WaylandWindow* OzoneDisplay::GetWidget(gfx::AcceleratedWidget w) {
410   DCHECK(display_);
411   const std::map<unsigned, WaylandWindow*> widget_map =
412       display_->GetWindowList();
413
414   std::map<unsigned, WaylandWindow*>::const_iterator it = widget_map.find(w);
415     return it == widget_map.end() ? NULL : it->second;
416 }
417
418 void OzoneDisplay::Terminate() {
419   if (!(state_ & Initialized))
420     return;
421
422   state_ &= ~Initialized;
423   if (spec_) {
424     delete[] spec_;
425     spec_ = NULL;
426   }
427
428   if (channel_) {
429     delete channel_;
430     channel_ = NULL;
431   }
432
433   if (child_process_observer_) {
434     delete child_process_observer_;
435     child_process_observer_ = NULL;
436   }
437
438   if (dispatcher_) {
439     delete dispatcher_;
440     dispatcher_ = NULL;
441   }
442
443   if (desktop_screen_) {
444     delete desktop_screen_;
445     desktop_screen_ = NULL;
446   }
447
448   if (display_) {
449     delete display_;
450     display_ = NULL;
451   }
452 }
453
454 void OzoneDisplay::InitializeDispatcher(int fd) {
455   dispatcher_ = new WaylandDispatcher(fd);
456   DCHECK(base::MessageLoop::current());
457
458   if (fd) {
459     dispatcher_->SetDelegate(new GpuProcessDispatcherDelegate());
460     dispatcher_->PostTask(WaylandDispatcher::Poll);
461   } else {
462     dispatcher_->SetDelegate(new BrowserProcessDispatcherDelegate());
463     spec_ = new char[kMaxDisplaySize_];
464     spec_[0] = '\0';
465     base::MessageLoop::current()->AddDestructionObserver(this);
466
467     if (display_) {
468       e_factory_ = new EventFactoryWayland();
469     } else {
470       child_process_observer_ = new OzoneProcessObserver(this);
471       EstablishChannel();
472     }
473   }
474 }
475
476 // TODO(vignatti): GPU process conceptually is the one that deals with hardware
477 // details and therefore we assume that the window system connection should
478 // happen in there only. There's a glitch with Chrome though, that creates its
479 // frame contents requiring access to the window system, before the GPU process
480 // even exists. In other words, Chrome runs
481 // BrowserMainLoop::PreMainMessageLoopRun before GpuProcessHost::Get. If the
482 // assumption of window system connection belongs to the GPU process is valid,
483 // then I believe this Chrome behavior needs to be addressed upstream.
484 //
485 // For now, we create another window system connection to look ahead the needed
486 // output properties that Chrome (among others) need and then close right after
487 // that. I haven't measured how long it takes to open a Wayland connection,
488 // listen all the interface the compositor sends and close it, but _for_ _sure_
489 // it slows down the overall initialization time of Chromium targets.
490 // Therefore, this is something that has to be solved in the future, moving all
491 // Chrome tasks after GPU process is created.
492 //
493 void OzoneDisplay::LookAheadOutputGeometry() {
494   DCHECK(desktop_screen_);
495   WaylandDisplay disp_(WaylandDisplay::RegisterOutputOnly);
496   CHECK(disp_.display()) << "Ozone: Wayland server connection not found.";
497
498   while (disp_.PrimaryScreen()->Geometry().IsEmpty())
499     disp_.SyncDisplay();
500
501   desktop_screen_->SetGeometry(disp_.PrimaryScreen()->Geometry());
502 }
503
504 }  // namespace ozonewayland