summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreveman <reveman@chromium.org>2015-11-19 14:16:48 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-19 22:17:34 +0000
commitb195f41daf755c80d31f964538b1159c81d58e70 (patch)
tree43eb3395abffda3cf49cc9d0b376a85d545ece74
parent96bac60134c383aa929ce6139c168bd31e87f67d (diff)
downloadchromium_src-b195f41daf755c80d31f964538b1159c81d58e70.zip
chromium_src-b195f41daf755c80d31f964538b1159c81d58e70.tar.gz
chromium_src-b195f41daf755c80d31f964538b1159c81d58e70.tar.bz2
components: Add Exosphere component.
Exosphere is a set of display server classes for Chromium that can be used for building a display server on top of Ash, the Chrome Compositor and the GpuMemoryBuffer framework. The classes and interfaces are inspired by the Wayland protocol and reference implementation of a display server that uses the Wayland protocol and server library is provided in the wayland/ sub-directory. BUG=549781 TEST=components_unittests --gtest_filter=ServerTest.*:SurfaceTest.*:BufferTest.*:DisplayTest.*:ShellSurfaceTest.*:SharedMemoryTest.* Review URL: https://codereview.chromium.org/1412093006 Cr-Commit-Position: refs/heads/master@{#360672}
-rw-r--r--ash/BUILD.gn4
-rw-r--r--build/common.gypi16
-rw-r--r--build/config/BUILD.gn3
-rw-r--r--build/config/ui.gni8
-rw-r--r--chrome/browser/BUILD.gn11
-rw-r--r--chrome/browser/DEPS1
-rw-r--r--chrome/browser/chrome_browser_main_extra_parts_exo.cc59
-rw-r--r--chrome/browser/chrome_browser_main_extra_parts_exo.h37
-rw-r--r--chrome/browser/chrome_content_browser_client.cc8
-rw-r--r--chrome/chrome_browser.gypi10
-rw-r--r--chrome/common/chrome_switches.cc5
-rw-r--r--chrome/common/chrome_switches.h4
-rw-r--r--components/BUILD.gn10
-rw-r--r--components/OWNERS2
-rw-r--r--components/components.gyp7
-rw-r--r--components/exo.gypi70
-rw-r--r--components/exo/BUILD.gn88
-rw-r--r--components/exo/DEPS8
-rw-r--r--components/exo/OWNERS1
-rw-r--r--components/exo/PRESUBMIT.py12
-rw-r--r--components/exo/README2
-rw-r--r--components/exo/buffer.cc187
-rw-r--r--components/exo/buffer.h82
-rw-r--r--components/exo/buffer_unittest.cc52
-rw-r--r--components/exo/display.cc46
-rw-r--r--components/exo/display.h43
-rw-r--r--components/exo/display_unittest.cc67
-rw-r--r--components/exo/shared_memory.cc79
-rw-r--r--components/exo/shared_memory.h42
-rw-r--r--components/exo/shared_memory_unittest.cc59
-rw-r--r--components/exo/shell_surface.cc164
-rw-r--r--components/exo/shell_surface.h71
-rw-r--r--components/exo/shell_surface_unittest.cc54
-rw-r--r--components/exo/surface.cc160
-rw-r--r--components/exo/surface.h112
-rw-r--r--components/exo/surface_delegate.h26
-rw-r--r--components/exo/surface_unittest.cc105
-rw-r--r--components/exo/test/exo_test_base.cc28
-rw-r--r--components/exo/test/exo_test_base.h36
-rw-r--r--components/exo/test/exo_test_helper.cc31
-rw-r--r--components/exo/test/exo_test_helper.h36
-rw-r--r--components/exo/test/run_all_unittests.cc15
-rw-r--r--components/exo/wayland/BUILD.gn35
-rw-r--r--components/exo/wayland/DEPS3
-rw-r--r--components/exo/wayland/scoped_wl_types.cc20
-rw-r--r--components/exo/wayland/scoped_wl_types.h31
-rw-r--r--components/exo/wayland/server.cc534
-rw-r--r--components/exo/wayland/server.h57
-rw-r--r--components/exo/wayland/server_unittest.cc97
49 files changed, 2637 insertions, 1 deletions
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index deb3226..067e045 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -196,6 +196,10 @@ source_set("test_support") {
if (is_win) {
deps += [ "//ui/platform_window/win" ]
}
+
+ if (is_chromeos) {
+ deps += [ "//ui/display" ]
+ }
}
source_set("test_support_with_content") {
diff --git a/build/common.gypi b/build/common.gypi
index ca65fc3..dcdf155 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -85,6 +85,9 @@
# Enable top chrome material design.
'enable_topchrome_md%' : 0,
+ # Enable Wayland display server support.
+ 'enable_wayland_server%' : 0,
+
# Build against pre-built sysroot image on linux. By default
# the sysroot image is only used for Official builds or when cross
# compiling.
@@ -161,6 +164,7 @@
'enable_viewport%': '<(enable_viewport)',
'enable_hidpi%': '<(enable_hidpi)',
'enable_topchrome_md%': '<(enable_topchrome_md)',
+ 'enable_wayland_server%': '<(enable_wayland_server)',
'buildtype%': '<(buildtype)',
'branding%': '<(branding)',
'branding_path_component%': '<(branding)',
@@ -337,6 +341,7 @@
'enable_viewport%': '<(enable_viewport)',
'enable_hidpi%': '<(enable_hidpi)',
'enable_topchrome_md%': '<(enable_topchrome_md)',
+ 'enable_wayland_server%': '<(enable_wayland_server)',
'android_channel%': '<(android_channel)',
'use_goma%': '<(use_goma)',
'gomadir%': '<(gomadir)',
@@ -751,6 +756,13 @@
'use_glib%': 1,
}],
+ # Flags to use Wayland server support.
+ ['chromeos==1 and use_ozone==1', {
+ 'enable_wayland_server%': 1,
+ }, {
+ 'enable_wayland_server%': 0,
+ }],
+
# Flags to use pango and cairo.
['OS=="win" or OS=="mac" or OS=="ios" or OS=="android" or embedded==1', {
'use_pango%': 0,
@@ -1128,6 +1140,7 @@
'enable_viewport%': '<(enable_viewport)',
'enable_hidpi%': '<(enable_hidpi)',
'enable_topchrome_md%': '<(enable_topchrome_md)',
+ 'enable_wayland_server%': '<(enable_wayland_server)',
'image_loader_extension%': '<(image_loader_extension)',
'fastbuild%': '<(fastbuild)',
'dont_embed_build_metadata%': '<(dont_embed_build_metadata)',
@@ -2757,6 +2770,9 @@
['enable_topchrome_md==1', {
'defines': ['ENABLE_TOPCHROME_MD=1'],
}],
+ ['enable_wayland_server==1', {
+ 'defines': ['ENABLE_WAYLAND_SERVER=1'],
+ }],
['use_udev==1', {
'defines': ['USE_UDEV'],
}],
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index bfb0de7..643d4d9 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -230,6 +230,9 @@ config("feature_flags") {
if (enable_topchrome_md) {
defines += [ "ENABLE_TOPCHROME_MD=1" ]
}
+ if (enable_wayland_server) {
+ defines += [ "ENABLE_WAYLAND_SERVER=1" ]
+ }
if (proprietary_codecs) {
defines += [ "USE_PROPRIETARY_CODECS" ]
}
diff --git a/build/config/ui.gni b/build/config/ui.gni
index 3acf655..fa4f142 100644
--- a/build/config/ui.gni
+++ b/build/config/ui.gni
@@ -46,6 +46,9 @@ declare_args() {
# Whether we should use glib, a low level C utility library.
use_glib = is_linux
+
+ # Indicates if Wayland display server support is enabled.
+ enable_wayland_server = is_chromeos
}
# Additional dependent variables -----------------------------------------------
@@ -66,6 +69,11 @@ if (use_ozone) {
use_glib = false
}
+# Turn off Wayland if glib is enabled.
+if (use_glib) {
+ enable_wayland_server = false
+}
+
if (is_linux && !use_ozone) {
use_cairo = true
use_pango = true
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2a0fcbe..e1d05f9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -901,6 +901,17 @@ source_set("browser") {
if (enable_mojo_media == "utility") {
deps += [ "//media/mojo/services:application" ]
}
+
+ if (enable_wayland_server) {
+ deps += [
+ "//components/exo",
+ "//components/exo/wayland",
+ ]
+ sources += [
+ "chrome_browser_main_extra_parts_exo.cc",
+ "chrome_browser_main_extra_parts_exo.h",
+ ]
+ }
}
if (is_android) {
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 1a49bfb..27c939d 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -41,6 +41,7 @@ include_rules = [
"+components/dom_distiller",
"+components/domain_reliability",
"+components/enhanced_bookmarks",
+ "+components/exo",
"+components/favicon",
"+components/favicon_base",
"+components/feedback",
diff --git a/chrome/browser/chrome_browser_main_extra_parts_exo.cc b/chrome/browser/chrome_browser_main_extra_parts_exo.cc
new file mode 100644
index 0000000..9cecfbd
--- /dev/null
+++ b/chrome/browser/chrome_browser_main_extra_parts_exo.cc
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chrome_browser_main_extra_parts_exo.h"
+
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "components/exo/display.h"
+#include "components/exo/wayland/server.h"
+#include "content/public/browser/browser_thread.h"
+
+class ChromeBrowserMainExtraPartsExo::WaylandWatcher
+ : public base::MessagePumpLibevent::Watcher {
+ public:
+ explicit WaylandWatcher(exo::wayland::Server* server) : server_(server) {
+ base::MessageLoopForUI::current()->WatchFileDescriptor(
+ server_->GetFileDescriptor(),
+ true, // persistent
+ base::MessagePumpLibevent::WATCH_READ, &controller_, this);
+ }
+
+ // base::MessagePumpLibevent::Watcher:
+ void OnFileCanReadWithoutBlocking(int fd) override {
+ server_->Dispatch(base::TimeDelta());
+ server_->Flush();
+ }
+ void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
+
+ private:
+ base::MessagePumpLibevent::FileDescriptorWatcher controller_;
+ exo::wayland::Server* const server_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaylandWatcher);
+};
+
+ChromeBrowserMainExtraPartsExo::ChromeBrowserMainExtraPartsExo()
+ : display_(new exo::Display) {}
+
+ChromeBrowserMainExtraPartsExo::~ChromeBrowserMainExtraPartsExo() {}
+
+void ChromeBrowserMainExtraPartsExo::PreProfileInit() {
+ if (!chrome::ShouldOpenAshOnStartup())
+ return;
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableWaylandServer)) {
+ wayland_server_ = exo::wayland::Server::Create(display_.get());
+ wayland_watcher_ =
+ make_scoped_ptr(new WaylandWatcher(wayland_server_.get()));
+ }
+}
+
+void ChromeBrowserMainExtraPartsExo::PostMainMessageLoopRun() {
+ wayland_watcher_.reset();
+ wayland_server_.reset();
+}
diff --git a/chrome/browser/chrome_browser_main_extra_parts_exo.h b/chrome/browser/chrome_browser_main_extra_parts_exo.h
new file mode 100644
index 0000000..16960e9
--- /dev/null
+++ b/chrome/browser/chrome_browser_main_extra_parts_exo.h
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROME_BROWSER_MAIN_EXTRA_PARTS_EXO_H_
+#define CHROME_BROWSER_CHROME_BROWSER_MAIN_EXTRA_PARTS_EXO_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/chrome_browser_main_extra_parts.h"
+
+namespace exo {
+class Display;
+namespace wayland {
+class Server;
+}
+}
+
+class ChromeBrowserMainExtraPartsExo : public ChromeBrowserMainExtraParts {
+ public:
+ ChromeBrowserMainExtraPartsExo();
+ ~ChromeBrowserMainExtraPartsExo() override;
+
+ // Overridden from ChromeBrowserMainExtraParts:
+ void PreProfileInit() override;
+ void PostMainMessageLoopRun() override;
+
+ private:
+ scoped_ptr<exo::Display> display_;
+ scoped_ptr<exo::wayland::Server> wayland_server_;
+ class WaylandWatcher;
+ scoped_ptr<WaylandWatcher> wayland_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainExtraPartsExo);
+};
+
+#endif // CHROME_BROWSER_CHROME_BROWSER_MAIN_EXTRA_PARTS_EXO_H_
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index fc1d416..2956d6e 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -276,6 +276,10 @@
#include "chrome/browser/media/router/presentation_service_delegate_impl.h"
#endif
+#if defined(ENABLE_WAYLAND_SERVER)
+#include "chrome/browser/chrome_browser_main_extra_parts_exo.h"
+#endif
+
using base::FileDescriptor;
using blink::WebWindowFeatures;
using content::AccessTokenStore;
@@ -753,6 +757,10 @@ content::BrowserMainParts* ChromeContentBrowserClient::CreateBrowserMainParts(
main_parts->AddParts(new ChromeBrowserMainExtraPartsX11());
#endif
+#if defined(ENABLE_WAYLAND_SERVER)
+ main_parts->AddParts(new ChromeBrowserMainExtraPartsExo());
+#endif
+
chrome::AddMetricsExtraParts(main_parts);
return main_parts;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 96acbc1..e4018c2 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -3871,6 +3871,16 @@
'../components/components.gyp:printing_browser',
]
}],
+ ['enable_wayland_server==1', {
+ 'sources': [
+ 'browser/chrome_browser_main_extra_parts_exo.cc',
+ 'browser/chrome_browser_main_extra_parts_exo.h',
+ ],
+ 'dependencies': [
+ '../components/components.gyp:exo',
+ '../components/components.gyp:exo_wayland',
+ ],
+ }],
],
},
{
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index c02bacc..0e8e595 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -1309,6 +1309,11 @@ const char kDebugPrint[] = "debug-print";
const char kDisableNewTaskManager[] = "disable-new-task-manager";
#endif // defined(ENABLE_TASK_MANAGER)
+#if defined(ENABLE_WAYLAND_SERVER)
+// Enables Wayland display server support.
+const char kEnableWaylandServer[] = "enable-wayland-server";
+#endif
+
#if defined(OS_ANDROID)
// Sets the threshold for when to disable auto-hiding the toolbar. If the
// device's width and height are above the threshold, the toolbar will never
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 4dbc24a..c66a8e9 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -393,6 +393,10 @@ extern const char kDisallowUncheckedDangerousDownloads[];
extern const char kDisableNewTaskManager[];
#endif // defined(ENABLE_TASK_MANAGER)
+#if defined(ENABLE_WAYLAND_SERVER)
+extern const char kEnableWaylandServer[];
+#endif
+
bool AboutInSettingsEnabled();
bool MdExtensionsEnabled();
bool MdPolicyPageEnabled();
diff --git a/components/BUILD.gn b/components/BUILD.gn
index f0cac84..33fbe66 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -115,6 +115,8 @@ group("all_components") {
"//components/devtools_discovery",
"//components/devtools_http_handler",
"//components/domain_reliability",
+ "//components/exo",
+ "//components/exo/wayland",
"//components/feedback",
"//components/gcm_driver/crypto",
"//components/gcm_driver/instance_id",
@@ -230,6 +232,14 @@ group("all_components") {
if (is_chromeos && enable_arc) {
deps += [ "//components/arc" ]
}
+
+ if (!use_ash) {
+ deps -= [ "//components/exo" ]
+ }
+
+ if (!use_ash || !is_linux) {
+ deps -= [ "//components/exo/wayland" ]
+ }
} else {
deps += [
"//components/autofill/ios/browser",
diff --git a/components/OWNERS b/components/OWNERS
index da54de3..c507fb6 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -115,6 +115,8 @@ per-file enhanced_bookmarks*=sky@chromium.org
per-file error_page*=mmenke@chromium.org
per-file error_page*=ttuttle@chromium.org
+per-file exo.gypi=reveman@chromium.org
+
per-file external_video_surface.gypi=boliu@chromium.org
per-file external_video_surface.gypi=qinmin@chromium.org
diff --git a/components/components.gyp b/components/components.gyp
index b98cd63..2729f9a 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -201,6 +201,11 @@
'includes': [
'rlz.gypi',
],
- }]
+ }],
+ ['use_ash==1', {
+ 'includes': [
+ 'exo.gypi',
+ ],
+ }],
],
}
diff --git a/components/exo.gypi b/components/exo.gypi
new file mode 100644
index 0000000..d4c762b
--- /dev/null
+++ b/components/exo.gypi
@@ -0,0 +1,70 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ # GN version: //components/exo
+ 'target_name': 'exo',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../ash/ash.gyp:ash',
+ '../base/base.gyp:base',
+ '../cc/cc.gyp:cc',
+ '../gpu/gpu.gyp:gpu',
+ '../skia/skia.gyp:skia',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/compositor/compositor.gyp:compositor',
+ '../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/gl/gl.gyp:gl',
+ '../ui/views/views.gyp:views',
+ ],
+ 'sources': [
+ # Note: sources list duplicated in GN build.
+ 'exo/buffer.cc',
+ 'exo/buffer.h',
+ 'exo/display.cc',
+ 'exo/display.h',
+ 'exo/shared_memory.cc',
+ 'exo/shared_memory.h',
+ 'exo/shell_surface.cc',
+ 'exo/shell_surface.h',
+ 'exo/surface.cc',
+ 'exo/surface.h',
+ 'exo/surface_delegate.h',
+ ],
+ },
+ ],
+ 'conditions': [
+ [ 'OS=="linux"', {
+ 'targets': [
+ {
+ # GN version: //components/exo:wayland
+ 'target_name': 'exo_wayland',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../skia/skia.gyp:skia',
+ '../third_party/wayland/wayland.gyp:wayland_server',
+ 'exo',
+ ],
+ 'sources': [
+ # Note: sources list duplicated in GN build.
+ 'exo/wayland/scoped_wl_types.cc',
+ 'exo/wayland/scoped_wl_types.h',
+ 'exo/wayland/server.cc',
+ 'exo/wayland/server.h',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
new file mode 100644
index 0000000..64378b8
--- /dev/null
+++ b/components/exo/BUILD.gn
@@ -0,0 +1,88 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+source_set("exo") {
+ sources = [
+ "buffer.cc",
+ "buffer.h",
+ "display.cc",
+ "display.h",
+ "shared_memory.cc",
+ "shared_memory.h",
+ "shell_surface.cc",
+ "shell_surface.h",
+ "surface.cc",
+ "surface.h",
+ "surface_delegate.h",
+ ]
+
+ deps = [
+ "//ash",
+ "//base",
+ "//cc",
+ "//gpu",
+ "//skia",
+ "//ui/aura",
+ "//ui/compositor",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/gl",
+ "//ui/views",
+ ]
+}
+
+source_set("test_support") {
+ testonly = true
+
+ sources = [
+ "test/exo_test_base.cc",
+ "test/exo_test_base.h",
+ "test/exo_test_helper.cc",
+ "test/exo_test_helper.h",
+ ]
+
+ deps = [
+ "//ash:test_support",
+ "//base",
+ "//skia",
+ "//testing/gtest",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "buffer_unittest.cc",
+ "display_unittest.cc",
+ "shared_memory_unittest.cc",
+ "shell_surface_unittest.cc",
+ "surface_unittest.cc",
+ ]
+
+ deps = [
+ "//components/user_manager",
+ "//skia",
+ "//testing/gtest",
+ "//ui/keyboard",
+ "//ui/message_center",
+ ":exo",
+ ":test_support",
+ ]
+}
+
+test("exo_unittests") {
+ sources = [
+ "test/run_all_unittests.cc",
+ ]
+ deps = [
+ "//ash:test_support",
+ ":unit_tests",
+ ]
+ if (is_linux) {
+ deps += [ "//components/exo/wayland:unit_tests" ]
+ }
+}
diff --git a/components/exo/DEPS b/components/exo/DEPS
new file mode 100644
index 0000000..af9ba7f
--- /dev/null
+++ b/components/exo/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+ash",
+ "+cc",
+ "+gpu",
+ "+third_party/khronos",
+ "+third_party/skia",
+ "+ui",
+]
diff --git a/components/exo/OWNERS b/components/exo/OWNERS
new file mode 100644
index 0000000..157836a
--- /dev/null
+++ b/components/exo/OWNERS
@@ -0,0 +1 @@
+reveman@chromium.org
diff --git a/components/exo/PRESUBMIT.py b/components/exo/PRESUBMIT.py
new file mode 100644
index 0000000..1f71871
--- /dev/null
+++ b/components/exo/PRESUBMIT.py
@@ -0,0 +1,12 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Top-level presubmit script for Exosphere.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def CheckChangeOnUpload(input_api, output_api):
+ return input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
diff --git a/components/exo/README b/components/exo/README
new file mode 100644
index 0000000..81ae61f
--- /dev/null
+++ b/components/exo/README
@@ -0,0 +1,2 @@
+This directory contains an implementation of a display server on top of
+the Aura Shell.
diff --git a/components/exo/buffer.cc b/components/exo/buffer.cc
new file mode 100644
index 0000000..2f229ad
--- /dev/null
+++ b/components/exo/buffer.cc
@@ -0,0 +1,187 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/buffer.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/output/context_provider.h"
+#include "cc/resources/single_release_callback.h"
+#include "cc/resources/texture_mailbox.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "ui/aura/env.h"
+#include "ui/compositor/compositor.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace exo {
+namespace {
+
+GLenum GLInternalFormat(gfx::BufferFormat format) {
+ const GLenum kGLInternalFormats[] = {
+ GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD, // ATC
+ GL_COMPRESSED_RGB_S3TC_DXT1_EXT, // ATCIA
+ GL_COMPRESSED_RGB_S3TC_DXT1_EXT, // DXT1
+ GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, // DXT5
+ GL_ETC1_RGB8_OES, // ETC1
+ GL_R8_EXT, // R_8
+ GL_RGBA, // RGBA_4444
+ GL_RGB, // RGBX_8888
+ GL_RGBA, // RGBA_8888
+ GL_RGB, // BGRX_8888
+ GL_BGRA_EXT, // BGRA_8888
+ GL_RGB_YUV_420_CHROMIUM, // YUV_420
+ GL_INVALID_ENUM, // YUV_420_BIPLANAR
+ GL_RGB_YCBCR_422_CHROMIUM, // UYVY_422
+ };
+ static_assert(arraysize(kGLInternalFormats) ==
+ (static_cast<int>(gfx::BufferFormat::LAST) + 1),
+ "BufferFormat::LAST must be last value of kGLInternalFormats");
+
+ DCHECK(format <= gfx::BufferFormat::LAST);
+ return kGLInternalFormats[static_cast<int>(format)];
+}
+
+gpu::gles2::GLES2Interface* GetContextGL() {
+ ui::ContextFactory* context_factory =
+ aura::Env::GetInstance()->context_factory();
+ return context_factory->SharedMainThreadContextProvider()->ContextGL();
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// Buffer, public:
+
+Buffer::Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
+ unsigned texture_target)
+ : gpu_memory_buffer_(gpu_memory_buffer.Pass()),
+ texture_target_(texture_target),
+ texture_id_(0),
+ image_id_(0) {
+ gpu::gles2::GLES2Interface* gles2 = GetContextGL();
+ // Create an image for |gpu_memory_buffer_|.
+ gfx::Size size = gpu_memory_buffer_->GetSize();
+ image_id_ = gles2->CreateImageCHROMIUM(
+ gpu_memory_buffer_->AsClientBuffer(), size.width(), size.height(),
+ GLInternalFormat(gpu_memory_buffer_->GetFormat()));
+ // Create a texture with |texture_target_|.
+ gles2->ActiveTexture(GL_TEXTURE0);
+ gles2->GenTextures(1, &texture_id_);
+ gles2->BindTexture(texture_target_, texture_id_);
+ gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ // Generate a crypto-secure random mailbox name.
+ gles2->GenMailboxCHROMIUM(mailbox_.name);
+ gles2->ProduceTextureCHROMIUM(texture_target_, mailbox_.name);
+}
+
+Buffer::~Buffer() {
+ gpu::gles2::GLES2Interface* gles2 = GetContextGL();
+ if (texture_id_)
+ gles2->DeleteTextures(1, &texture_id_);
+ if (image_id_)
+ gles2->DestroyImageCHROMIUM(image_id_);
+}
+
+scoped_ptr<cc::SingleReleaseCallback> Buffer::AcquireTextureMailbox(
+ cc::TextureMailbox* texture_mailbox) {
+ // Buffer can only be used by one client at a time. If texture id is 0, then a
+ // previous call to AcquireTextureMailbox() is using this buffer and it has
+ // not been released yet.
+ if (!texture_id_) {
+ DLOG(WARNING) << "Client tried to use a buffer that has not been released";
+ return nullptr;
+ }
+
+ // Take ownerhsip of image and texture ids.
+ unsigned texture_id = 0;
+ unsigned image_id = 0;
+ std::swap(texture_id, texture_id_);
+ DCHECK_NE(image_id_, 0u);
+ std::swap(image_id, image_id_);
+
+ // Bind texture to |texture_target_|.
+ gpu::gles2::GLES2Interface* gles2 = GetContextGL();
+ gles2->ActiveTexture(GL_TEXTURE0);
+ gles2->BindTexture(texture_target_, texture_id);
+
+ // Bind the image to texture.
+ gles2->BindTexImage2DCHROMIUM(texture_target_, image_id);
+
+ // Create a sync token to ensure that the BindTexImage2DCHROMIUM call is
+ // processed before issuing any commands that will read from texture.
+ uint64 fence_sync = gles2->InsertFenceSyncCHROMIUM();
+ gles2->ShallowFlushCHROMIUM();
+ gpu::SyncToken sync_token;
+ gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
+
+ bool is_overlay_candidate = false;
+ *texture_mailbox =
+ cc::TextureMailbox(mailbox_, sync_token, texture_target_,
+ gpu_memory_buffer_->GetSize(), is_overlay_candidate);
+ return cc::SingleReleaseCallback::Create(
+ base::Bind(&Buffer::Release, AsWeakPtr(), texture_target_,
+ texture_id, image_id))
+ .Pass();
+}
+
+gfx::Size Buffer::GetSize() const {
+ return gpu_memory_buffer_->GetSize();
+}
+
+scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const {
+ scoped_refptr<base::trace_event::TracedValue> value =
+ new base::trace_event::TracedValue;
+ value->SetInteger("width", GetSize().width());
+ value->SetInteger("height", GetSize().height());
+ value->SetInteger("format",
+ static_cast<int>(gpu_memory_buffer_->GetFormat()));
+ return value;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Buffer, private:
+
+// static
+void Buffer::Release(base::WeakPtr<Buffer> buffer,
+ unsigned texture_target,
+ unsigned texture_id,
+ unsigned image_id,
+ const gpu::SyncToken& sync_token,
+ bool is_lost) {
+ TRACE_EVENT1("exo", "Buffer::Release", "is_lost", is_lost);
+
+ gpu::gles2::GLES2Interface* gles2 = GetContextGL();
+ if (sync_token.HasData())
+ gles2->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+ gles2->ActiveTexture(GL_TEXTURE0);
+ gles2->BindTexture(texture_target, texture_id);
+ gles2->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
+
+ // Delete resources and return if buffer is gone.
+ if (!buffer) {
+ gles2->DeleteTextures(1, &texture_id);
+ gles2->DestroyImageCHROMIUM(image_id);
+ return;
+ }
+
+ DCHECK_EQ(buffer->texture_id_, 0u);
+ buffer->texture_id_ = texture_id;
+ DCHECK_EQ(buffer->image_id_, 0u);
+ buffer->image_id_ = image_id;
+
+ if (!buffer->release_callback_.is_null())
+ buffer->release_callback_.Run();
+}
+
+} // namespace exo
diff --git a/components/exo/buffer.h b/components/exo/buffer.h
new file mode 100644
index 0000000..192513b
--- /dev/null
+++ b/components/exo/buffer.h
@@ -0,0 +1,82 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_BUFFER_H_
+#define COMPONENTS_EXO_BUFFER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+}
+
+namespace cc {
+class SingleReleaseCallback;
+class TextureMailbox;
+}
+
+namespace gfx {
+class GpuMemoryBuffer;
+}
+
+namespace exo {
+
+// This class provides the content for a Surface. The mechanism by which a
+// client provides and updates the contents is the responsibility of the client
+// and not defined as part of this class.
+class Buffer : public base::SupportsWeakPtr<Buffer> {
+ public:
+ Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
+ unsigned texture_target);
+ ~Buffer();
+
+ // Set the callback to run when the buffer is no longer used by the
+ // compositor. The client is free to re-use or destroy this buffer and
+ // its backing storage after this has been called.
+ void set_release_callback(const base::Closure& release_callback) {
+ release_callback_ = release_callback;
+ }
+
+ // This function can be used to acquire a texture mailbox that is bound to
+ // the buffer. Returns a release callback on success. The release callback
+ // must be called before a new texture mailbox can be acquired.
+ scoped_ptr<cc::SingleReleaseCallback> AcquireTextureMailbox(
+ cc::TextureMailbox* mailbox);
+
+ // Returns the size of the buffer.
+ gfx::Size GetSize() const;
+
+ // Returns a trace value representing the state of the buffer.
+ scoped_refptr<base::trace_event::TracedValue> AsTracedValue() const;
+
+ private:
+ static void Release(base::WeakPtr<Buffer> buffer,
+ unsigned texture_target,
+ unsigned texture_id,
+ unsigned image_id,
+ const gpu::SyncToken& sync_token,
+ bool is_lost);
+
+ scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
+ const unsigned texture_target_;
+ unsigned texture_id_;
+ unsigned image_id_;
+ gpu::Mailbox mailbox_;
+ base::Closure release_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Buffer);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_BUFFER_H_
diff --git a/components/exo/buffer_unittest.cc b/components/exo/buffer_unittest.cc
new file mode 100644
index 0000000..4da72f4
--- /dev/null
+++ b/components/exo/buffer_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "cc/resources/single_release_callback.h"
+#include "components/exo/buffer.h"
+#include "components/exo/surface.h"
+#include "components/exo/test/exo_test_base.h"
+#include "components/exo/test/exo_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace exo {
+namespace {
+
+using BufferTest = test::ExoTestBase;
+
+void Release(int* release_call_count) {
+ (*release_call_count)++;
+}
+
+TEST_F(BufferTest, ReleaseCallback) {
+ gfx::Size buffer_size(256, 256);
+ scoped_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(),
+ GL_TEXTURE_2D));
+
+ // Set the release callback.
+ int release_call_count = 0;
+ buffer->set_release_callback(
+ base::Bind(&Release, base::Unretained(&release_call_count)));
+
+ // Acquire a texture mailbox for the contents of the buffer.
+ cc::TextureMailbox texture_mailbox;
+ scoped_ptr<cc::SingleReleaseCallback> buffer_release_callback =
+ buffer->AcquireTextureMailbox(&texture_mailbox);
+ ASSERT_TRUE(buffer_release_callback);
+
+ // Trying to acquire an already in-use buffer should fail.
+ EXPECT_FALSE(buffer->AcquireTextureMailbox(&texture_mailbox));
+
+ // Release buffer.
+ buffer_release_callback->Run(gpu::SyncToken(), false);
+
+ // Release() should have been called exactly once.
+ ASSERT_EQ(release_call_count, 1);
+}
+
+} // namespace
+} // namespace exo
diff --git a/components/exo/display.cc b/components/exo/display.cc
new file mode 100644
index 0000000..bb6a017
--- /dev/null
+++ b/components/exo/display.cc
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/display.h"
+
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "components/exo/shared_memory.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/surface.h"
+
+namespace exo {
+
+////////////////////////////////////////////////////////////////////////////////
+// Display, public:
+
+Display::Display() {}
+
+Display::~Display() {}
+
+scoped_ptr<Surface> Display::CreateSurface() {
+ TRACE_EVENT0("exo", "Display::CreateSurface");
+
+ return make_scoped_ptr(new Surface);
+}
+
+scoped_ptr<SharedMemory> Display::CreateSharedMemory(
+ const base::SharedMemoryHandle& handle,
+ size_t size) {
+ TRACE_EVENT1("exo", "Display::CreateSharedMemory", "size", size);
+
+ if (!base::SharedMemory::IsHandleValid(handle))
+ return nullptr;
+
+ return make_scoped_ptr(new SharedMemory(handle));
+}
+
+scoped_ptr<ShellSurface> Display::CreateShellSurface(Surface* surface) {
+ TRACE_EVENT1("exo", "Display::CreateShellSurface", "surface",
+ surface->AsTracedValue());
+
+ return make_scoped_ptr(new ShellSurface(surface));
+}
+
+} // namespace exo
diff --git a/components/exo/display.h b/components/exo/display.h
new file mode 100644
index 0000000..472f4c9
--- /dev/null
+++ b/components/exo/display.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_DISPLAY_H_
+#define COMPONENTS_EXO_DISPLAY_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory_handle.h"
+
+namespace exo {
+class SharedMemory;
+class ShellSurface;
+class Surface;
+
+// The core display class. This class provides functions for creating surfaces
+// and is in charge of combining the contents of multiple surfaces into one
+// displayable output.
+class Display {
+ public:
+ Display();
+ ~Display();
+
+ // Creates a new surface.
+ scoped_ptr<Surface> CreateSurface();
+
+ // Creates a shared memory segment from |handle| of |size| with the
+ // given |id|. This function takes ownership of |handle|.
+ scoped_ptr<SharedMemory> CreateSharedMemory(
+ const base::SharedMemoryHandle& handle,
+ size_t size);
+
+ // Creates a shell surface for an existing surface.
+ scoped_ptr<ShellSurface> CreateShellSurface(Surface* surface);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Display);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DISPLAY_H_
diff --git a/components/exo/display_unittest.cc b/components/exo/display_unittest.cc
new file mode 100644
index 0000000..8274529
--- /dev/null
+++ b/components/exo/display_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/buffer.h"
+#include "components/exo/display.h"
+#include "components/exo/shared_memory.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/surface.h"
+#include "components/exo/test/exo_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace exo {
+namespace {
+
+using DisplayTest = test::ExoTestBase;
+
+TEST_F(DisplayTest, CreateSurface) {
+ scoped_ptr<Display> display(new Display);
+
+ // Creating a surface should succeed.
+ scoped_ptr<Surface> surface = display->CreateSurface();
+ EXPECT_TRUE(surface);
+}
+
+TEST_F(DisplayTest, CreateSharedMemory) {
+ scoped_ptr<Display> display(new Display);
+
+ int shm_size = 8192;
+ scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory);
+ bool rv = shared_memory->CreateAnonymous(shm_size);
+ ASSERT_TRUE(rv);
+
+ base::SharedMemoryHandle handle =
+ base::SharedMemory::DuplicateHandle(shared_memory->handle());
+ ASSERT_TRUE(base::SharedMemory::IsHandleValid(handle));
+
+ // Creating a shared memory instance from a valid handle should succeed.
+ scoped_ptr<SharedMemory> shm1 = display->CreateSharedMemory(handle, shm_size);
+ EXPECT_TRUE(shm1);
+
+ // Creating a shared memory instance from a invalid handle should fail.
+ scoped_ptr<SharedMemory> shm2 =
+ display->CreateSharedMemory(base::SharedMemoryHandle(), shm_size);
+ EXPECT_FALSE(shm2);
+}
+
+TEST_F(DisplayTest, CreateShellSurface) {
+ scoped_ptr<Display> display(new Display);
+
+ // Create two surfaces.
+ scoped_ptr<Surface> surface1 = display->CreateSurface();
+ EXPECT_TRUE(surface1);
+ scoped_ptr<Surface> surface2 = display->CreateSurface();
+ EXPECT_TRUE(surface2);
+
+ // Create a shell surface for surface1.
+ scoped_ptr<ShellSurface> shell_surface1 =
+ display->CreateShellSurface(surface1.get());
+
+ // Create a shell surface for surface2.
+ scoped_ptr<ShellSurface> shell_surface2 =
+ display->CreateShellSurface(surface2.get());
+}
+
+} // namespace
+} // namespace exo
diff --git a/components/exo/shared_memory.cc b/components/exo/shared_memory.cc
new file mode 100644
index 0000000..912cb8c
--- /dev/null
+++ b/components/exo/shared_memory.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/shared_memory.h"
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "components/exo/buffer.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/aura/env.h"
+#include "ui/compositor/compositor.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace exo {
+namespace {
+
+bool IsSupportedFormat(gfx::BufferFormat format) {
+ return format == gfx::BufferFormat::RGBX_8888 ||
+ format == gfx::BufferFormat::RGBA_8888 ||
+ format == gfx::BufferFormat::BGRX_8888 ||
+ format == gfx::BufferFormat::BGRA_8888;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// SharedMemory, public:
+
+SharedMemory::SharedMemory(const base::SharedMemoryHandle& handle)
+ : shared_memory_(handle, true /* read-only */) {}
+
+SharedMemory::~SharedMemory() {}
+
+scoped_ptr<Buffer> SharedMemory::CreateBuffer(const gfx::Size& size,
+ gfx::BufferFormat format,
+ unsigned offset,
+ int stride) {
+ TRACE_EVENT2("exo", "SharedMemory::CreateBuffer", "size", size.ToString(),
+ "format", static_cast<int>(format));
+
+ if (!IsSupportedFormat(format)) {
+ DLOG(WARNING) << "Failed to create shm buffer. Unsupported format 0x"
+ << static_cast<int>(format);
+ return nullptr;
+ }
+
+ if (!base::IsValueInRangeForNumericType<size_t>(stride) ||
+ gfx::RowSizeForBufferFormat(size.width(), format, 0) >
+ static_cast<size_t>(stride) ||
+ static_cast<size_t>(stride) & 3) {
+ DLOG(WARNING) << "Failed to create shm buffer. Unsupported stride "
+ << stride;
+ return nullptr;
+ }
+
+ gfx::GpuMemoryBufferHandle handle;
+ handle.type = gfx::SHARED_MEMORY_BUFFER;
+ handle.handle = base::SharedMemory::DuplicateHandle(shared_memory_.handle());
+ handle.offset = offset;
+ handle.stride = stride;
+
+ scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
+ aura::Env::GetInstance()
+ ->context_factory()
+ ->GetGpuMemoryBufferManager()
+ ->CreateGpuMemoryBufferFromHandle(handle, size, format);
+ if (!gpu_memory_buffer) {
+ LOG(ERROR) << "Failed to create GpuMemoryBuffer from handle";
+ return nullptr;
+ }
+
+ return make_scoped_ptr(new Buffer(gpu_memory_buffer.Pass(), GL_TEXTURE_2D));
+}
+
+} // namespace exo
diff --git a/components/exo/shared_memory.h b/components/exo/shared_memory.h
new file mode 100644
index 0000000..53ba8ce
--- /dev/null
+++ b/components/exo/shared_memory.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_SHARED_MEMORY_H_
+#define COMPONENTS_EXO_SHARED_MEMORY_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace exo {
+class Buffer;
+
+// Shared memory abstraction. Provides a wrapper around base::SharedMemory
+// with functionality to create buffers from the memory to use for display.
+class SharedMemory {
+ public:
+ explicit SharedMemory(const base::SharedMemoryHandle& handle);
+ ~SharedMemory();
+
+ // Creates a buffer from the shared memory. The buffer is created offset bytes
+ // into the memory and has width and height as specified. The stride
+ // arguments specifies the number of bytes from beginning of one row to the
+ // beginning of the next. The format is the pixel format of the buffer and
+ // must be one of RGBX_8888, RGBA_8888, BGRX_8888, BGRA_8888.
+ scoped_ptr<Buffer> CreateBuffer(const gfx::Size& size,
+ gfx::BufferFormat format,
+ unsigned offset,
+ int stride);
+
+ private:
+ base::SharedMemory shared_memory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedMemory);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_SHARED_MEMORY_H_
diff --git a/components/exo/shared_memory_unittest.cc b/components/exo/shared_memory_unittest.cc
new file mode 100644
index 0000000..3579630
--- /dev/null
+++ b/components/exo/shared_memory_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/buffer.h"
+#include "components/exo/shared_memory.h"
+#include "components/exo/test/exo_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/buffer_format_util.h"
+
+namespace exo {
+namespace {
+
+using SharedMemoryTest = test::ExoTestBase;
+
+scoped_ptr<SharedMemory> CreateSharedMemory(size_t size) {
+ scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory);
+ bool rv = shared_memory->CreateAnonymous(size);
+ DCHECK(rv);
+ base::SharedMemoryHandle handle =
+ base::SharedMemory::DuplicateHandle(shared_memory->handle());
+ DCHECK(base::SharedMemory::IsHandleValid(handle));
+ return make_scoped_ptr(new SharedMemory(handle));
+}
+
+TEST_F(SharedMemoryTest, CreateBuffer) {
+ const gfx::Size buffer_size(256, 256);
+ const gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
+
+ scoped_ptr<SharedMemory> shared_memory =
+ CreateSharedMemory(gfx::BufferSizeForBufferFormat(buffer_size, format));
+ ASSERT_TRUE(shared_memory);
+
+ // Creating a full size buffer should succeed.
+ scoped_ptr<Buffer> buffer = shared_memory->CreateBuffer(
+ buffer_size, format, 0,
+ gfx::RowSizeForBufferFormat(buffer_size.width(), format, 0));
+ EXPECT_TRUE(buffer);
+
+ // Creating a buffer for the top-left rectangle should succeed.
+ const gfx::Size top_left_buffer_size(128, 128);
+ scoped_ptr<Buffer> top_left_buffer = shared_memory->CreateBuffer(
+ top_left_buffer_size, format, 0,
+ gfx::RowSizeForBufferFormat(buffer_size.width(), format, 0));
+ EXPECT_TRUE(top_left_buffer);
+
+ // Creating a buffer for the bottom-right rectangle should succeed.
+ const gfx::Size bottom_right_buffer_size(64, 64);
+ scoped_ptr<Buffer> bottom_right_buffer = shared_memory->CreateBuffer(
+ bottom_right_buffer_size, format,
+ (buffer_size.height() - bottom_right_buffer_size.height()) *
+ gfx::RowSizeForBufferFormat(buffer_size.width(), format, 0) +
+ (buffer_size.width() - bottom_right_buffer_size.width()) * 4,
+ gfx::RowSizeForBufferFormat(buffer_size.width(), format, 0));
+ EXPECT_TRUE(bottom_right_buffer);
+}
+
+} // namespace
+} // namespace exo
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
new file mode 100644
index 0000000..0e7e90a
--- /dev/null
+++ b/components/exo/shell_surface.cc
@@ -0,0 +1,164 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/shell_surface.h"
+
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "components/exo/surface.h"
+#include "ui/aura/window.h"
+#include "ui/base/hit_test.h"
+#include "ui/views/widget/widget.h"
+
+namespace exo {
+namespace {
+
+class CustomFrameView : public views::NonClientFrameView {
+ public:
+ explicit CustomFrameView(views::Widget* widget) : widget_(widget) {}
+ ~CustomFrameView() override {}
+
+ // Overridden from views::NonClientFrameView:
+ gfx::Rect GetBoundsForClientView() const override { return bounds(); }
+ gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const override {
+ return client_bounds;
+ }
+ int NonClientHitTest(const gfx::Point& point) override {
+ return widget_->client_view()->NonClientHitTest(point);
+ }
+ void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override {}
+ void ResetWindowControls() override {}
+ void UpdateWindowIcon() override {}
+ void UpdateWindowTitle() override {}
+ void SizeConstraintsChanged() override {}
+
+ private:
+ views::Widget* const widget_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomFrameView);
+};
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// ShellSurface, public:
+
+ShellSurface::ShellSurface(Surface* surface)
+ : surface_(surface), show_state_(ui::SHOW_STATE_END) {
+ surface_->SetSurfaceDelegate(this);
+}
+
+ShellSurface::~ShellSurface() {
+ if (surface_)
+ surface_->SetSurfaceDelegate(nullptr);
+ if (widget_)
+ widget_->CloseNow();
+}
+
+void ShellSurface::Show() {
+ TRACE_EVENT0("exo", "ShellSurface::Show");
+
+ if (!widget_ && show_state_ == ui::SHOW_STATE_END)
+ show_state_ = ui::SHOW_STATE_DEFAULT;
+}
+
+void ShellSurface::SetToplevel() {
+ TRACE_EVENT0("exo", "ShellSurface::SetToplevel");
+
+ if (!widget_)
+ show_state_ = ui::SHOW_STATE_NORMAL;
+}
+
+void ShellSurface::SetFullscreen(bool fullscreen) {
+ TRACE_EVENT1("exo", "ShellSurface::SetFullscreen", "fullscreen", fullscreen);
+
+ if (widget_) {
+ widget_->SetFullscreen(fullscreen);
+ return;
+ }
+
+ show_state_ = fullscreen ? ui::SHOW_STATE_FULLSCREEN : ui::SHOW_STATE_DEFAULT;
+}
+
+void ShellSurface::SetTitle(const base::string16& title) {
+ TRACE_EVENT1("exo", "ShellSurface::SetTitle", "title",
+ base::UTF16ToUTF8(title));
+
+ title_ = title;
+ if (widget_)
+ widget_->UpdateWindowTitle();
+}
+
+scoped_refptr<base::trace_event::TracedValue> ShellSurface::AsTracedValue()
+ const {
+ scoped_refptr<base::trace_event::TracedValue> value =
+ new base::trace_event::TracedValue;
+ value->SetString("title", base::UTF16ToUTF8(title_));
+ return value;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SurfaceDelegate overrides:
+
+void ShellSurface::OnSurfaceDestroying() {
+ surface_ = nullptr;
+}
+
+void ShellSurface::OnSurfaceCommit() {
+ if (widget_ || show_state_ == ui::SHOW_STATE_END)
+ return;
+
+ views::Widget::InitParams params;
+ params.type = views::Widget::InitParams::TYPE_WINDOW;
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.delegate = this;
+ params.shadow_type = show_state_ == ui::SHOW_STATE_NORMAL
+ ? views::Widget::InitParams::SHADOW_TYPE_DROP
+ : views::Widget::InitParams::SHADOW_TYPE_NONE;
+ params.opacity = show_state_ == ui::SHOW_STATE_NORMAL
+ ? views::Widget::InitParams::OPAQUE_WINDOW
+ : views::Widget::InitParams::TRANSLUCENT_WINDOW;
+ params.show_state = show_state_;
+ params.parent = ash::Shell::GetContainer(
+ ash::Shell::GetPrimaryRootWindow(), ash::kShellWindowId_DefaultContainer);
+ widget_.reset(new views::Widget);
+ widget_->Init(params);
+ widget_->GetNativeWindow()->set_owned_by_parent(false);
+ widget_->GetNativeView()->SetName("ShellSurface");
+ widget_->Show();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// views::WidgetDelegate overrides:
+
+base::string16 ShellSurface::GetWindowTitle() const {
+ return title_;
+}
+
+views::Widget* ShellSurface::GetWidget() {
+ return widget_.get();
+}
+
+const views::Widget* ShellSurface::GetWidget() const {
+ return widget_.get();
+}
+
+views::View* ShellSurface::GetContentsView() {
+ return surface_;
+}
+
+views::NonClientFrameView* ShellSurface::CreateNonClientFrameView(
+ views::Widget* widget) {
+ // Default show state is borderless and requires a custom frame view as the
+ // default one does not support this.
+ return show_state_ != ui::SHOW_STATE_NORMAL ? new CustomFrameView(widget)
+ : nullptr;
+}
+
+} // namespace exo
diff --git a/components/exo/shell_surface.h b/components/exo/shell_surface.h
new file mode 100644
index 0000000..e997d01
--- /dev/null
+++ b/components/exo/shell_surface.h
@@ -0,0 +1,71 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_SHELL_SURFACE_H_
+#define COMPONENTS_EXO_SHELL_SURFACE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "components/exo/surface_delegate.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+}
+
+namespace exo {
+class Surface;
+
+// This class provides functions for treating a surfaces like toplevel,
+// fullscreen or popup widgets, move, resize or maximize them, associate
+// metadata like title and class, etc.
+class ShellSurface : public SurfaceDelegate, public views::WidgetDelegate {
+ public:
+ explicit ShellSurface(Surface* surface);
+ ~ShellSurface() override;
+
+ // Show surface at the time of the next commit.
+ void Show();
+
+ // Show surface as a toplevel window with decorations.
+ void SetToplevel();
+
+ // Make the surface fullscreen.
+ void SetFullscreen(bool fullscreen);
+
+ // Set title for surface.
+ void SetTitle(const base::string16& title);
+
+ // Returns a trace value representing the state of the surface.
+ scoped_refptr<base::trace_event::TracedValue> AsTracedValue() const;
+
+ // Overridden from SurfaceDelegate:
+ void OnSurfaceDestroying() override;
+ void OnSurfaceCommit() override;
+
+ // Overridden from views::WidgetDelegate:
+ base::string16 GetWindowTitle() const override;
+ views::Widget* GetWidget() override;
+ const views::Widget* GetWidget() const override;
+ views::View* GetContentsView() override;
+ views::NonClientFrameView* CreateNonClientFrameView(
+ views::Widget* widget) override;
+
+ private:
+ scoped_ptr<views::Widget> widget_;
+ Surface* surface_;
+ ui::WindowShowState show_state_;
+ base::string16 title_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellSurface);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_SHELL_SURFACE_H_
diff --git a/components/exo/shell_surface_unittest.cc b/components/exo/shell_surface_unittest.cc
new file mode 100644
index 0000000..6ade284
--- /dev/null
+++ b/components/exo/shell_surface_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/surface.h"
+#include "components/exo/test/exo_test_base.h"
+#include "components/exo/test/exo_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace exo {
+namespace {
+
+using ShellSurfaceTest = test::ExoTestBase;
+
+TEST_F(ShellSurfaceTest, Show) {
+ scoped_ptr<Surface> surface(new Surface);
+ scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+
+ shell_surface->Show();
+ surface->Commit();
+}
+
+TEST_F(ShellSurfaceTest, SetToplevel) {
+ scoped_ptr<Surface> surface(new Surface);
+ scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+
+ shell_surface->SetToplevel();
+ surface->Commit();
+}
+
+TEST_F(ShellSurfaceTest, SetFullscreen) {
+ scoped_ptr<Surface> surface(new Surface);
+ scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+
+ shell_surface->SetFullscreen(true);
+ surface->Commit();
+
+ // Fullscreen mode can change after the initial Commit().
+ shell_surface->SetFullscreen(false);
+ surface->Commit();
+}
+
+TEST_F(ShellSurfaceTest, SetTitle) {
+ scoped_ptr<Surface> surface(new Surface);
+ scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+
+ shell_surface->SetTitle(base::string16(base::ASCIIToUTF16("test")));
+ surface->Commit();
+}
+
+} // namespace
+} // namespace exo
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
new file mode 100644
index 0000000..6e21edc
--- /dev/null
+++ b/components/exo/surface.cc
@@ -0,0 +1,160 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/surface.h"
+
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/resources/single_release_callback.h"
+#include "components/exo/buffer.h"
+#include "components/exo/surface_delegate.h"
+#include "ui/compositor/layer.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace exo {
+
+////////////////////////////////////////////////////////////////////////////////
+// Surface, public:
+
+Surface::Surface() : compositor_(nullptr), delegate_(nullptr) {
+ SetLayer(new ui::Layer(ui::LAYER_SOLID_COLOR));
+ set_owned_by_client();
+}
+
+Surface::~Surface() {
+ if (delegate_)
+ delegate_->OnSurfaceDestroying();
+
+ layer()->SetShowSolidColorContent();
+
+ if (compositor_)
+ compositor_->RemoveObserver(this);
+
+ // Call pending frame callbacks with a null frame time to indicate that they
+ // have been cancelled.
+ frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
+ active_frame_callbacks_.splice(active_frame_callbacks_.end(),
+ frame_callbacks_);
+ for (const auto& frame_callback : active_frame_callbacks_)
+ frame_callback.Run(base::TimeTicks());
+}
+
+void Surface::Attach(Buffer* buffer) {
+ TRACE_EVENT1("exo", "Surface::Attach", "buffer", buffer->AsTracedValue());
+
+ pending_buffer_ = buffer ? buffer->AsWeakPtr() : base::WeakPtr<Buffer>();
+ PreferredSizeChanged();
+}
+
+void Surface::Damage(const gfx::Rect& damage) {
+ TRACE_EVENT1("exo", "Surface::Damage", "damage", damage.ToString());
+
+ pending_damage_.Union(damage);
+}
+
+void Surface::RequestFrameCallback(const FrameCallback& callback) {
+ TRACE_EVENT0("exo", "Surface::RequestFrameCallback");
+
+ pending_frame_callbacks_.push_back(callback);
+}
+
+void Surface::SetOpaqueRegion(const SkRegion& region) {
+ TRACE_EVENT1("exo", "Surface::SetOpaqueRegion", "region",
+ gfx::SkIRectToRect(region.getBounds()).ToString());
+
+ pending_opaque_region_ = region;
+}
+
+void Surface::Commit() {
+ TRACE_EVENT0("exo", "Surface::Commit");
+
+ if (delegate_)
+ delegate_->OnSurfaceCommit();
+
+ cc::TextureMailbox texture_mailbox;
+ scoped_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback;
+ if (pending_buffer_) {
+ texture_mailbox_release_callback =
+ pending_buffer_->AcquireTextureMailbox(&texture_mailbox);
+ pending_buffer_.reset();
+ } else {
+ // Show solid color content if there is no pending buffer.
+ layer()->SetShowSolidColorContent();
+ }
+
+ if (texture_mailbox_release_callback) {
+ // Update layer with the new contents.
+ layer()->SetTextureMailbox(texture_mailbox,
+ texture_mailbox_release_callback.Pass(),
+ texture_mailbox.size_in_pixels());
+ layer()->SetTextureFlipped(false);
+ layer()->SetBounds(gfx::Rect(layer()->bounds().origin(),
+ texture_mailbox.size_in_pixels()));
+ layer()->SetFillsBoundsOpaquely(pending_opaque_region_.contains(
+ gfx::RectToSkIRect(gfx::Rect(texture_mailbox.size_in_pixels()))));
+ }
+
+ // Schedule redraw of the damage region.
+ layer()->SchedulePaint(pending_damage_);
+ pending_damage_ = gfx::Rect();
+
+ ui::Compositor* compositor = layer()->GetCompositor();
+ if (compositor && !pending_frame_callbacks_.empty()) {
+ // Start observing the compositor for frame callbacks.
+ if (!compositor_) {
+ compositor->AddObserver(this);
+ compositor_ = compositor;
+ }
+
+ // Move pending frame callbacks to the end of |frame_callbacks_|.
+ frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
+ }
+}
+
+void Surface::SetSurfaceDelegate(SurfaceDelegate* delegate) {
+ DCHECK(!delegate_ || !delegate);
+ delegate_ = delegate;
+}
+
+scoped_refptr<base::trace_event::TracedValue> Surface::AsTracedValue() const {
+ scoped_refptr<base::trace_event::TracedValue> value =
+ new base::trace_event::TracedValue;
+ value->SetString("name", layer()->name());
+ return value;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// views::Views overrides:
+
+gfx::Size Surface::GetPreferredSize() const {
+ return pending_buffer_ ? pending_buffer_->GetSize() : layer()->size();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ui::CompositorObserver overrides:
+
+void Surface::OnCompositingDidCommit(ui::Compositor* compositor) {
+ // Move frame callbacks to the end of |active_frame_callbacks_|.
+ active_frame_callbacks_.splice(active_frame_callbacks_.end(),
+ frame_callbacks_);
+}
+
+void Surface::OnCompositingStarted(ui::Compositor* compositor,
+ base::TimeTicks start_time) {
+ // Run all frame callbacks associated with the compositor's active tree.
+ while (!active_frame_callbacks_.empty()) {
+ active_frame_callbacks_.front().Run(start_time);
+ active_frame_callbacks_.pop_front();
+ }
+}
+
+void Surface::OnCompositingShuttingDown(ui::Compositor* compositor) {
+ compositor->RemoveObserver(this);
+ compositor_ = nullptr;
+}
+
+} // namespace exo
diff --git a/components/exo/surface.h b/components/exo/surface.h
new file mode 100644
index 0000000..4db760e
--- /dev/null
+++ b/components/exo/surface.h
@@ -0,0 +1,112 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_SURFACE_H_
+#define COMPONENTS_EXO_SURFACE_H_
+
+#include <list>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/compositor/compositor_observer.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/view.h"
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+}
+
+namespace exo {
+class Buffer;
+class SurfaceDelegate;
+
+// This class represents a rectangular area that is displayed on the screen.
+// It has a location, size and pixel contents.
+class Surface : public views::View, public ui::CompositorObserver {
+ public:
+ Surface();
+ ~Surface() override;
+
+ // Set a buffer as the content of this surface. A buffer can only be attached
+ // to one surface at a time.
+ void Attach(Buffer* buffer);
+
+ // Describe the regions where the pending buffer is different from the
+ // current surface contents, and where the surface therefore needs to be
+ // repainted.
+ void Damage(const gfx::Rect& rect);
+
+ // Request notification when the next frame is displayed. Useful for
+ // throttling redrawing operations, and driving animations.
+ using FrameCallback = base::Callback<void(base::TimeTicks frame_time)>;
+ void RequestFrameCallback(const FrameCallback& callback);
+
+ // This sets the region of the surface that contains opaque content.
+ void SetOpaqueRegion(const SkRegion& region);
+
+ // Surface state (damage regions, attached buffers, etc.) is double-buffered.
+ // A Commit() call atomically applies all pending state, replacing the
+ // current state.
+ void Commit();
+
+ // Set the surface delegate.
+ void SetSurfaceDelegate(SurfaceDelegate* delegate);
+
+ // Returns a trace value representing the state of the surface.
+ scoped_refptr<base::trace_event::TracedValue> AsTracedValue() const;
+
+ bool HasPendingDamageForTesting() const { return !pending_damage_.IsEmpty(); }
+
+ // Overridden from views::View:
+ gfx::Size GetPreferredSize() const override;
+
+ // Overridden from ui::CompositorObserver:
+ void OnCompositingDidCommit(ui::Compositor* compositor) override;
+ void OnCompositingStarted(ui::Compositor* compositor,
+ base::TimeTicks start_time) override;
+ void OnCompositingEnded(ui::Compositor* compositor) override {}
+ void OnCompositingAborted(ui::Compositor* compositor) override {}
+ void OnCompositingLockStateChanged(ui::Compositor* compositor) override {}
+ void OnCompositingShuttingDown(ui::Compositor* compositor) override;
+
+ private:
+ // The buffer that will become the content of surface when Commit() is called.
+ base::WeakPtr<Buffer> pending_buffer_;
+
+ // The damage region to schedule paint for when Commit() is called.
+ // TODO(reveman): Use SkRegion here after adding a version of
+ // ui::Layer::SchedulePaint that takes a SkRegion.
+ gfx::Rect pending_damage_;
+
+ // These lists contains the callbacks to notify the client when it is a good
+ // time to start producing a new frame. These callbacks move to
+ // |frame_callbacks_| when Commit() is called. Later they are moved to
+ // |active_frame_callbacks_| when the effect of the Commit() is reflected in
+ // the compositor's active layer tree. The callbacks fire once we're notified
+ // that the compositor started drawing that active layer tree.
+ std::list<FrameCallback> pending_frame_callbacks_;
+ std::list<FrameCallback> frame_callbacks_;
+ std::list<FrameCallback> active_frame_callbacks_;
+
+ // The opaque region to take effect when Commit() is called.
+ SkRegion pending_opaque_region_;
+
+ // The compsitor being observer or null if not observing a compositor.
+ ui::Compositor* compositor_;
+
+ // This can be set to have some functions delegated. E.g. ShellSurface class
+ // can set this to handle Commit() and apply any double buffered state it
+ // maintains.
+ SurfaceDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(Surface);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_SURFACE_H_
diff --git a/components/exo/surface_delegate.h b/components/exo/surface_delegate.h
new file mode 100644
index 0000000..bfda475
--- /dev/null
+++ b/components/exo/surface_delegate.h
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_SURFACE_DELEGATE_H_
+#define COMPONENTS_EXO_SURFACE_DELEGATE_H_
+
+namespace exo {
+
+// Handles events on surfaces in context-specific ways.
+class SurfaceDelegate {
+ public:
+ // Called when the surface is destroyed. The delegate can use this
+ // opportunity to delete itself if necessary.
+ virtual void OnSurfaceDestroying() = 0;
+
+ // Called when surface was requested to commit all double-buffered state.
+ virtual void OnSurfaceCommit() = 0;
+
+ protected:
+ virtual ~SurfaceDelegate() {}
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_SURFACE_DELEGATE_H_
diff --git a/components/exo/surface_unittest.cc b/components/exo/surface_unittest.cc
new file mode 100644
index 0000000..f867a8d
--- /dev/null
+++ b/components/exo/surface_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "components/exo/buffer.h"
+#include "components/exo/surface.h"
+#include "components/exo/test/exo_test_base.h"
+#include "components/exo/test/exo_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/views/test/widget_test.h"
+
+namespace exo {
+namespace {
+
+using SurfaceTest = test::ExoTestBase;
+
+void ReleaseBuffer(int* release_buffer_call_count) {
+ (*release_buffer_call_count)++;
+}
+
+TEST_F(SurfaceTest, Attach) {
+ gfx::Size buffer_size(256, 256);
+ scoped_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(),
+ GL_TEXTURE_2D));
+
+ // Set the release callback that will be run when buffer is no longer in use.
+ int release_buffer_call_count = 0;
+ buffer->set_release_callback(
+ base::Bind(&ReleaseBuffer, base::Unretained(&release_buffer_call_count)));
+
+ scoped_ptr<Surface> surface1(new Surface);
+ scoped_ptr<Surface> surface2(new Surface);
+
+ // Attach the buffer to surface1.
+ surface1->Attach(buffer.get());
+ surface1->Commit();
+
+ // Attaching buffer to surface2 when it is already attached to surface1
+ // should fail and buffer should remain attached to surface1.
+ surface2->Attach(buffer.get());
+ surface2->Commit();
+
+ // Attach a null buffer to surface1, this should release the previously
+ // attached buffer.
+ surface1->Attach(nullptr);
+ surface1->Commit();
+ ASSERT_EQ(release_buffer_call_count, 1);
+}
+
+TEST_F(SurfaceTest, Damage) {
+ gfx::Size buffer_size(256, 256);
+ scoped_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(),
+ GL_TEXTURE_2D));
+ scoped_ptr<Surface> surface(new Surface);
+
+ // Attach the buffer to the surface. This will update the pending bounds of
+ // the surface to the buffer size.
+ surface->Attach(buffer.get());
+
+ // Mark area inside the bounds of the surface as damaged. This should result
+ // in pending damage.
+ surface->Damage(gfx::Rect(0, 0, 10, 10));
+ EXPECT_TRUE(surface->HasPendingDamageForTesting());
+}
+
+void SetFrameTime(base::TimeTicks* result, base::TimeTicks frame_time) {
+ *result = frame_time;
+}
+
+TEST_F(SurfaceTest, RequestFrameCallback) {
+ scoped_ptr<Surface> surface(new Surface);
+
+ base::TimeTicks frame_time;
+ surface->RequestFrameCallback(
+ base::Bind(&SetFrameTime, base::Unretained(&frame_time)));
+ surface->Commit();
+
+ // Callback should not run synchronously.
+ EXPECT_TRUE(frame_time.is_null());
+}
+
+TEST_F(SurfaceTest, SetOpaqueRegion) {
+ scoped_ptr<Surface> surface(new Surface);
+
+ // Setting a non-empty opaque region should succeed.
+ surface->SetOpaqueRegion(SkRegion(SkIRect::MakeWH(256, 256)));
+
+ // Setting an empty opaque region should succeed.
+ surface->SetOpaqueRegion(SkRegion(SkIRect::MakeEmpty()));
+}
+
+TEST_F(SurfaceTest, Commit) {
+ scoped_ptr<Surface> surface(new Surface);
+
+ // Calling commit without a buffer should succeed.
+ surface->Commit();
+}
+
+} // namespace
+} // namespace exo
diff --git a/components/exo/test/exo_test_base.cc b/components/exo/test/exo_test_base.cc
new file mode 100644
index 0000000..c988048
--- /dev/null
+++ b/components/exo/test/exo_test_base.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/test/exo_test_base.h"
+
+#include "components/exo/test/exo_test_helper.h"
+
+namespace exo {
+namespace test {
+
+////////////////////////////////////////////////////////////////////////////////
+// ExoTestBase, public:
+
+ExoTestBase::ExoTestBase() : exo_test_helper_(new ExoTestHelper) {}
+
+ExoTestBase::~ExoTestBase() {}
+
+void ExoTestBase::SetUp() {
+ AshTestBase::SetUp();
+}
+
+void ExoTestBase::TearDown() {
+ AshTestBase::TearDown();
+}
+
+} // namespace test
+} // namespace exo
diff --git a/components/exo/test/exo_test_base.h b/components/exo/test/exo_test_base.h
new file mode 100644
index 0000000..98a1f86
--- /dev/null
+++ b/components/exo/test/exo_test_base.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_TEST_EXO_TEST_BASE_H_
+#define COMPONENTS_EXO_TEST_EXO_TEST_BASE_H_
+
+#include "ash/test/ash_test_base.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace exo {
+namespace test {
+class ExoTestHelper;
+
+class ExoTestBase : public ash::test::AshTestBase {
+ public:
+ ExoTestBase();
+ ~ExoTestBase() override;
+
+ // Overridden from testing::Test:
+ void SetUp() override;
+ void TearDown() override;
+
+ ExoTestHelper* exo_test_helper() { return exo_test_helper_.get(); }
+
+ private:
+ scoped_ptr<ExoTestHelper> exo_test_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExoTestBase);
+};
+
+} // namespace test
+} // namespace exo
+
+#endif // COMPONENTS_EXO_TEST_EXO_TEST_BASE_H_
diff --git a/components/exo/test/exo_test_helper.cc b/components/exo/test/exo_test_helper.cc
new file mode 100644
index 0000000..84d580a
--- /dev/null
+++ b/components/exo/test/exo_test_helper.cc
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/test/exo_test_helper.h"
+
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "ui/aura/env.h"
+#include "ui/compositor/compositor.h"
+
+namespace exo {
+namespace test {
+
+////////////////////////////////////////////////////////////////////////////////
+// ExoTestHelper, public:
+
+ExoTestHelper::ExoTestHelper() {}
+
+ExoTestHelper::~ExoTestHelper() {}
+
+scoped_ptr<gfx::GpuMemoryBuffer> ExoTestHelper::CreateGpuMemoryBuffer(
+ const gfx::Size& size) {
+ return aura::Env::GetInstance()
+ ->context_factory()
+ ->GetGpuMemoryBufferManager()
+ ->AllocateGpuMemoryBuffer(size, gfx::BufferFormat::RGBA_8888,
+ gfx::BufferUsage::GPU_READ);
+}
+
+} // namespace test
+} // namespace exo
diff --git a/components/exo/test/exo_test_helper.h b/components/exo/test/exo_test_helper.h
new file mode 100644
index 0000000..2044405
--- /dev/null
+++ b/components/exo/test/exo_test_helper.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_TEST_EXO_TEST_HELPER_H_
+#define COMPONENTS_EXO_TEST_EXO_TEST_HELPER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+class GpuMemoryBuffer;
+}
+
+namespace exo {
+namespace test {
+
+// A helper class that does common initialization required for Exosphere.
+class ExoTestHelper {
+ public:
+ ExoTestHelper();
+ ~ExoTestHelper();
+
+ // Creates a GpuMemoryBuffer instance that can be used for tests.
+ scoped_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(const gfx::Size& size);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExoTestHelper);
+};
+
+} // namespace test
+} // namespace exo
+
+#endif // COMPONENTS_EXO_TEST_EXO_TEST_HELPER_H_
diff --git a/components/exo/test/run_all_unittests.cc b/components/exo/test/run_all_unittests.cc
new file mode 100644
index 0000000..e69d387
--- /dev/null
+++ b/components/exo/test/run_all_unittests.cc
@@ -0,0 +1,15 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/test/test_suite.h"
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+
+int main(int argc, char** argv) {
+ ash::test::AuraShellTestSuite test_suite(argc, argv);
+
+ return base::LaunchUnitTestsSerially(
+ argc, argv, base::Bind(&ash::test::AuraShellTestSuite::Run,
+ base::Unretained(&test_suite)));
+}
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
new file mode 100644
index 0000000..2041108
--- /dev/null
+++ b/components/exo/wayland/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("wayland") {
+ sources = [
+ "scoped_wl_types.cc",
+ "scoped_wl_types.h",
+ "server.cc",
+ "server.h",
+ ]
+
+ defines = [ "EXO_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ "//components/exo",
+ "//skia",
+ "//third_party/wayland:wayland_server",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "server_unittest.cc",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ "//third_party/wayland:wayland_client",
+ ":wayland",
+ ]
+}
diff --git a/components/exo/wayland/DEPS b/components/exo/wayland/DEPS
new file mode 100644
index 0000000..067074d
--- /dev/null
+++ b/components/exo/wayland/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/wayland/include",
+]
diff --git a/components/exo/wayland/scoped_wl_types.cc b/components/exo/wayland/scoped_wl_types.cc
new file mode 100644
index 0000000..7050cee
--- /dev/null
+++ b/components/exo/wayland/scoped_wl_types.cc
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/wayland/scoped_wl_types.h"
+
+#include <wayland-server-core.h>
+
+namespace exo {
+namespace wayland {
+namespace internal {
+
+// static
+void ScopedWLDisplayTraits::Free(wl_display* display) {
+ wl_display_destroy(display);
+}
+
+} // namespace internal
+} // namespace wayland
+} // namespace exo
diff --git a/components/exo/wayland/scoped_wl_types.h b/components/exo/wayland/scoped_wl_types.h
new file mode 100644
index 0000000..4ee819d
--- /dev/null
+++ b/components/exo/wayland/scoped_wl_types.h
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_WAYLAND_SCOPED_WL_TYPES_H_
+#define COMPONENTS_EXO_WAYLAND_SCOPED_WL_TYPES_H_
+
+#include "base/scoped_generic.h"
+
+extern "C" {
+struct wl_display;
+}
+
+namespace exo {
+namespace wayland {
+namespace internal {
+
+struct ScopedWLDisplayTraits {
+ static wl_display* InvalidValue() { return nullptr; }
+ static void Free(wl_display* display);
+};
+
+} // namespace internal
+
+using ScopedWLDisplay =
+ base::ScopedGeneric<wl_display*, internal::ScopedWLDisplayTraits>;
+
+} // namespace wayland
+} // namespace exo
+
+#endif // COMPONENTS_EXO_WAYLAND_SCOPED_WL_TYPES_H_
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
new file mode 100644
index 0000000..d5efe22
--- /dev/null
+++ b/components/exo/wayland/server.cc
@@ -0,0 +1,534 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/wayland/server.h"
+
+#include <wayland-server-core.h>
+#include <wayland-server-protocol-core.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/cancelable_callback.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/exo/buffer.h"
+#include "components/exo/display.h"
+#include "components/exo/shared_memory.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/surface.h"
+#include "third_party/skia/include/core/SkRegion.h"
+
+namespace exo {
+namespace wayland {
+namespace {
+
+template <class T>
+T* GetUserDataAs(wl_resource* resource) {
+ return static_cast<T*>(wl_resource_get_user_data(resource));
+}
+
+template <class T>
+scoped_ptr<T> TakeUserDataAs(wl_resource* resource) {
+ scoped_ptr<T> user_data = make_scoped_ptr(GetUserDataAs<T>(resource));
+ wl_resource_set_user_data(resource, nullptr);
+ return user_data.Pass();
+}
+
+template <class T>
+void DestroyUserData(wl_resource* resource) {
+ TakeUserDataAs<T>(resource);
+}
+
+template <class T>
+void SetImplementation(wl_resource* resource,
+ const void* implementation,
+ scoped_ptr<T> user_data) {
+ wl_resource_set_implementation(resource, implementation, user_data.release(),
+ DestroyUserData<T>);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_buffer_interface:
+
+void buffer_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+const struct wl_buffer_interface buffer_implementation = {buffer_destroy};
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_surface_interface:
+
+void surface_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void surface_attach(wl_client* client,
+ wl_resource* resource,
+ wl_resource* buffer,
+ int32_t x,
+ int32_t y) {
+ // TODO(reveman): Implement buffer offset support.
+ if (x || y) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ GetUserDataAs<Surface>(resource)
+ ->Attach(buffer ? GetUserDataAs<Buffer>(buffer) : nullptr);
+}
+
+void surface_damage(wl_client* client,
+ wl_resource* resource,
+ int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height) {
+ GetUserDataAs<Surface>(resource)->Damage(gfx::Rect(x, y, width, height));
+}
+
+void handle_surface_frame_callback(wl_resource* resource,
+ base::TimeTicks frame_time) {
+ if (!frame_time.is_null()) {
+ wl_callback_send_done(resource,
+ (frame_time - base::TimeTicks()).InMilliseconds());
+ // TODO(reveman): Remove this potentially blocking flush and instead watch
+ // the file descriptor to be ready for write without blocking.
+ wl_client_flush(wl_resource_get_client(resource));
+ }
+ wl_resource_destroy(resource);
+}
+
+void surface_frame(wl_client* client,
+ wl_resource* resource,
+ uint32_t callback) {
+ wl_resource* callback_resource =
+ wl_resource_create(client, &wl_callback_interface, 1, callback);
+ if (!callback_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ // base::Unretained is safe as the resource owns the callback.
+ scoped_ptr<base::CancelableCallback<void(base::TimeTicks)>>
+ cancelable_callback(new base::CancelableCallback<void(base::TimeTicks)>(
+ base::Bind(&handle_surface_frame_callback,
+ base::Unretained(callback_resource))));
+
+ GetUserDataAs<Surface>(resource)
+ ->RequestFrameCallback(cancelable_callback->callback());
+
+ SetImplementation(callback_resource, nullptr, cancelable_callback.Pass());
+}
+
+void surface_set_opaque_region(wl_client* client,
+ wl_resource* resource,
+ wl_resource* region_resource) {
+ GetUserDataAs<Surface>(resource)->SetOpaqueRegion(
+ region_resource ? *GetUserDataAs<SkRegion>(region_resource)
+ : SkRegion(SkIRect::MakeEmpty()));
+}
+
+void surface_set_input_region(wl_client* client,
+ wl_resource* resource,
+ wl_resource* region_resource) {
+ NOTIMPLEMENTED();
+}
+
+void surface_commit(wl_client* client, wl_resource* resource) {
+ GetUserDataAs<Surface>(resource)->Commit();
+}
+
+void surface_set_buffer_transform(wl_client* client,
+ wl_resource* resource,
+ int transform) {
+ NOTIMPLEMENTED();
+}
+
+void surface_set_buffer_scale(wl_client* client,
+ wl_resource* resource,
+ int32_t scale) {
+ NOTIMPLEMENTED();
+}
+
+const struct wl_surface_interface surface_implementation = {
+ surface_destroy,
+ surface_attach,
+ surface_damage,
+ surface_frame,
+ surface_set_opaque_region,
+ surface_set_input_region,
+ surface_commit,
+ surface_set_buffer_transform,
+ surface_set_buffer_scale};
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_region_interface:
+
+void region_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void region_add(wl_client* client,
+ wl_resource* resource,
+ int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height) {
+ GetUserDataAs<SkRegion>(resource)
+ ->op(SkIRect::MakeXYWH(x, y, width, height), SkRegion::kUnion_Op);
+}
+
+static void region_subtract(wl_client* client,
+ wl_resource* resource,
+ int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height) {
+ GetUserDataAs<SkRegion>(resource)
+ ->op(SkIRect::MakeXYWH(x, y, width, height), SkRegion::kDifference_Op);
+}
+
+const struct wl_region_interface region_implementation = {
+ region_destroy, region_add, region_subtract};
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_compositor_interface:
+
+void compositor_create_surface(wl_client* client,
+ wl_resource* resource,
+ uint32_t id) {
+ scoped_ptr<Surface> surface =
+ GetUserDataAs<Display>(resource)->CreateSurface();
+ DCHECK(surface);
+
+ wl_resource* surface_resource = wl_resource_create(
+ client, &wl_surface_interface, wl_resource_get_version(resource), id);
+ if (!surface_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ SetImplementation(surface_resource, &surface_implementation, surface.Pass());
+}
+
+void compositor_create_region(wl_client* client,
+ wl_resource* resource,
+ uint32_t id) {
+ scoped_ptr<SkRegion> region(new SkRegion);
+
+ wl_resource* region_resource =
+ wl_resource_create(client, &wl_region_interface, 1, id);
+ if (!region_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ SetImplementation(region_resource, &region_implementation, region.Pass());
+}
+
+const struct wl_compositor_interface compositor_implementation = {
+ compositor_create_surface, compositor_create_region};
+
+const uint32_t compositor_version = 3;
+
+void bind_compositor(wl_client* client,
+ void* data,
+ uint32_t version,
+ uint32_t id) {
+ wl_resource* resource =
+ wl_resource_create(client, &wl_compositor_interface,
+ std::min(version, compositor_version), id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &compositor_implementation, data,
+ nullptr);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_shm_pool_interface:
+
+const struct shm_supported_format {
+ uint32_t shm_format;
+ gfx::BufferFormat buffer_format;
+} shm_supported_formats[] = {
+ {WL_SHM_FORMAT_XBGR8888, gfx::BufferFormat::RGBX_8888},
+ {WL_SHM_FORMAT_ABGR8888, gfx::BufferFormat::RGBA_8888},
+ {WL_SHM_FORMAT_XRGB8888, gfx::BufferFormat::BGRX_8888},
+ {WL_SHM_FORMAT_ARGB8888, gfx::BufferFormat::BGRA_8888}};
+
+void shm_pool_create_buffer(wl_client* client,
+ wl_resource* resource,
+ uint32_t id,
+ int32_t offset,
+ int32_t width,
+ int32_t height,
+ int32_t stride,
+ uint32_t format) {
+ const auto supported_format =
+ std::find_if(shm_supported_formats,
+ shm_supported_formats + arraysize(shm_supported_formats),
+ [format](const shm_supported_format& supported_format) {
+ return supported_format.shm_format == format;
+ });
+ if (supported_format ==
+ (shm_supported_formats + arraysize(shm_supported_formats))) {
+ wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT,
+ "invalid format 0x%x", format);
+ return;
+ }
+
+ if (offset < 0) {
+ wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT,
+ "invalid offset %d", offset);
+ return;
+ }
+
+ scoped_ptr<Buffer> buffer =
+ GetUserDataAs<SharedMemory>(resource)
+ ->CreateBuffer(gfx::Size(width, height),
+ supported_format->buffer_format, offset, stride);
+ if (!buffer) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource* buffer_resource =
+ wl_resource_create(client, &wl_buffer_interface, 1, id);
+ if (!buffer_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ buffer->set_release_callback(
+ base::Bind(&wl_buffer_send_release, base::Unretained(buffer_resource)));
+
+ SetImplementation(buffer_resource, &buffer_implementation, buffer.Pass());
+}
+
+void shm_pool_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void shm_pool_resize(wl_client* client, wl_resource* resource, int32_t size) {
+ // Nothing to do here.
+}
+
+const struct wl_shm_pool_interface shm_pool_implementation = {
+ shm_pool_create_buffer, shm_pool_destroy, shm_pool_resize};
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_shm_interface:
+
+void shm_create_pool(wl_client* client,
+ wl_resource* resource,
+ uint32_t id,
+ int fd,
+ int32_t size) {
+ scoped_ptr<SharedMemory> shared_memory =
+ GetUserDataAs<Display>(resource)
+ ->CreateSharedMemory(base::FileDescriptor(fd, true), size);
+ if (!shared_memory) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource* shm_pool_resource =
+ wl_resource_create(client, &wl_shm_pool_interface, 1, id);
+ if (!shm_pool_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ SetImplementation(shm_pool_resource, &shm_pool_implementation,
+ shared_memory.Pass());
+}
+
+const struct wl_shm_interface shm_implementation = {shm_create_pool};
+
+void bind_shm(wl_client* client, void* data, uint32_t version, uint32_t id) {
+ wl_resource* resource = wl_resource_create(client, &wl_shm_interface, 1, id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &shm_implementation, data, nullptr);
+
+ for (const auto& supported_format : shm_supported_formats)
+ wl_shm_send_format(resource, supported_format.shm_format);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_shell_surface_interface:
+
+void shell_surface_pong(wl_client* client,
+ wl_resource* resource,
+ uint32_t serial) {
+ NOTIMPLEMENTED();
+}
+
+void shell_surface_move(wl_client* client,
+ wl_resource* resource,
+ wl_resource* seat_resource,
+ uint32_t serial) {
+ NOTIMPLEMENTED();
+}
+
+void shell_surface_resize(wl_client* client,
+ wl_resource* resource,
+ wl_resource* seat_resource,
+ uint32_t serial,
+ uint32_t edges) {
+ NOTIMPLEMENTED();
+}
+
+void shell_surface_set_toplevel(wl_client* client, wl_resource* resource) {
+ GetUserDataAs<ShellSurface>(resource)->SetToplevel();
+}
+
+void shell_surface_set_transient(wl_client* client,
+ wl_resource* resource,
+ wl_resource* parent_resource,
+ int x,
+ int y,
+ uint32_t flags) {
+ NOTIMPLEMENTED();
+}
+
+void shell_surface_set_fullscreen(wl_client* client,
+ wl_resource* resource,
+ uint32_t method,
+ uint32_t framerate,
+ wl_resource* output_resource) {
+ GetUserDataAs<ShellSurface>(resource)->SetFullscreen(true);
+}
+
+void shell_surface_set_popup(wl_client* client,
+ wl_resource* resource,
+ wl_resource* seat_resource,
+ uint32_t serial,
+ wl_resource* parent_resource,
+ int32_t x,
+ int32_t y,
+ uint32_t flags) {
+ NOTIMPLEMENTED();
+}
+
+void shell_surface_set_maximized(wl_client* client,
+ wl_resource* resource,
+ wl_resource* output_resource) {
+ NOTIMPLEMENTED();
+}
+
+void shell_surface_set_title(wl_client* client,
+ wl_resource* resource,
+ const char* title) {
+ GetUserDataAs<ShellSurface>(resource)
+ ->SetTitle(base::string16(base::ASCIIToUTF16(title)));
+}
+
+void shell_surface_set_class(wl_client* client,
+ wl_resource* resource,
+ const char* clazz) {
+ NOTIMPLEMENTED();
+}
+
+const struct wl_shell_surface_interface shell_surface_implementation = {
+ shell_surface_pong, shell_surface_move,
+ shell_surface_resize, shell_surface_set_toplevel,
+ shell_surface_set_transient, shell_surface_set_fullscreen,
+ shell_surface_set_popup, shell_surface_set_maximized,
+ shell_surface_set_title, shell_surface_set_class};
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_shell_interface:
+
+void shell_get_shell_surface(wl_client* client,
+ wl_resource* resource,
+ uint32_t id,
+ wl_resource* surface) {
+ scoped_ptr<ShellSurface> shell_surface =
+ GetUserDataAs<Display>(resource)
+ ->CreateShellSurface(GetUserDataAs<Surface>(surface));
+ if (!shell_surface) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource* shell_surface_resource =
+ wl_resource_create(client, &wl_shell_surface_interface, 1, id);
+ if (!shell_surface_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ SetImplementation(shell_surface_resource, &shell_surface_implementation,
+ shell_surface.Pass());
+}
+
+const struct wl_shell_interface shell_implementation = {
+ shell_get_shell_surface};
+
+void bind_shell(wl_client* client, void* data, uint32_t version, uint32_t id) {
+ wl_resource* resource =
+ wl_resource_create(client, &wl_shell_interface, 1, id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &shell_implementation, data,
+ nullptr);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// Server, public:
+
+Server::Server(Display* display)
+ : display_(display), wl_display_(wl_display_create()) {
+ wl_global_create(wl_display_.get(), &wl_compositor_interface,
+ compositor_version, display_, bind_compositor);
+ wl_global_create(wl_display_.get(), &wl_shm_interface, 1, display_, bind_shm);
+ wl_global_create(wl_display_.get(), &wl_shell_interface, 1, display_,
+ bind_shell);
+}
+
+Server::~Server() {}
+
+// static
+scoped_ptr<Server> Server::Create(Display* display) {
+ scoped_ptr<Server> server(new Server(display));
+ int rv = wl_display_add_socket(server->wl_display_.get(), nullptr);
+ DCHECK_EQ(rv, 0) << "wl_display_add_socket failed: " << rv;
+ return server;
+}
+
+bool Server::AddSocket(const std::string name) {
+ DCHECK(!name.empty());
+ return !wl_display_add_socket(wl_display_.get(), name.c_str());
+}
+
+int Server::GetFileDescriptor() const {
+ wl_event_loop* event_loop = wl_display_get_event_loop(wl_display_.get());
+ DCHECK(event_loop);
+ return wl_event_loop_get_fd(event_loop);
+}
+
+void Server::Dispatch(base::TimeDelta timeout) {
+ wl_event_loop* event_loop = wl_display_get_event_loop(wl_display_.get());
+ DCHECK(event_loop);
+ wl_event_loop_dispatch(event_loop, timeout.InMilliseconds());
+}
+
+void Server::Flush() {
+ wl_display_flush_clients(wl_display_.get());
+}
+
+} // namespace wayland
+} // namespace exo
diff --git a/components/exo/wayland/server.h b/components/exo/wayland/server.h
new file mode 100644
index 0000000..34280bb
--- /dev/null
+++ b/components/exo/wayland/server.h
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_WAYLAND_SERVER_H_
+#define COMPONENTS_EXO_WAYLAND_SERVER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "components/exo/wayland/scoped_wl_types.h"
+
+namespace exo {
+class Display;
+
+namespace wayland {
+
+// This class is a thin wrapper around a Wayland display server. All Wayland
+// requests are dispatched into the given Exospere display.
+class Server {
+ public:
+ explicit Server(Display* display);
+ ~Server();
+
+ // Creates a Wayland display server that clients can connect to using the
+ // default socket name.
+ static scoped_ptr<Server> Create(Display* display);
+
+ // This adds a Unix socket to the Wayland display server which can be used
+ // by clients to connect to the display server.
+ bool AddSocket(const std::string name);
+
+ // Returns the file descriptor associated with the server.
+ int GetFileDescriptor() const;
+
+ // This function dispatches events. This must be called on a thread for
+ // which it's safe to access the Exospere display that this server was
+ // created for. The |timeout| argument specifies the amount of time that
+ // Dispatch() should block waiting for the file descriptor to become ready.
+ void Dispatch(base::TimeDelta timeout);
+
+ // Send all buffered events to the clients.
+ void Flush();
+
+ private:
+ Display* const display_;
+ ScopedWLDisplay wl_display_;
+
+ DISALLOW_COPY_AND_ASSIGN(Server);
+};
+
+} // namespace wayland
+} // namespace exo
+
+#endif // COMPONENTS_EXO_WAYLAND_SERVER_H_
diff --git a/components/exo/wayland/server_unittest.cc b/components/exo/wayland/server_unittest.cc
new file mode 100644
index 0000000..adc1dd4
--- /dev/null
+++ b/components/exo/wayland/server_unittest.cc
@@ -0,0 +1,97 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <wayland-client-core.h>
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/process_handle.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread.h"
+#include "components/exo/display.h"
+#include "components/exo/wayland/server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace exo {
+namespace wayland {
+namespace {
+
+base::StaticAtomicSequenceNumber g_next_socket_id;
+
+std::string GetUniqueSocketName() {
+ return base::StringPrintf("wayland-test-%d-%d", base::GetCurrentProcId(),
+ g_next_socket_id.GetNext());
+}
+
+TEST(ServerTest, AddSocket) {
+ scoped_ptr<Display> display(new Display);
+ scoped_ptr<Server> server(new Server(display.get()));
+
+ // Check that calling AddSocket() with a unique socket name succeeds.
+ bool rv = server->AddSocket(GetUniqueSocketName());
+ EXPECT_TRUE(rv);
+}
+
+TEST(ServerTest, GetFileDescriptor) {
+ scoped_ptr<Display> display(new Display);
+ scoped_ptr<Server> server(new Server(display.get()));
+
+ bool rv = server->AddSocket(GetUniqueSocketName());
+ EXPECT_TRUE(rv);
+
+ // Check that the returned file descriptor is valid.
+ int fd = server->GetFileDescriptor();
+ DCHECK_GE(fd, 0);
+}
+
+void ConnectToServer(const std::string socket_name,
+ bool* connected_to_server,
+ base::WaitableEvent* event) {
+ wl_display* display = wl_display_connect(socket_name.c_str());
+ *connected_to_server = !!display;
+ event->Signal();
+ wl_display_disconnect(display);
+}
+
+TEST(ServerTest, Dispatch) {
+ scoped_ptr<Display> display(new Display);
+ scoped_ptr<Server> server(new Server(display.get()));
+
+ std::string socket_name = GetUniqueSocketName();
+ bool rv = server->AddSocket(socket_name);
+ EXPECT_TRUE(rv);
+
+ base::Thread client("client-" + socket_name);
+ ASSERT_TRUE(client.Start());
+
+ // Post a task that connects server on the created thread.
+ bool connected_to_server = false;
+ base::WaitableEvent event(false, false);
+ client.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ConnectToServer, socket_name, &connected_to_server, &event));
+
+ // Call Dispatch() with a 5 second timeout.
+ server->Dispatch(base::TimeDelta::FromSeconds(5));
+
+ // Check if client thread managed to connect to server.
+ event.Wait();
+ EXPECT_TRUE(connected_to_server);
+}
+
+TEST(ServerTest, Flush) {
+ scoped_ptr<Display> display(new Display);
+ scoped_ptr<Server> server(new Server(display.get()));
+
+ bool rv = server->AddSocket(GetUniqueSocketName());
+ EXPECT_TRUE(rv);
+
+ // Just call Flush to check that it doesn't have any bad side-effects.
+ server->Flush();
+}
+
+} // namespace
+} // namespace wayland
+} // namespace exo