Commit dabc0b61 authored by Fergus Dall's avatar Fergus Dall Committed by Commit Bot

Track wayland event serial numbers in exo

Currently we ignore these, but properly interfacing with chrome's
drag&drop implementation will require distinguishing between pointer
and touch events from the event serial. This CL adds a mechanism to do
this.

Bug: 927324
Change-Id: If829533c4b5b28b3b2b4eb5826dfe687df0de792
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1709497
Commit-Queue: Fergus Dall <sidereal@google.com>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#681210}
parent a429385f
...@@ -19,6 +19,8 @@ source_set("wayland") { ...@@ -19,6 +19,8 @@ source_set("wayland") {
sources = [ sources = [
"scoped_wl.cc", "scoped_wl.cc",
"scoped_wl.h", "scoped_wl.h",
"serial_tracker.cc",
"serial_tracker.h",
"server.cc", "server.cc",
"server.h", "server.h",
"server_util.cc", "server_util.cc",
......
// Copyright 2019 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 "components/exo/wayland/serial_tracker.h"
#include <wayland-server-core.h>
namespace exo {
namespace wayland {
namespace {
// Number of previous events to retain information about.
constexpr uint32_t kMaxEventsTracked = 1024;
} // namespace
SerialTracker::SerialTracker(struct wl_display* display)
: display_(display), events_(kMaxEventsTracked) {}
SerialTracker::~SerialTracker() {}
uint32_t SerialTracker::GetNextSerial(EventType type) {
uint32_t serial = wl_display_next_serial(display_);
events_[serial % kMaxEventsTracked] = type;
max_event_ = serial + 1;
if ((max_event_ - min_event_) > kMaxEventsTracked)
min_event_ = max_event_ - kMaxEventsTracked;
return serial;
}
base::Optional<SerialTracker::EventType> SerialTracker::GetEventType(
uint32_t serial) const {
if (max_event_ < min_event_) {
// The valid range has partially overflowed the 32 bit space, so we should
// only reject if the serial number is in neither the upper nor lower parts
// of the space.
if (!((serial < max_event_) || (serial >= min_event_)))
return base::nullopt;
} else {
// Normal, non-overflowed case. Reject the serial number if it isn't in the
// interval.
if (!((serial < max_event_) && (serial >= min_event_)))
return base::nullopt;
}
return events_[serial % kMaxEventsTracked];
}
} // namespace wayland
} // namespace exo
// Copyright 2019 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.
#ifndef COMPONENTS_EXO_WAYLAND_SERIAL_TRACKER_H_
#define COMPONENTS_EXO_WAYLAND_SERIAL_TRACKER_H_
#include <map>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/optional.h"
struct wl_display;
namespace exo {
namespace wayland {
class SerialTracker {
public:
enum EventType {
POINTER_ENTER,
POINTER_LEAVE,
POINTER_BUTTON,
TOUCH_DOWN,
TOUCH_UP,
OTHER_EVENT,
};
explicit SerialTracker(struct wl_display* display);
~SerialTracker();
uint32_t GetNextSerial(EventType type);
// Get the EventType for a serial number, or nullopt if the serial number was
// never sent or is too old.
base::Optional<EventType> GetEventType(uint32_t serial) const;
private:
FRIEND_TEST_ALL_PREFIXES(SerialTrackerTest, WrapAroundWholeRange);
struct wl_display* const display_;
// EventTypes are stored in a circular buffer, because serial numbers are
// issued sequentially and we only want to store the most recent events.
std::vector<EventType> events_;
// [min_event_, max_event) is a half-open interval containing the range of
// valid serial numbers. Note that as serial numbers are allowed to wrap
// around the 32 bit space, we cannot assume that max_event_ >= min_event_.
uint32_t min_event_ = 1;
uint32_t max_event_ = 1;
DISALLOW_COPY_AND_ASSIGN(SerialTracker);
};
} // namespace wayland
} // namespace exo
#endif // COMPONENTS_EXO_WAYLAND_SERIAL_TRACKER_H_
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "components/exo/display.h" #include "components/exo/display.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/wayland_display_output.h" #include "components/exo/wayland/wayland_display_output.h"
#include "components/exo/wayland/wl_compositor.h" #include "components/exo/wayland/wl_compositor.h"
#include "components/exo/wayland/wl_data_device_manager.h" #include "components/exo/wayland/wl_data_device_manager.h"
...@@ -103,7 +104,9 @@ const char kWaylandSocketGroup[] = "wayland"; ...@@ -103,7 +104,9 @@ const char kWaylandSocketGroup[] = "wayland";
// Server, public: // Server, public:
Server::Server(Display* display) Server::Server(Display* display)
: display_(display), wl_display_(wl_display_create()) { : display_(display),
wl_display_(wl_display_create()),
serial_tracker_(std::make_unique<SerialTracker>(wl_display_.get())) {
wl_global_create(wl_display_.get(), &wl_compositor_interface, wl_global_create(wl_display_.get(), &wl_compositor_interface,
kWlCompositorVersion, display_, bind_compositor); kWlCompositorVersion, display_, bind_compositor);
wl_global_create(wl_display_.get(), &wl_shm_interface, 1, display_, bind_shm); wl_global_create(wl_display_.get(), &wl_shm_interface, 1, display_, bind_shm);
...@@ -118,9 +121,13 @@ Server::Server(Display* display) ...@@ -118,9 +121,13 @@ Server::Server(Display* display)
OnDisplayAdded(display); OnDisplayAdded(display);
wl_global_create(wl_display_.get(), &zcr_vsync_feedback_v1_interface, 1, wl_global_create(wl_display_.get(), &zcr_vsync_feedback_v1_interface, 1,
display_, bind_vsync_feedback); display_, bind_vsync_feedback);
data_device_manager_data_ = std::make_unique<WaylandDataDeviceManager>(
display_, serial_tracker_.get());
wl_global_create(wl_display_.get(), &wl_data_device_manager_interface, wl_global_create(wl_display_.get(), &wl_data_device_manager_interface,
kWlDataDeviceManagerVersion, display_, kWlDataDeviceManagerVersion, data_device_manager_data_.get(),
bind_data_device_manager); bind_data_device_manager);
wl_global_create(wl_display_.get(), &wp_viewporter_interface, 1, display_, wl_global_create(wl_display_.get(), &wp_viewporter_interface, 1, display_,
bind_viewporter); bind_viewporter);
wl_global_create(wl_display_.get(), &wp_presentation_interface, 1, display_, wl_global_create(wl_display_.get(), &wp_presentation_interface, 1, display_,
...@@ -131,8 +138,12 @@ Server::Server(Display* display) ...@@ -131,8 +138,12 @@ Server::Server(Display* display)
display_, bind_alpha_compositing); display_, bind_alpha_compositing);
wl_global_create(wl_display_.get(), &zcr_stylus_v2_interface, 1, display_, wl_global_create(wl_display_.get(), &zcr_stylus_v2_interface, 1, display_,
bind_stylus_v2); bind_stylus_v2);
seat_data_ =
std::make_unique<WaylandSeat>(display_->seat(), serial_tracker_.get());
wl_global_create(wl_display_.get(), &wl_seat_interface, kWlSeatVersion, wl_global_create(wl_display_.get(), &wl_seat_interface, kWlSeatVersion,
display_->seat(), bind_seat); seat_data_.get(), bind_seat);
wl_global_create(wl_display_.get(), wl_global_create(wl_display_.get(),
&zwp_linux_explicit_synchronization_v1_interface, 1, &zwp_linux_explicit_synchronization_v1_interface, 1,
display_, bind_linux_explicit_synchronization); display_, bind_linux_explicit_synchronization);
...@@ -164,10 +175,16 @@ Server::Server(Display* display) ...@@ -164,10 +175,16 @@ Server::Server(Display* display)
wl_global_create(wl_display_.get(), wl_global_create(wl_display_.get(),
&zwp_relative_pointer_manager_v1_interface, 1, display_, &zwp_relative_pointer_manager_v1_interface, 1, display_,
bind_relative_pointer_manager); bind_relative_pointer_manager);
zwp_text_manager_data_ =
std::make_unique<WaylandTextInputManager>(serial_tracker_.get());
wl_global_create(wl_display_.get(), &zwp_text_input_manager_v1_interface, 1, wl_global_create(wl_display_.get(), &zwp_text_input_manager_v1_interface, 1,
display_, bind_text_input_manager); zwp_text_manager_data_.get(), bind_text_input_manager);
wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1, display_,
bind_xdg_shell_v6); xdg_shell_data_ =
std::make_unique<WaylandXdgShell>(display_, serial_tracker_.get());
wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1,
xdg_shell_data_.get(), bind_xdg_shell_v6);
#endif #endif
#if defined(USE_FULLSCREEN_SHELL) #if defined(USE_FULLSCREEN_SHELL)
......
...@@ -19,7 +19,12 @@ class Display; ...@@ -19,7 +19,12 @@ class Display;
namespace wayland { namespace wayland {
class SerialTracker;
struct WaylandDataDeviceManager;
class WaylandDisplayOutput; class WaylandDisplayOutput;
struct WaylandSeat;
struct WaylandTextInputManager;
struct WaylandXdgShell;
// This class is a thin wrapper around a Wayland display server. All Wayland // This class is a thin wrapper around a Wayland display server. All Wayland
// requests are dispatched into the given Exosphere display. // requests are dispatched into the given Exosphere display.
...@@ -55,7 +60,15 @@ class Server : public display::DisplayObserver { ...@@ -55,7 +60,15 @@ class Server : public display::DisplayObserver {
private: private:
Display* const display_; Display* const display_;
std::unique_ptr<wl_display, WlDisplayDeleter> wl_display_; std::unique_ptr<wl_display, WlDisplayDeleter> wl_display_;
std::unique_ptr<SerialTracker> serial_tracker_;
base::flat_map<int64_t, std::unique_ptr<WaylandDisplayOutput>> outputs_; base::flat_map<int64_t, std::unique_ptr<WaylandDisplayOutput>> outputs_;
std::unique_ptr<WaylandDataDeviceManager> data_device_manager_data_;
std::unique_ptr<WaylandSeat> seat_data_;
#if defined(OS_CHROMEOS)
std::unique_ptr<WaylandTextInputManager> zwp_text_manager_data_;
std::unique_ptr<WaylandXdgShell> xdg_shell_data_;
#endif
DISALLOW_COPY_AND_ASSIGN(Server); DISALLOW_COPY_AND_ASSIGN(Server);
}; };
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <wayland-server-protocol-core.h> #include <wayland-server-protocol-core.h>
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "components/exo/wayland/serial_tracker.h"
#include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/dom_code.h"
namespace exo { namespace exo {
...@@ -15,9 +16,11 @@ namespace wayland { ...@@ -15,9 +16,11 @@ namespace wayland {
#if BUILDFLAG(USE_XKBCOMMON) #if BUILDFLAG(USE_XKBCOMMON)
WaylandKeyboardDelegate::WaylandKeyboardDelegate(wl_resource* keyboard_resource) WaylandKeyboardDelegate::WaylandKeyboardDelegate(wl_resource* keyboard_resource,
SerialTracker* serial_tracker)
: keyboard_resource_(keyboard_resource), : keyboard_resource_(keyboard_resource),
xkb_context_(xkb_context_new(XKB_CONTEXT_NO_FLAGS)) { xkb_context_(xkb_context_new(XKB_CONTEXT_NO_FLAGS)),
serial_tracker_(serial_tracker) {
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
ash::ImeController* ime_controller = ash::Shell::Get()->ime_controller(); ash::ImeController* ime_controller = ash::Shell::Get()->ime_controller();
ime_controller->AddObserver(this); ime_controller->AddObserver(this);
...@@ -59,8 +62,10 @@ void WaylandKeyboardDelegate::OnKeyboardEnter( ...@@ -59,8 +62,10 @@ void WaylandKeyboardDelegate::OnKeyboardEnter(
DCHECK(value); DCHECK(value);
*value = DomCodeToKey(entry.second); *value = DomCodeToKey(entry.second);
} }
wl_keyboard_send_enter(keyboard_resource_, next_serial(), surface_resource, wl_keyboard_send_enter(
&keys); keyboard_resource_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
surface_resource, &keys);
wl_array_release(&keys); wl_array_release(&keys);
wl_client_flush(client()); wl_client_flush(client());
} }
...@@ -68,14 +73,18 @@ void WaylandKeyboardDelegate::OnKeyboardEnter( ...@@ -68,14 +73,18 @@ void WaylandKeyboardDelegate::OnKeyboardEnter(
void WaylandKeyboardDelegate::OnKeyboardLeave(Surface* surface) { void WaylandKeyboardDelegate::OnKeyboardLeave(Surface* surface) {
wl_resource* surface_resource = GetSurfaceResource(surface); wl_resource* surface_resource = GetSurfaceResource(surface);
DCHECK(surface_resource); DCHECK(surface_resource);
wl_keyboard_send_leave(keyboard_resource_, next_serial(), surface_resource); wl_keyboard_send_leave(
keyboard_resource_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
surface_resource);
wl_client_flush(client()); wl_client_flush(client());
} }
uint32_t WaylandKeyboardDelegate::OnKeyboardKey(base::TimeTicks time_stamp, uint32_t WaylandKeyboardDelegate::OnKeyboardKey(base::TimeTicks time_stamp,
ui::DomCode key, ui::DomCode key,
bool pressed) { bool pressed) {
uint32_t serial = next_serial(); uint32_t serial =
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT);
SendTimestamp(time_stamp); SendTimestamp(time_stamp);
wl_keyboard_send_key( wl_keyboard_send_key(
keyboard_resource_, serial, TimeTicksToMilliseconds(time_stamp), keyboard_resource_, serial, TimeTicksToMilliseconds(time_stamp),
...@@ -147,7 +156,8 @@ void WaylandKeyboardDelegate::SendKeyboardModifiers() { ...@@ -147,7 +156,8 @@ void WaylandKeyboardDelegate::SendKeyboardModifiers() {
xkb_state_update_mask(xkb_state_.get(), ModifierFlagsToXkbModifiers(), 0, 0, xkb_state_update_mask(xkb_state_.get(), ModifierFlagsToXkbModifiers(), 0, 0,
0, 0, 0); 0, 0, 0);
wl_keyboard_send_modifiers( wl_keyboard_send_modifiers(
keyboard_resource_, next_serial(), keyboard_resource_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_DEPRESSED), xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_DEPRESSED),
xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LOCKED), xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LOCKED),
xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LATCHED), xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LATCHED),
...@@ -197,10 +207,6 @@ wl_client* WaylandKeyboardDelegate::client() const { ...@@ -197,10 +207,6 @@ wl_client* WaylandKeyboardDelegate::client() const {
return wl_resource_get_client(keyboard_resource_); return wl_resource_get_client(keyboard_resource_);
} }
uint32_t WaylandKeyboardDelegate::next_serial() const {
return wl_display_next_serial(wl_client_get_display(client()));
}
#endif #endif
} // namespace wayland } // namespace wayland
......
...@@ -31,6 +31,7 @@ struct wl_resource; ...@@ -31,6 +31,7 @@ struct wl_resource;
namespace exo { namespace exo {
namespace wayland { namespace wayland {
class SerialTracker;
// Keyboard delegate class that accepts events for surfaces owned by the same // Keyboard delegate class that accepts events for surfaces owned by the same
// client as a keyboard resource. // client as a keyboard resource.
...@@ -44,7 +45,8 @@ class WaylandKeyboardDelegate : public WaylandInputDelegate, ...@@ -44,7 +45,8 @@ class WaylandKeyboardDelegate : public WaylandInputDelegate,
{ {
#if BUILDFLAG(USE_XKBCOMMON) #if BUILDFLAG(USE_XKBCOMMON)
public: public:
explicit WaylandKeyboardDelegate(wl_resource* keyboard_resource); explicit WaylandKeyboardDelegate(wl_resource* keyboard_resource,
SerialTracker* serial_tracker);
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
~WaylandKeyboardDelegate() override; ~WaylandKeyboardDelegate() override;
...@@ -89,9 +91,6 @@ class WaylandKeyboardDelegate : public WaylandInputDelegate, ...@@ -89,9 +91,6 @@ class WaylandKeyboardDelegate : public WaylandInputDelegate,
// The client who own this keyboard instance. // The client who own this keyboard instance.
wl_client* client() const; wl_client* client() const;
// Returns the next serial to use for keyboard events.
uint32_t next_serial() const;
// The keyboard resource associated with the keyboard. // The keyboard resource associated with the keyboard.
wl_resource* const keyboard_resource_; wl_resource* const keyboard_resource_;
...@@ -104,6 +103,9 @@ class WaylandKeyboardDelegate : public WaylandInputDelegate, ...@@ -104,6 +103,9 @@ class WaylandKeyboardDelegate : public WaylandInputDelegate,
// we treat numlock as always on. // we treat numlock as always on.
int modifier_flags_ = ui::EF_NUM_LOCK_ON; int modifier_flags_ = ui::EF_NUM_LOCK_ON;
// Owned by Server, which always outlives this delegate.
SerialTracker* const serial_tracker_;
DISALLOW_COPY_AND_ASSIGN(WaylandKeyboardDelegate); DISALLOW_COPY_AND_ASSIGN(WaylandKeyboardDelegate);
#endif #endif
}; };
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <wayland-server-protocol-core.h> #include <wayland-server-protocol-core.h>
#include "components/exo/pointer.h" #include "components/exo/pointer.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h" #include "components/exo/wayland/server_util.h"
#include "ui/events/event.h" #include "ui/events/event.h"
#include "ui/events/event_constants.h" #include "ui/events/event_constants.h"
...@@ -16,8 +17,9 @@ ...@@ -16,8 +17,9 @@
namespace exo { namespace exo {
namespace wayland { namespace wayland {
WaylandPointerDelegate::WaylandPointerDelegate(wl_resource* pointer_resource) WaylandPointerDelegate::WaylandPointerDelegate(wl_resource* pointer_resource,
: pointer_resource_(pointer_resource) {} SerialTracker* serial_tracker)
: pointer_resource_(pointer_resource), serial_tracker_(serial_tracker) {}
void WaylandPointerDelegate::OnPointerDestroying(Pointer* pointer) { void WaylandPointerDelegate::OnPointerDestroying(Pointer* pointer) {
delete this; delete this;
...@@ -39,15 +41,20 @@ void WaylandPointerDelegate::OnPointerEnter(Surface* surface, ...@@ -39,15 +41,20 @@ void WaylandPointerDelegate::OnPointerEnter(Surface* surface,
DCHECK(surface_resource); DCHECK(surface_resource);
// Should we be sending button events to the client before the enter event // Should we be sending button events to the client before the enter event
// if client's pressed button state is different from |button_flags|? // if client's pressed button state is different from |button_flags|?
wl_pointer_send_enter(pointer_resource_, next_serial(), surface_resource, wl_pointer_send_enter(
wl_fixed_from_double(location.x()), pointer_resource_,
wl_fixed_from_double(location.y())); 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) { void WaylandPointerDelegate::OnPointerLeave(Surface* surface) {
wl_resource* surface_resource = GetSurfaceResource(surface); wl_resource* surface_resource = GetSurfaceResource(surface);
DCHECK(surface_resource); DCHECK(surface_resource);
wl_pointer_send_leave(pointer_resource_, next_serial(), surface_resource); wl_pointer_send_leave(
pointer_resource_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::POINTER_LEAVE),
surface_resource);
} }
void WaylandPointerDelegate::OnPointerMotion(base::TimeTicks time_stamp, void WaylandPointerDelegate::OnPointerMotion(base::TimeTicks time_stamp,
...@@ -71,11 +78,12 @@ void WaylandPointerDelegate::OnPointerButton(base::TimeTicks time_stamp, ...@@ -71,11 +78,12 @@ void WaylandPointerDelegate::OnPointerButton(base::TimeTicks time_stamp,
{ui::EF_FORWARD_MOUSE_BUTTON, BTN_FORWARD}, {ui::EF_FORWARD_MOUSE_BUTTON, BTN_FORWARD},
{ui::EF_BACK_MOUSE_BUTTON, BTN_BACK}, {ui::EF_BACK_MOUSE_BUTTON, BTN_BACK},
}; };
uint32_t serial = next_serial();
for (auto button : buttons) { for (auto button : buttons) {
if (button_flags & button.flag) { if (button_flags & button.flag) {
SendTimestamp(time_stamp); SendTimestamp(time_stamp);
wl_pointer_send_button(pointer_resource_, serial, wl_pointer_send_button(pointer_resource_,
serial_tracker_->GetNextSerial(
SerialTracker::EventType::POINTER_BUTTON),
TimeTicksToMilliseconds(time_stamp), button.value, TimeTicksToMilliseconds(time_stamp), button.value,
pressed ? WL_POINTER_BUTTON_STATE_PRESSED pressed ? WL_POINTER_BUTTON_STATE_PRESSED
: WL_POINTER_BUTTON_STATE_RELEASED); : WL_POINTER_BUTTON_STATE_RELEASED);
...@@ -135,9 +143,5 @@ wl_client* WaylandPointerDelegate::client() const { ...@@ -135,9 +143,5 @@ wl_client* WaylandPointerDelegate::client() const {
return wl_resource_get_client(pointer_resource_); return wl_resource_get_client(pointer_resource_);
} }
uint32_t WaylandPointerDelegate::next_serial() const {
return wl_display_next_serial(wl_client_get_display(client()));
}
} // namespace wayland } // namespace wayland
} // namespace exo } // namespace exo
...@@ -13,13 +13,15 @@ struct wl_resource; ...@@ -13,13 +13,15 @@ struct wl_resource;
namespace exo { namespace exo {
namespace wayland { namespace wayland {
class SerialTracker;
// Pointer delegate class that accepts events for surfaces owned by the same // Pointer delegate class that accepts events for surfaces owned by the same
// client as a pointer resource. // client as a pointer resource.
class WaylandPointerDelegate : public WaylandInputDelegate, class WaylandPointerDelegate : public WaylandInputDelegate,
public PointerDelegate { public PointerDelegate {
public: public:
explicit WaylandPointerDelegate(wl_resource* pointer_resource); explicit WaylandPointerDelegate(wl_resource* pointer_resource,
SerialTracker* serial_tracker);
// Overridden from PointerDelegate: // Overridden from PointerDelegate:
void OnPointerDestroying(Pointer* pointer) override; void OnPointerDestroying(Pointer* pointer) override;
...@@ -43,12 +45,12 @@ class WaylandPointerDelegate : public WaylandInputDelegate, ...@@ -43,12 +45,12 @@ class WaylandPointerDelegate : public WaylandInputDelegate,
// The client who own this pointer instance. // The client who own this pointer instance.
wl_client* client() const; wl_client* client() const;
// Returns the next serial to use for pointer events.
uint32_t next_serial() const;
// The pointer resource associated with the pointer. // The pointer resource associated with the pointer.
wl_resource* const pointer_resource_; wl_resource* const pointer_resource_;
// Owned by Server, which always outlives this delegate.
SerialTracker* const serial_tracker_;
DISALLOW_COPY_AND_ASSIGN(WaylandPointerDelegate); DISALLOW_COPY_AND_ASSIGN(WaylandPointerDelegate);
}; };
......
...@@ -8,13 +8,15 @@ ...@@ -8,13 +8,15 @@
#include <wayland-server-protocol-core.h> #include <wayland-server-protocol-core.h>
#include "components/exo/touch.h" #include "components/exo/touch.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h" #include "components/exo/wayland/server_util.h"
namespace exo { namespace exo {
namespace wayland { namespace wayland {
WaylandTouchDelegate::WaylandTouchDelegate(wl_resource* touch_resource) WaylandTouchDelegate::WaylandTouchDelegate(wl_resource* touch_resource,
: touch_resource_(touch_resource) {} SerialTracker* serial_tracker)
: touch_resource_(touch_resource), serial_tracker_(serial_tracker) {}
void WaylandTouchDelegate::OnTouchDestroying(Touch* touch) { void WaylandTouchDelegate::OnTouchDestroying(Touch* touch) {
delete this; delete this;
...@@ -35,15 +37,18 @@ void WaylandTouchDelegate::OnTouchDown(Surface* surface, ...@@ -35,15 +37,18 @@ void WaylandTouchDelegate::OnTouchDown(Surface* surface,
wl_resource* surface_resource = GetSurfaceResource(surface); wl_resource* surface_resource = GetSurfaceResource(surface);
DCHECK(surface_resource); DCHECK(surface_resource);
SendTimestamp(time_stamp); SendTimestamp(time_stamp);
wl_touch_send_down(touch_resource_, next_serial(), wl_touch_send_down(
TimeTicksToMilliseconds(time_stamp), surface_resource, id, touch_resource_,
wl_fixed_from_double(location.x()), serial_tracker_->GetNextSerial(SerialTracker::EventType::TOUCH_DOWN),
wl_fixed_from_double(location.y())); TimeTicksToMilliseconds(time_stamp), surface_resource, id,
wl_fixed_from_double(location.x()), wl_fixed_from_double(location.y()));
} }
void WaylandTouchDelegate::OnTouchUp(base::TimeTicks time_stamp, int id) { void WaylandTouchDelegate::OnTouchUp(base::TimeTicks time_stamp, int id) {
SendTimestamp(time_stamp); SendTimestamp(time_stamp);
wl_touch_send_up(touch_resource_, next_serial(), wl_touch_send_up(
TimeTicksToMilliseconds(time_stamp), id); touch_resource_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::TOUCH_UP),
TimeTicksToMilliseconds(time_stamp), id);
} }
void WaylandTouchDelegate::OnTouchMotion(base::TimeTicks time_stamp, void WaylandTouchDelegate::OnTouchMotion(base::TimeTicks time_stamp,
int id, int id,
...@@ -75,9 +80,5 @@ wl_client* WaylandTouchDelegate::client() const { ...@@ -75,9 +80,5 @@ wl_client* WaylandTouchDelegate::client() const {
return wl_resource_get_client(touch_resource_); return wl_resource_get_client(touch_resource_);
} }
uint32_t WaylandTouchDelegate::next_serial() const {
return wl_display_next_serial(wl_client_get_display(client()));
}
} // namespace wayland } // namespace wayland
} // namespace exo } // namespace exo
...@@ -13,12 +13,14 @@ struct wl_resource; ...@@ -13,12 +13,14 @@ struct wl_resource;
namespace exo { namespace exo {
namespace wayland { namespace wayland {
class SerialTracker;
// Touch delegate class that accepts events for surfaces owned by the same // Touch delegate class that accepts events for surfaces owned by the same
// client as a touch resource. // client as a touch resource.
class WaylandTouchDelegate : public WaylandInputDelegate, public TouchDelegate { class WaylandTouchDelegate : public WaylandInputDelegate, public TouchDelegate {
public: public:
explicit WaylandTouchDelegate(wl_resource* touch_resource); explicit WaylandTouchDelegate(wl_resource* touch_resource,
SerialTracker* serial_tracker);
// Overridden from TouchDelegate: // Overridden from TouchDelegate:
void OnTouchDestroying(Touch* touch) override; void OnTouchDestroying(Touch* touch) override;
...@@ -39,12 +41,12 @@ class WaylandTouchDelegate : public WaylandInputDelegate, public TouchDelegate { ...@@ -39,12 +41,12 @@ class WaylandTouchDelegate : public WaylandInputDelegate, public TouchDelegate {
// The client who own this touch instance. // The client who own this touch instance.
wl_client* client() const; wl_client* client() const;
// Returns the next serial to use for keyboard events.
uint32_t next_serial() const;
// The touch resource associated with the touch. // The touch resource associated with the touch.
wl_resource* const touch_resource_; wl_resource* const touch_resource_;
// Owned by Server, which always outlives this delegate.
SerialTracker* const serial_tracker_;
DISALLOW_COPY_AND_ASSIGN(WaylandTouchDelegate); DISALLOW_COPY_AND_ASSIGN(WaylandTouchDelegate);
}; };
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "components/exo/data_source.h" #include "components/exo/data_source.h"
#include "components/exo/data_source_delegate.h" #include "components/exo/data_source_delegate.h"
#include "components/exo/display.h" #include "components/exo/display.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h" #include "components/exo/wayland/server_util.h"
namespace exo { namespace exo {
...@@ -229,8 +230,12 @@ const struct wl_data_offer_interface data_offer_implementation = { ...@@ -229,8 +230,12 @@ const struct wl_data_offer_interface data_offer_implementation = {
class WaylandDataDeviceDelegate : public DataDeviceDelegate { class WaylandDataDeviceDelegate : public DataDeviceDelegate {
public: public:
WaylandDataDeviceDelegate(wl_client* client, wl_resource* device_resource) WaylandDataDeviceDelegate(wl_client* client,
: client_(client), data_device_resource_(device_resource) {} wl_resource* device_resource,
SerialTracker* serial_tracker)
: client_(client),
data_device_resource_(device_resource),
serial_tracker_(serial_tracker) {}
// Overridden from DataDeviceDelegate: // Overridden from DataDeviceDelegate:
void OnDataDeviceDestroying(DataDevice* device) override { delete this; } void OnDataDeviceDestroying(DataDevice* device) override { delete this; }
...@@ -258,7 +263,7 @@ class WaylandDataDeviceDelegate : public DataDeviceDelegate { ...@@ -258,7 +263,7 @@ class WaylandDataDeviceDelegate : public DataDeviceDelegate {
const DataOffer& data_offer) override { const DataOffer& data_offer) override {
wl_data_device_send_enter( wl_data_device_send_enter(
data_device_resource_, data_device_resource_,
wl_display_next_serial(wl_client_get_display(client_)), serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
GetSurfaceResource(surface), wl_fixed_from_double(point.x()), GetSurfaceResource(surface), wl_fixed_from_double(point.x()),
wl_fixed_from_double(point.y()), GetDataOfferResource(&data_offer)); wl_fixed_from_double(point.y()), GetDataOfferResource(&data_offer));
wl_client_flush(client_); wl_client_flush(client_);
...@@ -287,6 +292,9 @@ class WaylandDataDeviceDelegate : public DataDeviceDelegate { ...@@ -287,6 +292,9 @@ class WaylandDataDeviceDelegate : public DataDeviceDelegate {
wl_client* const client_; wl_client* const client_;
wl_resource* const data_device_resource_; wl_resource* const data_device_resource_;
// Owned by Server, which always outlives this delegate.
SerialTracker* const serial_tracker_;
DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceDelegate); DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceDelegate);
}; };
...@@ -334,12 +342,13 @@ void data_device_manager_get_data_device(wl_client* client, ...@@ -334,12 +342,13 @@ void data_device_manager_get_data_device(wl_client* client,
wl_resource* resource, wl_resource* resource,
uint32_t id, uint32_t id,
wl_resource* seat_resource) { wl_resource* seat_resource) {
Display* display = GetUserDataAs<Display>(resource); auto* data = GetUserDataAs<WaylandDataDeviceManager>(resource);
wl_resource* data_device_resource = wl_resource_create( wl_resource* data_device_resource = wl_resource_create(
client, &wl_data_device_interface, wl_resource_get_version(resource), id); client, &wl_data_device_interface, wl_resource_get_version(resource), id);
SetImplementation(data_device_resource, &data_device_implementation, SetImplementation(
display->CreateDataDevice(new WaylandDataDeviceDelegate( data_device_resource, &data_device_implementation,
client, data_device_resource))); data->display->CreateDataDevice(new WaylandDataDeviceDelegate(
client, data_device_resource, data->serial_tracker)));
} }
const struct wl_data_device_manager_interface const struct wl_data_device_manager_interface
......
...@@ -7,13 +7,32 @@ ...@@ -7,13 +7,32 @@
#include <stdint.h> #include <stdint.h>
#include "base/macros.h"
struct wl_client; struct wl_client;
namespace exo { namespace exo {
class Display;
namespace wayland { namespace wayland {
class SerialTracker;
constexpr uint32_t kWlDataDeviceManagerVersion = 3; constexpr uint32_t kWlDataDeviceManagerVersion = 3;
struct WaylandDataDeviceManager {
WaylandDataDeviceManager(Display* display, SerialTracker* serial_tracker)
: display(display), serial_tracker(serial_tracker) {}
// Owned by WaylandServerController, which always outlives
// wl_data_device_manager.
Display* const display;
// Owned by Server, which always outlives wl_data_device_manager.
SerialTracker* const serial_tracker;
DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManager);
};
void bind_data_device_manager(wl_client* client, void bind_data_device_manager(wl_client* client,
void* data, void* data,
uint32_t version, uint32_t version,
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "components/exo/pointer.h" #include "components/exo/pointer.h"
#include "components/exo/touch.h" #include "components/exo/touch.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h" #include "components/exo/wayland/server_util.h"
#include "components/exo/wayland/wayland_pointer_delegate.h" #include "components/exo/wayland/wayland_pointer_delegate.h"
#include "components/exo/wayland/wayland_touch_delegate.h" #include "components/exo/wayland/wayland_touch_delegate.h"
...@@ -72,25 +73,29 @@ const struct wl_touch_interface touch_implementation = {touch_release}; ...@@ -72,25 +73,29 @@ const struct wl_touch_interface touch_implementation = {touch_release};
// wl_seat_interface: // wl_seat_interface:
void seat_get_pointer(wl_client* client, wl_resource* resource, uint32_t id) { void seat_get_pointer(wl_client* client, wl_resource* resource, uint32_t id) {
auto* data = GetUserDataAs<WaylandSeat>(resource);
wl_resource* pointer_resource = wl_resource_create( wl_resource* pointer_resource = wl_resource_create(
client, &wl_pointer_interface, wl_resource_get_version(resource), id); client, &wl_pointer_interface, wl_resource_get_version(resource), id);
SetImplementation( SetImplementation(pointer_resource, &pointer_implementation,
pointer_resource, &pointer_implementation, std::make_unique<Pointer>(new WaylandPointerDelegate(
std::make_unique<Pointer>(new WaylandPointerDelegate(pointer_resource))); pointer_resource, data->serial_tracker)));
} }
void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) { void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) {
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
#if BUILDFLAG(USE_XKBCOMMON) #if BUILDFLAG(USE_XKBCOMMON)
auto* data = GetUserDataAs<WaylandSeat>(resource);
uint32_t version = wl_resource_get_version(resource); uint32_t version = wl_resource_get_version(resource);
wl_resource* keyboard_resource = wl_resource* keyboard_resource =
wl_resource_create(client, &wl_keyboard_interface, version, id); wl_resource_create(client, &wl_keyboard_interface, version, id);
WaylandKeyboardDelegate* delegate = WaylandKeyboardDelegate* delegate =
new WaylandKeyboardDelegate(keyboard_resource); new WaylandKeyboardDelegate(keyboard_resource, data->serial_tracker);
std::unique_ptr<Keyboard> keyboard = std::unique_ptr<Keyboard> keyboard =
std::make_unique<Keyboard>(delegate, GetUserDataAs<Seat>(resource)); std::make_unique<Keyboard>(delegate, data->seat);
keyboard->AddObserver(delegate); keyboard->AddObserver(delegate);
SetImplementation(keyboard_resource, &keyboard_implementation, SetImplementation(keyboard_resource, &keyboard_implementation,
std::move(keyboard)); std::move(keyboard));
...@@ -107,12 +112,14 @@ void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) { ...@@ -107,12 +112,14 @@ void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) {
} }
void seat_get_touch(wl_client* client, wl_resource* resource, uint32_t id) { void seat_get_touch(wl_client* client, wl_resource* resource, uint32_t id) {
auto* data = GetUserDataAs<WaylandSeat>(resource);
wl_resource* touch_resource = wl_resource_create( wl_resource* touch_resource = wl_resource_create(
client, &wl_touch_interface, wl_resource_get_version(resource), id); client, &wl_touch_interface, wl_resource_get_version(resource), id);
SetImplementation( SetImplementation(touch_resource, &touch_implementation,
touch_resource, &touch_implementation, std::make_unique<Touch>(new WaylandTouchDelegate(
std::make_unique<Touch>(new WaylandTouchDelegate(touch_resource))); touch_resource, data->serial_tracker)));
} }
void seat_release(wl_client* client, wl_resource* resource) { void seat_release(wl_client* client, wl_resource* resource) {
......
...@@ -7,13 +7,31 @@ ...@@ -7,13 +7,31 @@
#include <stdint.h> #include <stdint.h>
#include "base/macros.h"
struct wl_client; struct wl_client;
namespace exo { namespace exo {
class Seat;
namespace wayland { namespace wayland {
class SerialTracker;
constexpr uint32_t kWlSeatVersion = 6; constexpr uint32_t kWlSeatVersion = 6;
struct WaylandSeat {
WaylandSeat(Seat* seat, SerialTracker* serial_tracker)
: seat(seat), serial_tracker(serial_tracker) {}
// Owned by Display, which always outlives wl_seat.
Seat* const seat;
// Owned by Server, which always outlives wl_seat.
SerialTracker* const serial_tracker;
DISALLOW_COPY_AND_ASSIGN(WaylandSeat);
};
void bind_seat(wl_client* client, void* data, uint32_t version, uint32_t id); void bind_seat(wl_client* client, void* data, uint32_t version, uint32_t id);
} // namespace wayland } // namespace wayland
......
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
#include <wayland-server-protocol-core.h> #include <wayland-server-protocol-core.h>
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/exo/display.h"
#include "components/exo/text_input.h" #include "components/exo/text_input.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h" #include "components/exo/wayland/server_util.h"
#include "ui/events/event.h" #include "ui/events/event.h"
#include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/dom/keycode_converter.h"
...@@ -28,7 +30,9 @@ namespace { ...@@ -28,7 +30,9 @@ namespace {
class WaylandTextInputDelegate : public TextInput::Delegate { class WaylandTextInputDelegate : public TextInput::Delegate {
public: public:
WaylandTextInputDelegate(wl_resource* text_input) : text_input_(text_input) {} WaylandTextInputDelegate(wl_resource* text_input,
SerialTracker* serial_tracker)
: text_input_(text_input), serial_tracker_(serial_tracker) {}
~WaylandTextInputDelegate() override = default; ~WaylandTextInputDelegate() override = default;
void set_surface(wl_resource* surface) { surface_ = surface; } void set_surface(wl_resource* surface) { surface_ = surface; }
...@@ -36,10 +40,6 @@ class WaylandTextInputDelegate : public TextInput::Delegate { ...@@ -36,10 +40,6 @@ class WaylandTextInputDelegate : public TextInput::Delegate {
private: private:
wl_client* client() { return wl_resource_get_client(text_input_); } wl_client* client() { return wl_resource_get_client(text_input_); }
uint32_t next_serial() {
return wl_display_next_serial(wl_client_get_display(client()));
}
// TextInput::Delegate: // TextInput::Delegate:
void Activated() override { void Activated() override {
zwp_text_input_v1_send_enter(text_input_, surface_); zwp_text_input_v1_send_enter(text_input_, surface_);
...@@ -87,15 +87,19 @@ class WaylandTextInputDelegate : public TextInput::Delegate { ...@@ -87,15 +87,19 @@ class WaylandTextInputDelegate : public TextInput::Delegate {
zwp_text_input_v1_send_preedit_cursor(text_input_, pos); zwp_text_input_v1_send_preedit_cursor(text_input_, pos);
const std::string utf8 = base::UTF16ToUTF8(composition.text); const std::string utf8 = base::UTF16ToUTF8(composition.text);
zwp_text_input_v1_send_preedit_string(text_input_, next_serial(), zwp_text_input_v1_send_preedit_string(
utf8.c_str(), utf8.c_str()); text_input_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
utf8.c_str(), utf8.c_str());
wl_client_flush(client()); wl_client_flush(client());
} }
void Commit(const base::string16& text) override { void Commit(const base::string16& text) override {
zwp_text_input_v1_send_commit_string(text_input_, next_serial(), zwp_text_input_v1_send_commit_string(
base::UTF16ToUTF8(text).c_str()); text_input_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
base::UTF16ToUTF8(text).c_str());
wl_client_flush(client()); wl_client_flush(client());
} }
...@@ -120,12 +124,14 @@ class WaylandTextInputDelegate : public TextInput::Delegate { ...@@ -120,12 +124,14 @@ class WaylandTextInputDelegate : public TextInput::Delegate {
// 1-bit shifts to adjust the bitpattern for the modifiers; see also // 1-bit shifts to adjust the bitpattern for the modifiers; see also
// WaylandTextInputDelegate::SendModifiers(). // WaylandTextInputDelegate::SendModifiers().
uint32_t modifiers = (event.flags() & modifiers_mask) >> 1; uint32_t modifiers = (event.flags() & modifiers_mask) >> 1;
zwp_text_input_v1_send_keysym(text_input_,
TimeTicksToMilliseconds(event.time_stamp()), zwp_text_input_v1_send_keysym(
next_serial(), code, text_input_, TimeTicksToMilliseconds(event.time_stamp()),
pressed ? WL_KEYBOARD_KEY_STATE_PRESSED serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
: WL_KEYBOARD_KEY_STATE_RELEASED, code,
modifiers); pressed ? WL_KEYBOARD_KEY_STATE_PRESSED
: WL_KEYBOARD_KEY_STATE_RELEASED,
modifiers);
wl_client_flush(client()); wl_client_flush(client());
} }
...@@ -141,13 +147,19 @@ class WaylandTextInputDelegate : public TextInput::Delegate { ...@@ -141,13 +147,19 @@ class WaylandTextInputDelegate : public TextInput::Delegate {
case base::i18n::UNKNOWN_DIRECTION: case base::i18n::UNKNOWN_DIRECTION:
LOG(ERROR) << "Unrecognized direction: " << direction; LOG(ERROR) << "Unrecognized direction: " << direction;
} }
zwp_text_input_v1_send_text_direction(text_input_, next_serial(),
wayland_direction); zwp_text_input_v1_send_text_direction(
text_input_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
wayland_direction);
} }
wl_resource* text_input_; wl_resource* text_input_;
wl_resource* surface_ = nullptr; wl_resource* surface_ = nullptr;
// Owned by Server, which always outlives this delegate.
SerialTracker* const serial_tracker_;
DISALLOW_COPY_AND_ASSIGN(WaylandTextInputDelegate); DISALLOW_COPY_AND_ASSIGN(WaylandTextInputDelegate);
}; };
...@@ -334,13 +346,15 @@ const struct zwp_text_input_v1_interface text_input_v1_implementation = { ...@@ -334,13 +346,15 @@ const struct zwp_text_input_v1_interface text_input_v1_implementation = {
void text_input_manager_create_text_input(wl_client* client, void text_input_manager_create_text_input(wl_client* client,
wl_resource* resource, wl_resource* resource,
uint32_t id) { uint32_t id) {
auto* data = GetUserDataAs<WaylandTextInputManager>(resource);
wl_resource* text_input_resource = wl_resource* text_input_resource =
wl_resource_create(client, &zwp_text_input_v1_interface, 1, id); wl_resource_create(client, &zwp_text_input_v1_interface, 1, id);
SetImplementation( SetImplementation(
text_input_resource, &text_input_v1_implementation, text_input_resource, &text_input_v1_implementation,
std::make_unique<TextInput>( std::make_unique<TextInput>(std::make_unique<WaylandTextInputDelegate>(
std::make_unique<WaylandTextInputDelegate>(text_input_resource))); text_input_resource, data->serial_tracker)));
} }
const struct zwp_text_input_manager_v1_interface const struct zwp_text_input_manager_v1_interface
......
...@@ -7,10 +7,23 @@ ...@@ -7,10 +7,23 @@
#include <stdint.h> #include <stdint.h>
#include "base/macros.h"
struct wl_client; struct wl_client;
namespace exo { namespace exo {
namespace wayland { namespace wayland {
class SerialTracker;
struct WaylandTextInputManager {
WaylandTextInputManager(SerialTracker* serial_tracker)
: serial_tracker(serial_tracker) {}
// Owned by Server, which always outlives zwp_text_input_manager.
SerialTracker* const serial_tracker;
DISALLOW_COPY_AND_ASSIGN(WaylandTextInputManager);
};
void bind_text_input_manager(wl_client* client, void bind_text_input_manager(wl_client* client,
void* data, void* data,
......
This diff is collapsed.
...@@ -7,10 +7,28 @@ ...@@ -7,10 +7,28 @@
#include <stdint.h> #include <stdint.h>
#include "base/macros.h"
struct wl_client; struct wl_client;
namespace exo { namespace exo {
class Display;
namespace wayland { namespace wayland {
class SerialTracker;
struct WaylandXdgShell {
WaylandXdgShell(Display* display, SerialTracker* serial_tracker)
: display(display), serial_tracker(serial_tracker) {}
// Owned by WaylandServerController, which always outlives zxdg_shell.
Display* const display;
// Owned by Server, which always outlives zxdg_shell.
SerialTracker* const serial_tracker;
DISALLOW_COPY_AND_ASSIGN(WaylandXdgShell);
};
void bind_xdg_shell_v6(wl_client* client, void bind_xdg_shell_v6(wl_client* client,
void* data, void* data,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment