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.
5 #include "ozone/impl/ozone_display.h"
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"
25 namespace ozonewayland {
27 OzoneDisplay* OzoneDisplay::instance_ = NULL;
29 OzoneDisplay* OzoneDisplay::GetInstance() {
33 OzoneDisplay::OzoneDisplay() : state_(UnInitialized),
34 desktop_screen_(NULL),
37 child_process_observer_(NULL),
42 kMaxDisplaySize_(20) {
46 OzoneDisplay::~OzoneDisplay() {
51 const char* OzoneDisplay::DefaultDisplaySpec() {
54 "OutputHandleMode should come from Wayland compositor first";
59 gfx::Screen* OzoneDisplay::CreateDesktopScreen() {
60 if (!desktop_screen_) {
61 desktop_screen_ = new DesktopScreenWayland;
62 LookAheadOutputGeometry();
65 return desktop_screen_;
68 gfx::SurfaceFactoryOzone::HardwareState OzoneDisplay::InitializeHardware() {
69 if (state_ & Initialized)
70 return initialized_state_;
72 state_ |= Initialized;
73 display_ = new WaylandDisplay(WaylandDisplay::RegisterAsNeeded);
75 display_->display() ? gfx::SurfaceFactoryOzone::INITIALIZED
76 : gfx::SurfaceFactoryOzone::FAILED;
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
88 base::MessageLoop::current()->message_loop_proxy()->PostTask(
89 FROM_HERE, base::Bind(&OzoneDisplay::DelayedInitialization, this));
92 return initialized_state_;
95 intptr_t OzoneDisplay::GetNativeDisplay() {
96 return (intptr_t)display_->display();
99 void OzoneDisplay::ShutdownHardware() {
103 gfx::AcceleratedWidget OzoneDisplay::GetAcceleratedWidget() {
104 static int opaque_handle = 0;
105 // Ensure dispatcher is initialized.
107 InitializeDispatcher();
110 CreateWidget(opaque_handle);
112 return (gfx::AcceleratedWidget)opaque_handle;
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.
120 InitializeDispatcher(display_->GetDisplayFd());
122 WaylandWindow* widget = GetWidget(w);
124 widget->RealizeAcceleratedWidget();
125 return (gfx::AcceleratedWidget)widget->egl_window();
128 bool OzoneDisplay::LoadEGLGLES2Bindings(
129 gfx::SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library,
130 gfx::SurfaceFactoryOzone::SetGLGetProcAddressProcCallback setprocaddress) {
132 base::NativeLibrary gles_library = base::LoadNativeLibrary(
133 base::FilePath("libGLESv2.so.2"), &error);
136 LOG(WARNING) << "Failed to load GLES library: " << error;
140 base::NativeLibrary egl_library = base::LoadNativeLibrary(
141 base::FilePath("libEGL.so.1"), &error);
144 LOG(WARNING) << "Failed to load EGL library: " << error;
145 base::UnloadNativeLibrary(gles_library);
149 GLGetProcAddressProc get_proc_address =
150 reinterpret_cast<GLGetProcAddressProc>(
151 base::GetFunctionPointerFromNativeLibrary(
152 egl_library, "eglGetProcAddress"));
154 if (!get_proc_address) {
155 LOG(ERROR) << "eglGetProcAddress not found.";
156 base::UnloadNativeLibrary(egl_library);
157 base::UnloadNativeLibrary(gles_library);
161 setprocaddress.Run(get_proc_address);
162 add_gl_library.Run(egl_library);
163 add_gl_library.Run(gles_library);
167 bool OzoneDisplay::AttemptToResizeAcceleratedWidget(gfx::AcceleratedWidget w,
168 const gfx::Rect& bounds) {
170 host_->SendWidgetState(w, Resize, bounds.width(), bounds.height());
175 WaylandWindow* window = GetWidget(w);
178 return window->SetBounds(bounds);
181 gfx::VSyncProvider* OzoneDisplay::GetVSyncProvider(gfx::AcceleratedWidget w) {
185 bool OzoneDisplay::SchedulePageFlip(gfx::AcceleratedWidget w) {
189 const int32* OzoneDisplay::GetEGLSurfaceProperties(const int32* desired_list) {
190 return EGLWindow::GetEGLConfigAttribs();
193 void OzoneDisplay::WillDestroyCurrentMessageLoop() {
194 DCHECK(base::MessageLoop::current());
195 dispatcher_->SetActive(false);
197 if (child_process_observer_)
198 child_process_observer_->WillDestroyCurrentMessageLoop();
201 e_factory_->WillDestroyCurrentMessageLoop();
203 base::MessageLoop::current()->RemoveDestructionObserver(this);
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_;
212 void OzoneDisplay::SetWidgetState(gfx::AcceleratedWidget w,
217 host_->SendWidgetState(w, state, width, height);
219 OnWidgetStateChanged(w, state, width, height);
222 void OzoneDisplay::OnWidgetStateChanged(gfx::AcceleratedWidget w,
234 WaylandWindow* widget = GetWidget(w);
235 widget->ToggleFullscreen();
236 widget->SetBounds(gfx::Rect(0, 0, width, height));
241 WaylandWindow* widget = GetWidget(w);
247 WaylandWindow* widget = GetWidget(w);
253 WaylandWindow* widget = GetWidget(w);
270 AttemptToResizeAcceleratedWidget(w, gfx::Rect(0, 0, width, height));
274 display_->DestroyWindow(w);
275 const std::map<unsigned, WaylandWindow*> widget_map =
276 display_->GetWindowList();
278 if (!widget_map.size()) {
280 WillDestroyCurrentMessageLoop();
282 dispatcher_->SetActive(false);
291 void OzoneDisplay::SetWidgetTitle(gfx::AcceleratedWidget w,
292 const string16& title) {
294 host_->SendWidgetTitle(w, title);
296 OnWidgetTitleChanged(w, title);
299 void OzoneDisplay::OnWidgetTitleChanged(gfx::AcceleratedWidget w,
300 const string16& title) {
301 WaylandWindow* widget = GetWidget(w);
303 widget->SetWindowTitle(title);
306 void OzoneDisplay::SetWidgetAttributes(gfx::AcceleratedWidget widget,
307 gfx::AcceleratedWidget parent,
312 host_->SendWidgetAttributes(widget, parent, x, y, type);
314 OnWidgetAttributesChanged(widget, parent, x, y, type);
317 void OzoneDisplay::OnWidgetAttributesChanged(gfx::AcceleratedWidget widget,
318 gfx::AcceleratedWidget parent,
322 WaylandWindow* window = GetWidget(widget);
323 WaylandWindow* parent_window = GetWidget(parent);
327 window->SetShellAttributes(WaylandWindow::TOPLEVEL);
329 case WindowFrameLess:
333 DCHECK(parent_window);
334 window->SetShellAttributes(WaylandWindow::TRANSIENT,
335 parent_window->ShellSurface(),
344 void OzoneDisplay::SetWindowChangeObserver(WindowChangeObserver* observer) {
346 dispatcher_->SetWindowChangeObserver(observer);
349 void OzoneDisplay::DelayedInitialization(OzoneDisplay* display) {
350 display->channel_ = new OzoneDisplayChannel();
351 display->channel_->Register();
354 void OzoneDisplay::EstablishChannel() {
355 if (state_ & ChannelConnected)
359 host_ = new OzoneDisplayChannelHost();
361 host_->EstablishChannel();
362 state_ |= ChannelConnected;
365 void OzoneDisplay::OnChannelEstablished() {
366 state_ |= ChannelConnected;
369 void OzoneDisplay::OnChannelHostDestroyed() {
370 state_ &= ~ChannelConnected;
374 void OzoneDisplay::OnOutputSizeChanged(WaylandScreen* screen,
377 if (!(state_ & Initialized))
380 if (screen != display_->PrimaryScreen()) {
381 NOTIMPLEMENTED() << "Multiple screens are not implemented";
385 if (channel_ && (state_ & ChannelConnected))
386 dispatcher_->OutputSizeChanged(width, height);
388 OnOutputSizeChanged(width, height);
391 void OzoneDisplay::OnOutputSizeChanged(unsigned width, unsigned height) {
393 base::snprintf(spec_, kMaxDisplaySize_, "%dx%d*2", width, height);
395 desktop_screen_->SetGeometry(gfx::Rect(0, 0, width, height));
398 WaylandWindow* OzoneDisplay::CreateWidget(unsigned w) {
399 DCHECK((!display_ && host_) || (display_ && !host_));
400 WaylandWindow* window = NULL;
402 return display_->CreateAcceleratedSurface(w);
404 host_->SendWidgetState(w, Create, 0, 0);
409 WaylandWindow* OzoneDisplay::GetWidget(gfx::AcceleratedWidget w) {
411 const std::map<unsigned, WaylandWindow*> widget_map =
412 display_->GetWindowList();
414 std::map<unsigned, WaylandWindow*>::const_iterator it = widget_map.find(w);
415 return it == widget_map.end() ? NULL : it->second;
418 void OzoneDisplay::Terminate() {
419 if (!(state_ & Initialized))
422 state_ &= ~Initialized;
433 if (child_process_observer_) {
434 delete child_process_observer_;
435 child_process_observer_ = NULL;
443 if (desktop_screen_) {
444 delete desktop_screen_;
445 desktop_screen_ = NULL;
454 void OzoneDisplay::InitializeDispatcher(int fd) {
455 dispatcher_ = new WaylandDispatcher(fd);
456 DCHECK(base::MessageLoop::current());
459 dispatcher_->SetDelegate(new GpuProcessDispatcherDelegate());
460 dispatcher_->PostTask(WaylandDispatcher::Poll);
462 dispatcher_->SetDelegate(new BrowserProcessDispatcherDelegate());
463 spec_ = new char[kMaxDisplaySize_];
465 base::MessageLoop::current()->AddDestructionObserver(this);
468 e_factory_ = new EventFactoryWayland();
470 child_process_observer_ = new OzoneProcessObserver(this);
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.
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.
493 void OzoneDisplay::LookAheadOutputGeometry() {
494 DCHECK(desktop_screen_);
495 WaylandDisplay disp_(WaylandDisplay::RegisterOutputOnly);
496 CHECK(disp_.display()) << "Ozone: Wayland server connection not found.";
498 while (disp_.PrimaryScreen()->Geometry().IsEmpty())
501 desktop_screen_->SetGeometry(disp_.PrimaryScreen()->Geometry());
504 } // namespace ozonewayland