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