1// Copyright 2014 The Chromium Authors. 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/egltest/ozone_platform_egltest.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/environment.h"
10#include "base/files/file_path.h"
11#include "base/path_service.h"
12#include "library_loaders/libeglplatform_shim.h"
13#include "third_party/khronos/EGL/egl.h"
14#include "ui/events/ozone/device/device_manager.h"
15#include "ui/events/ozone/evdev/event_factory_evdev.h"
16#include "ui/events/ozone/events_ozone.h"
17#include "ui/events/platform/platform_event_dispatcher.h"
18#include "ui/gfx/vsync_provider.h"
19#include "ui/ozone/public/cursor_factory_ozone.h"
20#include "ui/ozone/public/gpu_platform_support.h"
21#include "ui/ozone/public/gpu_platform_support_host.h"
22#include "ui/ozone/public/ozone_platform.h"
23#include "ui/ozone/public/ozone_switches.h"
24#include "ui/ozone/public/surface_factory_ozone.h"
25#include "ui/ozone/public/surface_ozone_egl.h"
26#include "ui/platform_window/platform_window.h"
27#include "ui/platform_window/platform_window_delegate.h"
28
29#if defined(OS_CHROMEOS)
30#include "ui/ozone/common/chromeos/native_display_delegate_ozone.h"
31#endif
32
33namespace ui {
34
35namespace {
36
37const char kEglplatformShim[] = "EGLPLATFORM_SHIM";
38const char kEglplatformShimDefault[] = "libeglplatform_shim.so.1";
39const char kDefaultEglSoname[] = "libEGL.so.1";
40const char kDefaultGlesSoname[] = "libGLESv2.so.2";
41
42// Get the library soname to load.
43std::string GetShimLibraryName() {
44  std::string library;
45  scoped_ptr<base::Environment> env(base::Environment::Create());
46  if (env->GetVar(kEglplatformShim, &library))
47    return library;
48  return kEglplatformShimDefault;
49}
50
51class EgltestWindow : public PlatformWindow, public PlatformEventDispatcher {
52 public:
53  EgltestWindow(PlatformWindowDelegate* delegate,
54                LibeglplatformShimLoader* eglplatform_shim,
55                EventFactoryEvdev* event_factory,
56                const gfx::Rect& bounds);
57  virtual ~EgltestWindow();
58
59  // PlatformWindow:
60  virtual gfx::Rect GetBounds() OVERRIDE;
61  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
62  virtual void Show() OVERRIDE;
63  virtual void Hide() OVERRIDE;
64  virtual void Close() OVERRIDE;
65  virtual void SetCapture() OVERRIDE;
66  virtual void ReleaseCapture() OVERRIDE;
67  virtual void ToggleFullscreen() OVERRIDE;
68  virtual void Maximize() OVERRIDE;
69  virtual void Minimize() OVERRIDE;
70  virtual void Restore() OVERRIDE;
71  virtual void SetCursor(PlatformCursor cursor) OVERRIDE;
72  virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
73
74  // PlatformEventDispatcher:
75  virtual bool CanDispatchEvent(const PlatformEvent& event) OVERRIDE;
76  virtual uint32_t DispatchEvent(const PlatformEvent& event) OVERRIDE;
77
78 private:
79  PlatformWindowDelegate* delegate_;
80  LibeglplatformShimLoader* eglplatform_shim_;
81  EventFactoryEvdev* event_factory_;
82  gfx::Rect bounds_;
83  ShimNativeWindowId window_id_;
84
85  DISALLOW_COPY_AND_ASSIGN(EgltestWindow);
86};
87
88EgltestWindow::EgltestWindow(PlatformWindowDelegate* delegate,
89                             LibeglplatformShimLoader* eglplatform_shim,
90                             EventFactoryEvdev* event_factory,
91                             const gfx::Rect& bounds)
92    : delegate_(delegate),
93      eglplatform_shim_(eglplatform_shim),
94      event_factory_(event_factory),
95      bounds_(bounds),
96      window_id_(SHIM_NO_WINDOW_ID) {
97  window_id_ = eglplatform_shim_->ShimCreateWindow();
98  delegate_->OnAcceleratedWidgetAvailable(window_id_);
99  ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
100}
101
102EgltestWindow::~EgltestWindow() {
103  ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
104  if (window_id_ != SHIM_NO_WINDOW_ID)
105    eglplatform_shim_->ShimDestroyWindow(window_id_);
106}
107
108gfx::Rect EgltestWindow::GetBounds() {
109  return bounds_;
110}
111
112void EgltestWindow::SetBounds(const gfx::Rect& bounds) {
113  bounds_ = bounds;
114  delegate_->OnBoundsChanged(bounds);
115}
116
117void EgltestWindow::Show() {
118}
119
120void EgltestWindow::Hide() {
121}
122
123void EgltestWindow::Close() {
124}
125
126void EgltestWindow::SetCapture() {
127}
128
129void EgltestWindow::ReleaseCapture() {
130}
131
132void EgltestWindow::ToggleFullscreen() {
133}
134
135void EgltestWindow::Maximize() {
136}
137
138void EgltestWindow::Minimize() {
139}
140
141void EgltestWindow::Restore() {
142}
143
144void EgltestWindow::SetCursor(PlatformCursor cursor) {
145}
146
147void EgltestWindow::MoveCursorTo(const gfx::Point& location) {
148  event_factory_->WarpCursorTo(window_id_, location);
149}
150
151bool EgltestWindow::CanDispatchEvent(const ui::PlatformEvent& ne) {
152  return true;
153}
154
155uint32_t EgltestWindow::DispatchEvent(const ui::PlatformEvent& native_event) {
156  DispatchEventFromNativeUiEvent(
157      native_event,
158      base::Bind(&PlatformWindowDelegate::DispatchEvent,
159                 base::Unretained(delegate_)));
160
161  return ui::POST_DISPATCH_STOP_PROPAGATION;
162}
163
164// EGL surface wrapper for libeglplatform_shim.
165//
166// This just manages the native window lifetime using
167// ShimGetNativeWindow & ShimReleaseNativeWindow.
168class SurfaceOzoneEgltest : public SurfaceOzoneEGL {
169 public:
170  SurfaceOzoneEgltest(ShimNativeWindowId window_id,
171                      LibeglplatformShimLoader* eglplatform_shim)
172      : eglplatform_shim_(eglplatform_shim) {
173    native_window_ = eglplatform_shim_->ShimGetNativeWindow(window_id);
174  }
175  virtual ~SurfaceOzoneEgltest() {
176    bool ret = eglplatform_shim_->ShimReleaseNativeWindow(native_window_);
177    DCHECK(ret);
178  }
179
180  virtual intptr_t GetNativeWindow() OVERRIDE { return native_window_; }
181
182  virtual bool OnSwapBuffers() OVERRIDE { return true; }
183
184  virtual bool ResizeNativeWindow(const gfx::Size& viewport_size) OVERRIDE {
185    return true;
186  }
187
188  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() OVERRIDE {
189    return scoped_ptr<gfx::VSyncProvider>();
190  }
191
192 private:
193  LibeglplatformShimLoader* eglplatform_shim_;
194  intptr_t native_window_;
195};
196
197// EGL surface factory for libeglplatform_shim.
198//
199// This finds the right EGL/GLES2 libraries for loading, and creates
200// a single native window via ShimCreateWindow for drawing
201// into.
202class SurfaceFactoryEgltest : public ui::SurfaceFactoryOzone {
203 public:
204  SurfaceFactoryEgltest(LibeglplatformShimLoader* eglplatform_shim)
205      : eglplatform_shim_(eglplatform_shim) {}
206  virtual ~SurfaceFactoryEgltest() {}
207
208  // SurfaceFactoryOzone:
209  virtual intptr_t GetNativeDisplay() OVERRIDE;
210  virtual scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget(
211      gfx::AcceleratedWidget widget) OVERRIDE;
212  virtual const int32* GetEGLSurfaceProperties(
213      const int32* desired_list) OVERRIDE;
214  virtual bool LoadEGLGLES2Bindings(
215      AddGLLibraryCallback add_gl_library,
216      SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE;
217
218 private:
219  LibeglplatformShimLoader* eglplatform_shim_;
220};
221
222intptr_t SurfaceFactoryEgltest::GetNativeDisplay() {
223  return eglplatform_shim_->ShimGetNativeDisplay();
224}
225
226scoped_ptr<SurfaceOzoneEGL> SurfaceFactoryEgltest::CreateEGLSurfaceForWidget(
227    gfx::AcceleratedWidget widget) {
228  return make_scoped_ptr<SurfaceOzoneEGL>(
229      new SurfaceOzoneEgltest(widget, eglplatform_shim_));
230}
231
232bool SurfaceFactoryEgltest::LoadEGLGLES2Bindings(
233    AddGLLibraryCallback add_gl_library,
234    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
235  const char* egl_soname = eglplatform_shim_->ShimQueryString(SHIM_EGL_LIBRARY);
236  const char* gles_soname =
237      eglplatform_shim_->ShimQueryString(SHIM_GLES_LIBRARY);
238  if (!egl_soname)
239    egl_soname = kDefaultEglSoname;
240  if (!gles_soname)
241    gles_soname = kDefaultGlesSoname;
242
243  base::NativeLibraryLoadError error;
244  base::NativeLibrary egl_library =
245      base::LoadNativeLibrary(base::FilePath(egl_soname), &error);
246  if (!egl_library) {
247    LOG(WARNING) << "Failed to load EGL library: " << error.ToString();
248    return false;
249  }
250
251  base::NativeLibrary gles_library =
252      base::LoadNativeLibrary(base::FilePath(gles_soname), &error);
253  if (!gles_library) {
254    LOG(WARNING) << "Failed to load GLES library: " << error.ToString();
255    base::UnloadNativeLibrary(egl_library);
256    return false;
257  }
258
259  GLGetProcAddressProc get_proc_address =
260      reinterpret_cast<GLGetProcAddressProc>(
261          base::GetFunctionPointerFromNativeLibrary(egl_library,
262                                                    "eglGetProcAddress"));
263  if (!get_proc_address) {
264    LOG(ERROR) << "eglGetProcAddress not found.";
265    base::UnloadNativeLibrary(egl_library);
266    base::UnloadNativeLibrary(gles_library);
267    return false;
268  }
269
270  set_gl_get_proc_address.Run(get_proc_address);
271  add_gl_library.Run(egl_library);
272  add_gl_library.Run(gles_library);
273  return true;
274}
275
276const int32* SurfaceFactoryEgltest::GetEGLSurfaceProperties(
277    const int32* desired_list) {
278  static const int32 broken_props[] = {
279      EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
280      EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
281      EGL_NONE,
282  };
283  return broken_props;
284}
285
286// Test platform for EGL.
287//
288// This is a tiny EGL-based platform. Creation of the native window is
289// handled by a separate library called eglplatform_shim.so.1 because
290// this itself is platform specific and we want to test out multiple
291// hardware platforms.
292class OzonePlatformEgltest : public OzonePlatform {
293 public:
294  OzonePlatformEgltest() : shim_initialized_(false) {}
295  virtual ~OzonePlatformEgltest() {
296    if (shim_initialized_)
297      eglplatform_shim_.ShimTerminate();
298  }
299
300  void LoadShim() {
301    std::string library = GetShimLibraryName();
302
303    if (eglplatform_shim_.Load(library))
304      return;
305
306    base::FilePath module_path;
307    if (!PathService::Get(base::DIR_MODULE, &module_path))
308      LOG(ERROR) << "failed to get DIR_MODULE from PathService";
309    base::FilePath library_path = module_path.Append(library);
310
311    if (eglplatform_shim_.Load(library_path.value()))
312      return;
313
314    LOG(FATAL) << "failed to load " << library;
315  }
316
317  void Initialize() {
318    LoadShim();
319    shim_initialized_ = eglplatform_shim_.ShimInitialize();
320  }
321
322  // OzonePlatform:
323  virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() OVERRIDE {
324    return surface_factory_ozone_.get();
325  }
326  virtual CursorFactoryOzone* GetCursorFactoryOzone() OVERRIDE {
327    return cursor_factory_ozone_.get();
328  }
329  virtual GpuPlatformSupport* GetGpuPlatformSupport() OVERRIDE {
330    return gpu_platform_support_.get();
331  }
332  virtual GpuPlatformSupportHost* GetGpuPlatformSupportHost() OVERRIDE {
333    return gpu_platform_support_host_.get();
334  }
335  virtual scoped_ptr<PlatformWindow> CreatePlatformWindow(
336      PlatformWindowDelegate* delegate,
337      const gfx::Rect& bounds) OVERRIDE {
338    return make_scoped_ptr<PlatformWindow>(
339        new EgltestWindow(delegate,
340                          &eglplatform_shim_,
341                          event_factory_ozone_.get(),
342                          bounds));
343  }
344
345#if defined(OS_CHROMEOS)
346  virtual scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate()
347      OVERRIDE {
348    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateOzone());
349  }
350#endif
351
352  virtual void InitializeUI() OVERRIDE {
353    device_manager_ = CreateDeviceManager();
354    if (!surface_factory_ozone_)
355      surface_factory_ozone_.reset(
356          new SurfaceFactoryEgltest(&eglplatform_shim_));
357    event_factory_ozone_.reset(
358        new EventFactoryEvdev(NULL, device_manager_.get()));
359    cursor_factory_ozone_.reset(new CursorFactoryOzone());
360    gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
361  }
362
363  virtual void InitializeGPU() OVERRIDE {
364    if (!surface_factory_ozone_)
365      surface_factory_ozone_.reset(
366          new SurfaceFactoryEgltest(&eglplatform_shim_));
367    gpu_platform_support_.reset(CreateStubGpuPlatformSupport());
368  }
369
370 private:
371  LibeglplatformShimLoader eglplatform_shim_;
372  scoped_ptr<DeviceManager> device_manager_;
373  scoped_ptr<SurfaceFactoryEgltest> surface_factory_ozone_;
374  scoped_ptr<EventFactoryEvdev> event_factory_ozone_;
375  scoped_ptr<CursorFactoryOzone> cursor_factory_ozone_;
376  scoped_ptr<GpuPlatformSupport> gpu_platform_support_;
377  scoped_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
378
379  bool shim_initialized_;
380
381  DISALLOW_COPY_AND_ASSIGN(OzonePlatformEgltest);
382};
383
384}  // namespace
385
386OzonePlatform* CreateOzonePlatformEgltest() {
387  OzonePlatformEgltest* platform = new OzonePlatformEgltest;
388  platform->Initialize();
389  return platform;
390}
391
392}  // namespace ui
393