// Copyright 2017 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/at_exit.h" #include "base/command_line.h" #include "base/message_loop/message_pump_type.h" #include "base/stl_util.h" #include "base/task/single_thread_task_executor.h" #include "components/exo/wayland/clients/client_base.h" #include "components/exo/wayland/clients/client_helper.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "ui/gl/gl_bindings.h" namespace exo { namespace wayland { namespace clients { namespace { void FrameCallback(void* data, wl_callback* callback, uint32_t time) { bool* callback_pending = static_cast(data); *callback_pending = false; } } // namespace // This demo testes subsurface features of exo. // For the first 200 frames: we animate parent surface, child_surface and // child_surface's position. // For 200-400 frames: we animate parent surface and child_surface's position. // For 400-600 frames: we only animate child_surface's position. // After 600 frames: we still animate child_surface's position, but don't call // commit on parent surface. So the window will stop updating. class SubSurfaceClient : public ClientBase { public: SubSurfaceClient() = default; ~SubSurfaceClient() override = default; void Run(const ClientBase::InitParams& params); private: DISALLOW_COPY_AND_ASSIGN(SubSurfaceClient); }; void SubSurfaceClient::Run(const ClientBase::InitParams& params) { if (!ClientBase::Init(params)) return; std::unique_ptr child_surface(static_cast( wl_compositor_create_surface(globals_.compositor.get()))); std::unique_ptr subsurface( static_cast(wl_subcompositor_get_subsurface( globals_.subcompositor.get(), child_surface.get(), surface_.get()))); if (!child_surface || !subsurface) { LOG(ERROR) << "Can't create subsurface"; return; } constexpr int32_t kSubsurfaceWidth = 128; constexpr int32_t kSubsurfaceHeight = 128; auto subbuffer = CreateBuffer(gfx::Size(kSubsurfaceWidth, kSubsurfaceHeight), params.drm_format, params.bo_usage); if (!subbuffer) { LOG(ERROR) << "Failed to create subbuffer"; return; } bool callback_pending = false; std::unique_ptr frame_callback; wl_callback_listener frame_listener = {FrameCallback}; size_t frame_count = 0; do { if (callback_pending && frame_count < 600) continue; // Only generate frames to child surface for the first 200 frames. if (frame_count < 200) { SkScalar half_width = SkScalarHalf(kSubsurfaceWidth); SkScalar half_height = SkScalarHalf(kSubsurfaceHeight); SkIRect rect = SkIRect::MakeXYWH(-SkScalarHalf(half_width), -SkScalarHalf(half_height), half_width, half_height); // Rotation speed (degrees/frame). const double kRotationSpeed = 5.; SkScalar rotation = frame_count * kRotationSpeed; SkCanvas* canvas = subbuffer->sk_surface->getCanvas(); canvas->save(); canvas->clear(SK_ColorBLACK); SkPaint paint; paint.setColor(SkColorSetA(SK_ColorYELLOW, 0xA0)); canvas->translate(half_width, half_height); canvas->rotate(rotation); canvas->drawIRect(rect, paint); canvas->restore(); if (gr_context_) { gr_context_->flushAndSubmit(); glFinish(); } wl_surface_damage(child_surface.get(), 0, 0, kSubsurfaceWidth, kSubsurfaceHeight); wl_surface_attach(child_surface.get(), subbuffer->buffer.get(), 0, 0); wl_surface_commit(child_surface.get()); } // Only generate frames to parent surface for the first 400 frames. if (frame_count < 400) { Buffer* buffer = buffers_.front().get(); SkCanvas* canvas = buffer->sk_surface->getCanvas(); static const SkColor kColors[] = {SK_ColorRED, SK_ColorBLACK}; canvas->clear(kColors[frame_count % base::size(kColors)]); if (gr_context_) { gr_context_->flushAndSubmit(); glFinish(); } wl_surface_set_buffer_scale(surface_.get(), scale_); wl_surface_set_buffer_transform(surface_.get(), transform_); wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(), surface_size_.height()); wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0); callback_pending = true; frame_callback.reset(wl_surface_frame(surface_.get())); wl_callback_add_listener(frame_callback.get(), &frame_listener, &callback_pending); } // Always animate subsurface position. wl_subsurface_set_position(subsurface.get(), frame_count % 50, frame_count % 50); // Only commit changes for the first 600 frames. if (frame_count < 600) wl_surface_commit(surface_.get()); wl_display_flush(display_.get()); ++frame_count; } while (wl_display_dispatch(display_.get()) != -1); } } // namespace clients } // namespace wayland } // namespace exo int main(int argc, char* argv[]) { base::AtExitManager exit_manager; base::CommandLine::Init(argc, argv); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); exo::wayland::clients::ClientBase::InitParams params; if (!params.FromCommandLine(*command_line)) return 1; base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI); exo::wayland::clients::SubSurfaceClient client; client.Run(params); return 1; }