blob: e86efb4af235289bc9bcd300864d3820e988b30b [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
#include <linux/input.h>
#include <wayland-util.h>
#include <optional>
#include "base/logging.h"
#include "base/notimplemented.h"
#include "base/version.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/events/types/event_type.h"
#include "ui/ozone/common/features.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_serial_tracker.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
namespace ui {
namespace {
// See TODO in //ui/ozone/common/features.cc
wl::EventDispatchPolicy GetEventDispatchPolicy() {
return IsDispatchPointerEventsOnFrameEventEnabled()
? wl::EventDispatchPolicy::kOnFrame
: wl::EventDispatchPolicy::kImmediate;
}
} // namespace
WaylandPointer::WaylandPointer(wl_pointer* pointer,
WaylandConnection* connection,
Delegate* delegate)
: obj_(pointer), connection_(connection), delegate_(delegate) {
static constexpr wl_pointer_listener kPointerListener = {
.enter = &OnEnter,
.leave = &OnLeave,
.motion = &OnMotion,
.button = &OnButton,
.axis = &OnAxis,
.frame = &OnFrame,
.axis_source = &OnAxisSource,
.axis_stop = &OnAxisStop,
.axis_discrete = &OnAxisDiscrete,
.axis_value120 = &OnAxisValue120,
.axis_relative_direction = &OnAxisRelativeDirection,
};
wl_pointer_add_listener(obj_.get(), &kPointerListener, this);
}
WaylandPointer::~WaylandPointer() {
// If a cursor already exists, we need to reset it first before
// destroying the pointer to prevent dangling references.
connection_->ResetCursor();
// Even though, WaylandPointer::Leave is always called when Wayland destroys
// wl_pointer, it's better to be explicit as some Wayland compositors may have
// bugs.
delegate_->OnPointerFocusChanged(nullptr, {}, EventTimeForNow(),
wl::EventDispatchPolicy::kImmediate);
delegate_->ReleasePressedPointerButtons(nullptr, EventTimeForNow());
}
// static
void WaylandPointer::OnEnter(void* data,
wl_pointer* pointer,
uint32_t serial,
wl_surface* surface,
wl_fixed_t surface_x,
wl_fixed_t surface_y) {
// enter event doesn't have timestamp. Use EventTimeForNow().
const auto timestamp = EventTimeForNow();
auto* self = static_cast<WaylandPointer*>(data);
self->connection_->serial_tracker().UpdateSerial(wl::SerialType::kMouseEnter,
serial);
WaylandWindow* window = wl::RootWindowFromWlSurface(surface);
if (!window) {
return;
}
self->delegate_->OnPointerFocusChanged(
window,
gfx::PointF(static_cast<float>(wl_fixed_to_double(surface_x)),
static_cast<float>(wl_fixed_to_double(surface_y))),
timestamp, GetEventDispatchPolicy());
}
// static
void WaylandPointer::OnLeave(void* data,
wl_pointer* pointer,
uint32_t serial,
wl_surface* surface) {
// leave event doesn't have timestamp. Use EventTimeForNow().
const auto timestamp = EventTimeForNow();
auto* self = static_cast<WaylandPointer*>(data);
self->connection_->serial_tracker().ResetSerial(wl::SerialType::kMouseEnter);
self->delegate_->OnPointerFocusChanged(nullptr,
self->delegate_->GetPointerLocation(),
timestamp, GetEventDispatchPolicy());
}
// static
void WaylandPointer::OnMotion(void* data,
wl_pointer* pointer,
uint32_t time,
wl_fixed_t surface_x,
wl_fixed_t surface_y) {
auto* self = static_cast<WaylandPointer*>(data);
self->delegate_->OnPointerMotionEvent(
gfx::PointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)),
wl::EventMillisecondsToTimeTicks(time), GetEventDispatchPolicy(),
/*is_synthesized=*/false);
}
// static
void WaylandPointer::OnButton(void* data,
wl_pointer* pointer,
uint32_t serial,
uint32_t time,
uint32_t button,
uint32_t state) {
auto* self = static_cast<WaylandPointer*>(data);
int changed_button;
switch (button) {
case BTN_LEFT:
changed_button = EF_LEFT_MOUSE_BUTTON;
break;
case BTN_MIDDLE:
changed_button = EF_MIDDLE_MOUSE_BUTTON;
break;
case BTN_RIGHT:
changed_button = EF_RIGHT_MOUSE_BUTTON;
break;
case BTN_BACK:
case BTN_SIDE:
changed_button = EF_BACK_MOUSE_BUTTON;
break;
case BTN_FORWARD:
case BTN_EXTRA:
changed_button = EF_FORWARD_MOUSE_BUTTON;
break;
default:
return;
}
EventType type = state == WL_POINTER_BUTTON_STATE_PRESSED
? EventType::kMousePressed
: EventType::kMouseReleased;
if (type == EventType::kMousePressed) {
self->connection_->serial_tracker().UpdateSerial(
wl::SerialType::kMousePress, serial);
}
self->delegate_->OnPointerButtonEvent(
type, changed_button, wl::EventMillisecondsToTimeTicks(time),
/*window=*/nullptr, GetEventDispatchPolicy(),
/*allow_release_of_unpressed_button=*/false, /*is_synthesized=*/false);
}
// static
void WaylandPointer::OnAxis(void* data,
wl_pointer* pointer,
uint32_t time,
uint32_t axis,
wl_fixed_t value) {
// Wayland compositors send axis events with values in the surface coordinate
// space. They send a value of 10 per mouse wheel click by convention, so
// clients (e.g. GTK+) typically scale down by this amount to convert to
// discrete step coordinates. wl_pointer version 5 improves the situation by
// adding axis sources and discrete axis events.
const double kAxisValueScale = 10.0;
const double delta = -wl_fixed_to_double(value) / kAxisValueScale *
MouseWheelEvent::kWheelDelta;
const auto timestamp = wl::EventMillisecondsToTimeTicks(time);
auto* self = static_cast<WaylandPointer*>(data);
self->OnAxisImpl(delta, axis, timestamp, /*is_high_resolution=*/false);
}
// ---- Version 5 ----
// static
void WaylandPointer::OnFrame(void* data, wl_pointer* pointer) {
auto* self = static_cast<WaylandPointer*>(data);
// The frame event ends the sequence of pointer events. Clear the flag. The
// next frame will set it when necessary.
self->axis_source_received_ = false;
self->delegate_->OnPointerFrameEvent();
}
// static
void WaylandPointer::OnAxisSource(void* data,
wl_pointer* pointer,
uint32_t axis_source) {
auto* self = static_cast<WaylandPointer*>(data);
self->axis_source_received_ = true;
self->delegate_->OnPointerAxisSourceEvent(axis_source);
}
// static
void WaylandPointer::OnAxisStop(void* data,
wl_pointer* pointer,
uint32_t time,
uint32_t axis) {
auto* self = static_cast<WaylandPointer*>(data);
self->delegate_->OnPointerAxisStopEvent(
axis, wl::EventMillisecondsToTimeTicks(time));
}
// static
void WaylandPointer::OnAxisDiscrete(void* data,
wl_pointer* pointer,
uint32_t axis,
int32_t discrete) {
// Deprecated since version 8
NOTIMPLEMENTED_LOG_ONCE();
}
// --- Version 8 ---
// static
void WaylandPointer::OnAxisValue120(void* data,
wl_pointer* pointer,
uint32_t axis,
int32_t value120) {
const double delta = -value120;
auto* self = static_cast<WaylandPointer*>(data);
self->OnAxisImpl(delta, axis, /*timestamp=*/std::nullopt,
/*is_high_resolution=*/true);
}
void WaylandPointer::OnAxisImpl(double delta,
uint32_t axis,
std::optional<base::TimeTicks> timestamp,
bool is_high_resolution) {
gfx::Vector2dF offset;
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
offset.set_y(delta);
} else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
offset.set_x(delta);
} else {
return;
}
delegate_->OnPointerAxisEvent(offset, timestamp, is_high_resolution);
// If we did not receive the axis event source explicitly, set it to the mouse
// wheel so far. Should this be a part of some complex event coming from the
// different source, the compositor will let us know sooner or later.
if (!axis_source_received_) {
delegate_->OnPointerAxisSourceEvent(WL_POINTER_AXIS_SOURCE_WHEEL);
}
}
// --- Version 9 ---
// static
void WaylandPointer::OnAxisRelativeDirection(void* data,
wl_pointer* obj,
uint32_t axis,
uint32_t direction) {
NOTIMPLEMENTED_LOG_ONCE();
}
} // namespace ui