chromium/components/exo/wayland/wayland_pointer_delegate.cc

// Copyright 2018 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/wayland_pointer_delegate.h"

#include <linux/input-event-codes.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol-core.h>

#include "components/exo/pointer.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"

namespace exo {
namespace wayland {

WaylandPointerDelegate::WaylandPointerDelegate(wl_resource* pointer_resource,
                                               SerialTracker* serial_tracker)
    : pointer_resource_(pointer_resource), serial_tracker_(serial_tracker) {}

void WaylandPointerDelegate::OnPointerDestroying(Pointer* pointer) {
  delete this;
}

bool WaylandPointerDelegate::CanAcceptPointerEventsForSurface(
    Surface* surface) const {
  wl_resource* surface_resource = GetSurfaceResource(surface);
  // We can accept events for this surface if the client is the same as the
  // pointer.
  return surface_resource &&
         wl_resource_get_client(surface_resource) == client();
}

void WaylandPointerDelegate::OnPointerEnter(Surface* surface,
                                            const gfx::PointF& location,
                                            int button_flags) {
  wl_resource* surface_resource = GetSurfaceResource(surface);
  DCHECK(surface_resource);
  // Should we be sending button events to the client before the enter event
  // if client's pressed button state is different from |button_flags|?
  wl_pointer_send_enter(
      pointer_resource_,
      serial_tracker_->GetNextSerial(SerialTracker::EventType::POINTER_ENTER),
      surface_resource, wl_fixed_from_double(location.x()),
      wl_fixed_from_double(location.y()));
}

void WaylandPointerDelegate::OnPointerLeave(Surface* surface) {
  wl_resource* surface_resource = GetSurfaceResource(surface);
  DCHECK(surface_resource);
  wl_pointer_send_leave(
      pointer_resource_,
      serial_tracker_->GetNextSerial(SerialTracker::EventType::POINTER_LEAVE),
      surface_resource);
}

void WaylandPointerDelegate::OnPointerMotion(base::TimeTicks time_stamp,
                                             const gfx::PointF& location) {
  SendTimestamp(time_stamp);
  wl_pointer_send_motion(pointer_resource_, TimeTicksToMilliseconds(time_stamp),
                         wl_fixed_from_double(location.x()),
                         wl_fixed_from_double(location.y()));
}

void WaylandPointerDelegate::OnPointerButton(base::TimeTicks time_stamp,
                                             int button_flags,
                                             bool pressed) {
  constexpr struct {
    ui::EventFlags flag;
    uint32_t value;
    SerialTracker::EventType down;
    SerialTracker::EventType up;
  } buttons[] = {
      {ui::EF_LEFT_MOUSE_BUTTON, BTN_LEFT,
       SerialTracker::EventType::POINTER_LEFT_BUTTON_DOWN,
       SerialTracker::EventType::POINTER_LEFT_BUTTON_UP},
      {ui::EF_RIGHT_MOUSE_BUTTON, BTN_RIGHT,
       SerialTracker::EventType::POINTER_RIGHT_BUTTON_DOWN,
       SerialTracker::EventType::POINTER_RIGHT_BUTTON_UP},
      {ui::EF_MIDDLE_MOUSE_BUTTON, BTN_MIDDLE,
       SerialTracker::EventType::POINTER_MIDDLE_BUTTON_DOWN,
       SerialTracker::EventType::POINTER_MIDDLE_BUTTON_UP},
      {ui::EF_FORWARD_MOUSE_BUTTON, BTN_EXTRA,
       SerialTracker::EventType::POINTER_FORWARD_BUTTON_DOWN,
       SerialTracker::EventType::POINTER_FORWARD_BUTTON_UP},
      {ui::EF_BACK_MOUSE_BUTTON, BTN_SIDE,
       SerialTracker::EventType::POINTER_BACK_BUTTON_DOWN,
       SerialTracker::EventType::POINTER_BACK_BUTTON_UP},
  };
  for (auto button : buttons) {
    if (button_flags & button.flag) {
      SendTimestamp(time_stamp);
      const SerialTracker::EventType event_type =
          (pressed ? button.down : button.up);
      wl_pointer_send_button(pointer_resource_,
                             serial_tracker_->GetNextSerial(event_type),
                             TimeTicksToMilliseconds(time_stamp), button.value,
                             pressed ? WL_POINTER_BUTTON_STATE_PRESSED
                                     : WL_POINTER_BUTTON_STATE_RELEASED);
    }
  }
}

void WaylandPointerDelegate::OnPointerScroll(base::TimeTicks time_stamp,
                                             const gfx::Vector2dF& offset,
                                             bool discrete) {
  // The unit aura considers to be "one scroll tick".
  const int kAuraScrollUnit = ui::MouseWheelEvent::kWheelDelta;

  // Weston, the reference compositor, treats one scroll tick as 10 units, with
  // no acceleration applied.
  constexpr int kWaylandScrollUnit = 10;

  // The ratio between the wayland and aura unit sizes. Multiplying by this
  // converts from aura units to wayland units, dividing does the reverse.
  const double kAxisStepDistance = static_cast<double>(kWaylandScrollUnit) /
                                   static_cast<double>(kAuraScrollUnit);

  if (wl_resource_get_version(pointer_resource_) >=
      WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
    int32_t axis_source =
        discrete ? WL_POINTER_AXIS_SOURCE_WHEEL : WL_POINTER_AXIS_SOURCE_FINGER;
    wl_pointer_send_axis_source(pointer_resource_, axis_source);
  }

  double x_value = -offset.x() * kAxisStepDistance;
  double y_value = -offset.y() * kAxisStepDistance;

  // ::axis_discrete events must be sent before their corresponding ::axis
  // events, per the specification.
  if (wl_resource_get_version(pointer_resource_) >=
          WL_POINTER_AXIS_DISCRETE_SINCE_VERSION &&
      discrete) {
    // Ensure that we never round the discrete value down to 0.
    int discrete_x = static_cast<int>(x_value / kWaylandScrollUnit);
    if (discrete_x == 0 && x_value != 0) {
      discrete_x = copysign(1, x_value);
    }
    int discrete_y = static_cast<int>(y_value / kWaylandScrollUnit);
    if (discrete_y == 0 && y_value != 0) {
      discrete_y = copysign(1, y_value);
    }

    wl_pointer_send_axis_discrete(
        pointer_resource_, WL_POINTER_AXIS_HORIZONTAL_SCROLL, discrete_x);
    wl_pointer_send_axis_discrete(pointer_resource_,
                                  WL_POINTER_AXIS_VERTICAL_SCROLL, discrete_y);
  }

  SendTimestamp(time_stamp);
  wl_pointer_send_axis(pointer_resource_, TimeTicksToMilliseconds(time_stamp),
                       WL_POINTER_AXIS_HORIZONTAL_SCROLL,
                       wl_fixed_from_double(x_value));

  SendTimestamp(time_stamp);
  wl_pointer_send_axis(pointer_resource_, TimeTicksToMilliseconds(time_stamp),
                       WL_POINTER_AXIS_VERTICAL_SCROLL,
                       wl_fixed_from_double(y_value));
}

void WaylandPointerDelegate::OnFingerScrollStop(base::TimeTicks time_stamp) {
  if (wl_resource_get_version(pointer_resource_) >=
      WL_POINTER_AXIS_STOP_SINCE_VERSION) {
    wl_pointer_send_axis_source(pointer_resource_,
                                WL_POINTER_AXIS_SOURCE_FINGER);
    SendTimestamp(time_stamp);
    wl_pointer_send_axis_stop(pointer_resource_,
                              TimeTicksToMilliseconds(time_stamp),
                              WL_POINTER_AXIS_HORIZONTAL_SCROLL);
    SendTimestamp(time_stamp);
    wl_pointer_send_axis_stop(pointer_resource_,
                              TimeTicksToMilliseconds(time_stamp),
                              WL_POINTER_AXIS_VERTICAL_SCROLL);
  }
}

void WaylandPointerDelegate::OnPointerFrame() {
  if (wl_resource_get_version(pointer_resource_) >=
      WL_POINTER_FRAME_SINCE_VERSION) {
    wl_pointer_send_frame(pointer_resource_);
  }
  wl_client_flush(client());
}

wl_client* WaylandPointerDelegate::client() const {
  return wl_resource_get_client(pointer_resource_);
}

}  // namespace wayland
}  // namespace exo