1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/at_exit.h"
6 #include "base/command_line.h"
7 #include "base/message_loop/message_pump_type.h"
8 #include "base/stl_util.h"
9 #include "base/task/single_thread_task_executor.h"
10 #include "components/exo/wayland/clients/client_base.h"
11 #include "components/exo/wayland/clients/client_helper.h"
12 #include "third_party/skia/include/core/SkCanvas.h"
13 #include "third_party/skia/include/core/SkSurface.h"
14 #include "third_party/skia/include/gpu/GrContext.h"
15 #include "ui/gl/gl_bindings.h"
16
17 namespace exo {
18 namespace wayland {
19 namespace clients {
20 namespace {
21
FrameCallback(void * data,wl_callback * callback,uint32_t time)22 void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
23 bool* callback_pending = static_cast<bool*>(data);
24 *callback_pending = false;
25 }
26
27 } // namespace
28
29 // This demo testes subsurface features of exo.
30 // For the first 200 frames: we animate parent surface, child_surface and
31 // child_surface's position.
32 // For 200-400 frames: we animate parent surface and child_surface's position.
33 // For 400-600 frames: we only animate child_surface's position.
34 // After 600 frames: we still animate child_surface's position, but don't call
35 // commit on parent surface. So the window will stop updating.
36 class SubSurfaceClient : public ClientBase {
37 public:
38 SubSurfaceClient() = default;
39 ~SubSurfaceClient() override = default;
40
41 void Run(const ClientBase::InitParams& params);
42
43 private:
44 DISALLOW_COPY_AND_ASSIGN(SubSurfaceClient);
45 };
46
Run(const ClientBase::InitParams & params)47 void SubSurfaceClient::Run(const ClientBase::InitParams& params) {
48 if (!ClientBase::Init(params))
49 return;
50
51 std::unique_ptr<wl_surface> child_surface(static_cast<wl_surface*>(
52 wl_compositor_create_surface(globals_.compositor.get())));
53
54 std::unique_ptr<wl_subsurface> subsurface(
55 static_cast<wl_subsurface*>(wl_subcompositor_get_subsurface(
56 globals_.subcompositor.get(), child_surface.get(), surface_.get())));
57
58 if (!child_surface || !subsurface) {
59 LOG(ERROR) << "Can't create subsurface";
60 return;
61 }
62
63 constexpr int32_t kSubsurfaceWidth = 128;
64 constexpr int32_t kSubsurfaceHeight = 128;
65 auto subbuffer = CreateBuffer(gfx::Size(kSubsurfaceWidth, kSubsurfaceHeight),
66 params.drm_format, params.bo_usage);
67 if (!subbuffer) {
68 LOG(ERROR) << "Failed to create subbuffer";
69 return;
70 }
71
72 bool callback_pending = false;
73 std::unique_ptr<wl_callback> frame_callback;
74 wl_callback_listener frame_listener = {FrameCallback};
75
76 size_t frame_count = 0;
77 do {
78 if (callback_pending && frame_count < 600)
79 continue;
80
81 // Only generate frames to child surface for the first 200 frames.
82 if (frame_count < 200) {
83 SkScalar half_width = SkScalarHalf(kSubsurfaceWidth);
84 SkScalar half_height = SkScalarHalf(kSubsurfaceHeight);
85 SkIRect rect = SkIRect::MakeXYWH(-SkScalarHalf(half_width),
86 -SkScalarHalf(half_height), half_width,
87 half_height);
88 // Rotation speed (degrees/frame).
89 const double kRotationSpeed = 5.;
90 SkScalar rotation = frame_count * kRotationSpeed;
91 SkCanvas* canvas = subbuffer->sk_surface->getCanvas();
92 canvas->save();
93 canvas->clear(SK_ColorBLACK);
94 SkPaint paint;
95 paint.setColor(SkColorSetA(SK_ColorYELLOW, 0xA0));
96 canvas->translate(half_width, half_height);
97 canvas->rotate(rotation);
98 canvas->drawIRect(rect, paint);
99 canvas->restore();
100 if (gr_context_) {
101 gr_context_->flush();
102 glFinish();
103 }
104 wl_surface_damage(child_surface.get(), 0, 0, kSubsurfaceWidth,
105 kSubsurfaceHeight);
106 wl_surface_attach(child_surface.get(), subbuffer->buffer.get(), 0, 0);
107 wl_surface_commit(child_surface.get());
108 }
109
110 // Only generate frames to parent surface for the first 400 frames.
111 if (frame_count < 400) {
112 Buffer* buffer = buffers_.front().get();
113 SkCanvas* canvas = buffer->sk_surface->getCanvas();
114 static const SkColor kColors[] = {SK_ColorRED, SK_ColorBLACK};
115 canvas->clear(kColors[frame_count % base::size(kColors)]);
116 if (gr_context_) {
117 gr_context_->flush();
118 glFinish();
119 }
120 wl_surface_set_buffer_scale(surface_.get(), scale_);
121 wl_surface_set_buffer_transform(surface_.get(), transform_);
122 wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
123 surface_size_.height());
124 wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
125
126 callback_pending = true;
127 frame_callback.reset(wl_surface_frame(surface_.get()));
128 wl_callback_add_listener(frame_callback.get(), &frame_listener,
129 &callback_pending);
130 }
131
132 // Always animate subsurface position.
133 wl_subsurface_set_position(subsurface.get(), frame_count % 50,
134 frame_count % 50);
135
136 // Only commit changes for the first 600 frames.
137 if (frame_count < 600)
138 wl_surface_commit(surface_.get());
139
140 wl_display_flush(display_.get());
141 ++frame_count;
142 } while (wl_display_dispatch(display_.get()) != -1);
143 }
144
145 } // namespace clients
146 } // namespace wayland
147 } // namespace exo
148
main(int argc,char * argv[])149 int main(int argc, char* argv[]) {
150 base::AtExitManager exit_manager;
151 base::CommandLine::Init(argc, argv);
152 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
153
154 exo::wayland::clients::ClientBase::InitParams params;
155 if (!params.FromCommandLine(*command_line))
156 return 1;
157
158 base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
159 exo::wayland::clients::SubSurfaceClient client;
160 client.Run(params);
161 return 1;
162 }
163