chromium/components/exo/wayland/zwp_pointer_constraints.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/exo/wayland/zwp_pointer_constraints.h"

#include <pointer-constraints-unstable-v1-server-protocol.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol-core.h>

#include <cstdarg>
#include <memory>

#include "base/memory/raw_ptr.h"
#include "components/exo/pointer.h"
#include "components/exo/pointer_constraint_delegate.h"
#include "components/exo/wayland/server_util.h"
#include "third_party/skia/include/core/SkRegion.h"

namespace exo {
namespace wayland {

namespace {

// Implements a PointerConstraintDelegate in terms of the zwp_locked_pointer
// Wayland protocol.
//
// Lifetime note: The underlying Wayland protocol gives control over this
// object's lifetime to the client. However, it's possible that its
// dependencies could be destroyed prior to the client destroying it.
// At this point we consider the object "defunct" and its |surface_| member
// to be potentially dangling. |pointer_| is correctly nulled when appropriate.
class WaylandPointerConstraintDelegate : public PointerConstraintDelegate {
 public:
  WaylandPointerConstraintDelegate(wl_resource* constraint_resource,
                                   Surface* surface,
                                   Pointer* pointer,
                                   SkRegion* region,
                                   uint32_t lifetime)
      : constraint_resource_(constraint_resource),
        pointer_(pointer),
        surface_(surface),
        is_persistent_(lifetime ==
                       ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT) {
    pointer->ConstrainPointer(this);
  }

  ~WaylandPointerConstraintDelegate() override {
    if (pointer_) {
      pointer_->OnPointerConstraintDelegateDestroying(this);
      pointer_ = nullptr;
    }
  }

  // PointerConstraintDelegate::
  void OnConstraintActivated() override { SendLocked(); }
  void OnAlreadyConstrained() override {
    wl_resource_post_error(
        constraint_resource_.get(),
        ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED,
        "A pointer constraint was already requested for this wl_pointer "
        "on this wl_surface.");
  }
  void OnConstraintBroken() override { SendUnlocked(); }
  bool IsPersistent() override { return is_persistent_; }
  Surface* GetConstrainedSurface() override { return surface_; }
  void OnDefunct() override { pointer_ = nullptr; }

 private:
  // Inform the client of the state of the lock.
  void SendLocked() {
    VLOG(1) << "send_locked(" << constraint_resource_ << ")";
    zwp_locked_pointer_v1_send_locked(constraint_resource_);
  }

  void SendUnlocked() {
    VLOG(1) << "send_unlocked(" << constraint_resource_ << ")";
    zwp_locked_pointer_v1_send_unlocked(constraint_resource_);
  }

  const raw_ptr<wl_resource> constraint_resource_;
  raw_ptr<Pointer> pointer_;
  const raw_ptr<Surface> surface_;
  bool is_persistent_;
};

////////////////////////////////////////////////////////////////////////////////
// zwp_locked_pointer

void locked_pointer_destroy(wl_client* client, wl_resource* resource) {
  VLOG(1) << "locked_pointer_destroy(" << client << ", " << resource << ")";
  wl_resource_destroy(resource);
}

void locked_pointer_set_cursor_position_hint(wl_client* client,
                                             wl_resource* resource,
                                             wl_fixed_t surface_x,
                                             wl_fixed_t surface_y) {
  // Not supported.
}

void locked_pointer_set_region(wl_client* client,
                               wl_resource* resource,
                               wl_resource* region_resource) {
  // Not supported.
}

const struct zwp_locked_pointer_v1_interface locked_pointer_implementation = {
    locked_pointer_destroy, locked_pointer_set_cursor_position_hint,
    locked_pointer_set_region};

////////////////////////////////////////////////////////////////////////////////
// zwp_confined_pointer

void confined_pointer_destroy(wl_client* client, wl_resource* resource) {
  wl_resource_destroy(resource);
}

void confined_pointer_set_region(wl_client* client,
                                 wl_resource* resource,
                                 wl_resource* region_resource) {
  // Not supported.
}

const struct zwp_confined_pointer_v1_interface confined_pointer_implementation =
    {
        confined_pointer_destroy,
        confined_pointer_set_region,
};

////////////////////////////////////////////////////////////////////////////////
// zwp_pointer_constraints

void pointer_constraints_destroy(wl_client* client, wl_resource* resource) {
  VLOG(1) << "pointer_constraints_destroy(" << client << ", " << resource
          << ")";
  wl_resource_destroy(resource);
}

void pointer_constraints_lock_pointer(wl_client* client,
                                      wl_resource* resource,
                                      uint32_t id,
                                      wl_resource* surface_resource,
                                      wl_resource* pointer_resource,
                                      wl_resource* region_resource,
                                      uint32_t lifetime) {
  Surface* surface = GetUserDataAs<Surface>(surface_resource);
  Pointer* pointer = GetUserDataAs<Pointer>(pointer_resource);
  SkRegion* region =
      region_resource ? GetUserDataAs<SkRegion>(region_resource) : nullptr;

  VLOG(1) << "lock_pointer(" << client << ", " << resource << "; Surface "
          << surface << " @ window '"
          << (surface && surface->window() ? surface->window()->GetTitle()
                                           : std::u16string())
          << "', " << "Pointer " << pointer << ")";

  wl_resource* locked_pointer_resource =
      wl_resource_create(client, &zwp_locked_pointer_v1_interface, 1, id);
  SetImplementation(
      locked_pointer_resource, &locked_pointer_implementation,
      std::make_unique<WaylandPointerConstraintDelegate>(
          locked_pointer_resource, surface, pointer, region, lifetime));
}

void pointer_constraints_confine_pointer(wl_client* client,
                                         wl_resource* resource,
                                         uint32_t id,
                                         wl_resource* surface_resource,
                                         wl_resource* pointer_resource,
                                         wl_resource* region_resource,
                                         uint32_t lifetime) {
  // Confined pointer is not currently supported.
  wl_resource* confined_pointer_resource =
      wl_resource_create(client, &zwp_confined_pointer_v1_interface, 1, id);
  SetImplementation<int>(confined_pointer_resource,
                         &confined_pointer_implementation, nullptr);
}

const struct zwp_pointer_constraints_v1_interface
    pointer_constraints_implementation = {pointer_constraints_destroy,
                                          pointer_constraints_lock_pointer,
                                          pointer_constraints_confine_pointer};

}  // namespace

void bind_pointer_constraints(wl_client* client,
                              void* data,
                              uint32_t version,
                              uint32_t id) {
  wl_resource* resource = wl_resource_create(
      client, &zwp_pointer_constraints_v1_interface, version, id);
  wl_resource_set_implementation(resource, &pointer_constraints_implementation,
                                 data, nullptr);
}

}  // namespace wayland
}  // namespace exo