1 // Copyright 2018 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 "components/exo/wayland/clients/fullscreen_shell.h"
6 
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "third_party/skia/include/core/SkCanvas.h"
12 #include "third_party/skia/include/core/SkRefCnt.h"
13 #include "third_party/skia/include/core/SkSurface.h"
14 #include "third_party/skia/include/gpu/GrBackendSurface.h"
15 #include "third_party/skia/include/gpu/GrContext.h"
16 #include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
17 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
18 #include "ui/gfx/geometry/rect.h"
19 #include "ui/gfx/skia_util.h"
20 #include "ui/gl/gl_bindings.h"
21 
22 namespace exo {
23 namespace wayland {
24 namespace clients {
25 
26 namespace {
27 
FrameCallback(void * data,wl_callback * callback,uint32_t time)28 void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
29   bool* frame_callback_pending = static_cast<bool*>(data);
30   *frame_callback_pending = false;
31 }
32 
33 }  // namespace
34 
35 ////////////////////////////////////////////////////////////////////////////////
36 // FullscreenClient, public:
37 
FullscreenClient()38 FullscreenClient::FullscreenClient() {}
39 
~FullscreenClient()40 FullscreenClient::~FullscreenClient() {}
41 
Run(const InitParams & params)42 bool FullscreenClient::Run(const InitParams& params) {
43   wl_callback_listener frame_listener = {FrameCallback};
44 
45   // Wait for screen mode to be received
46   while (wl_display_dispatch(display_.get()) != -1) {
47     if (done_receiving_modes_ && !has_mode_) {
48       LOG(ERROR) << "Did not receive screen mode";
49       return false;
50     } else if (has_mode_) {
51       break;
52     }
53   }
54 
55   AllocateBuffers(params);
56 
57   do {
58     if (frame_callback_pending_)
59       continue;
60 
61     if (frame_count_ == frames_)
62       break;
63 
64     Paint(frame_listener);
65 
66   } while (wl_display_dispatch(display_.get()) != -1);
67 
68   return true;
69 }
70 
71 ////////////////////////////////////////////////////////////////////////////////
72 // FullscreenClient: Private
73 
AllocateBuffers(const InitParams & params)74 void FullscreenClient::AllocateBuffers(const InitParams& params) {
75   for (size_t i = 0; i < params.num_buffers; ++i) {
76     auto buffer = CreateBuffer(size_, params.drm_format, params.bo_usage);
77     if (!buffer) {
78       LOG(ERROR) << "Failed to create buffer";
79       return;
80     }
81     buffers_.push_back(std::move(buffer));
82   }
83 }
84 
Paint(const wl_callback_listener & frame_listener)85 void FullscreenClient::Paint(const wl_callback_listener& frame_listener) {
86   Buffer* buffer = DequeueBuffer();
87   if (!buffer)
88     return;
89 
90   ++frame_count_;
91 
92   int left = point_.x();
93   int top = point_.y();
94   int right = point_.x() + square_size_.width();
95   int bottom = point_.y() + square_size_.height();
96 
97   if (right >= size_.width() || left <= 0) {
98     dir_x_ *= -1;
99   }
100 
101   if (top <= 0 || bottom >= size_.height()) {
102     dir_y_ *= -1;
103   }
104 
105   point_.set_x(point_.x() + dir_x_ * step_size_);
106   point_.set_y(point_.y() + dir_y_ * step_size_);
107 
108   SkCanvas* canvas = buffer->sk_surface->getCanvas();
109   canvas->clear(color_);
110   const SkRect rect = gfx::RectToSkRect(gfx::Rect{point_, square_size_});
111   const SkPaint paint;
112   canvas->drawRect(rect, paint);
113 
114   if (gr_context_) {
115     gr_context_->flush();
116     glFinish();
117   }
118 
119   wl_surface_set_buffer_scale(surface_.get(), scale_);
120   wl_surface_set_buffer_transform(surface_.get(), transform_);
121   wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
122                     surface_size_.height());
123   wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
124 
125   // Set up the frame callback.
126   frame_callback_pending_ = true;
127   frame_callback_.reset(wl_surface_frame(surface_.get()));
128   wl_callback_add_listener(frame_callback_.get(), &frame_listener,
129                            &frame_callback_pending_);
130 
131   wl_surface_commit(surface_.get());
132   wl_display_flush(display_.get());
133 }
134 
HandleDown(void * data,struct wl_touch * wl_touch,uint32_t serial,uint32_t time,struct wl_surface * surface,int32_t id,wl_fixed_t x,wl_fixed_t y)135 void FullscreenClient::HandleDown(void* data,
136                                   struct wl_touch* wl_touch,
137                                   uint32_t serial,
138                                   uint32_t time,
139                                   struct wl_surface* surface,
140                                   int32_t id,
141                                   wl_fixed_t x,
142                                   wl_fixed_t y) {
143   if (color_ == SK_ColorBLUE) {
144     color_ = SK_ColorRED;
145   } else {
146     color_ = SK_ColorBLUE;
147   }
148 }
149 
HandleMode(void * data,struct wl_output * wl_output,uint32_t flags,int32_t width,int32_t height,int32_t refresh)150 void FullscreenClient::HandleMode(void* data,
151                                   struct wl_output* wl_output,
152                                   uint32_t flags,
153                                   int32_t width,
154                                   int32_t height,
155                                   int32_t refresh) {
156   if ((WL_OUTPUT_MODE_CURRENT & flags) != WL_OUTPUT_MODE_CURRENT)
157     return;
158 
159   size_.SetSize(width, height);
160   switch (transform_) {
161     case WL_OUTPUT_TRANSFORM_NORMAL:
162     case WL_OUTPUT_TRANSFORM_180:
163       surface_size_.SetSize(width, height);
164       break;
165     case WL_OUTPUT_TRANSFORM_90:
166     case WL_OUTPUT_TRANSFORM_270:
167       surface_size_.SetSize(height, width);
168       break;
169     default:
170       NOTREACHED();
171       break;
172   }
173 
174   std::unique_ptr<wl_region> opaque_region(static_cast<wl_region*>(
175       wl_compositor_create_region(globals_.compositor.get())));
176 
177   if (!opaque_region) {
178     LOG(ERROR) << "Can't create region";
179     return;
180   }
181 
182   wl_region_add(opaque_region.get(), 0, 0, surface_size_.width(),
183                 surface_size_.height());
184   wl_surface_set_opaque_region(surface_.get(), opaque_region.get());
185   wl_surface_set_input_region(surface_.get(), opaque_region.get());
186 
187   has_mode_ = true;
188 }
189 
HandleDone(void * data,struct wl_output * wl_output)190 void FullscreenClient::HandleDone(void* data, struct wl_output* wl_output) {
191   done_receiving_modes_ = true;
192 }
193 
194 }  // namespace clients
195 }  // namespace wayland
196 }  // namespace exo
197