| Index: ui/events/platform/x11/x11_event_source.cc
|
| diff --git a/ui/events/platform/x11/x11_event_source.cc b/ui/events/platform/x11/x11_event_source.cc
|
| index 1a9aa94b6a1954ecaccfb8626ff6dd24c4a9027c..2d9b0c032563c1f81e8faba589c4175ea8394561 100644
|
| --- a/ui/events/platform/x11/x11_event_source.cc
|
| +++ b/ui/events/platform/x11/x11_event_source.cc
|
| @@ -7,6 +7,8 @@
|
| #include <X11/Xatom.h>
|
| #include <X11/XKBlib.h>
|
| #include <X11/Xlib.h>
|
| +#include <X11/Xlib-xcb.h>
|
| +#include <xcb/xcbext.h>
|
|
|
| #include "base/logging.h"
|
| #include "base/metrics/histogram_macros.h"
|
| @@ -17,6 +19,8 @@
|
| #include "ui/events/platform/platform_event_dispatcher.h"
|
| #include "ui/events/platform/x11/x11_hotplug_event_handler.h"
|
|
|
| +#define XLIB_SEQUENCE_COMPARE(a, op, b) (((int64_t)(a) - (int64_t)(b)) op 0)
|
| +
|
| namespace ui {
|
|
|
| namespace {
|
| @@ -92,11 +96,22 @@ Bool IsPropertyNotifyForTimestamp(Display* display,
|
|
|
| X11EventSource* X11EventSource::instance_ = nullptr;
|
|
|
| +X11EventSource::Request::Request(uint32_t sequence)
|
| + : sequence_(sequence), weak_factory_(this) {}
|
| +X11EventSource::Request::~Request() {}
|
| +
|
| +base::WeakPtr<X11EventSource::Request> X11EventSource::Request::GetWeakPtr() {
|
| + return weak_factory_.GetWeakPtr();
|
| +}
|
| +
|
| X11EventSource::X11EventSource(X11EventSourceDelegate* delegate,
|
| XDisplay* display)
|
| : delegate_(delegate),
|
| display_(display),
|
| + connection_(XGetXCBConnection(display_)),
|
| dummy_initialized_(false),
|
| + next_reply_(nullptr),
|
| + next_error_(nullptr),
|
| continue_stream_(true) {
|
| DCHECK(!instance_);
|
| instance_ = this;
|
| @@ -109,6 +124,7 @@ X11EventSource::X11EventSource(X11EventSourceDelegate* delegate,
|
|
|
| X11EventSource::~X11EventSource() {
|
| DCHECK_EQ(this, instance_);
|
| + DCHECK(!next_reply_ && !next_error_);
|
| instance_ = nullptr;
|
| if (dummy_initialized_)
|
| XDestroyWindow(display_, dummy_window_);
|
| @@ -133,11 +149,22 @@ void X11EventSource::DispatchXEvents() {
|
| // It may be useful to eventually align this event dispatch with vsync, but
|
| // not yet.
|
| continue_stream_ = true;
|
| - while (XPending(display_) && continue_stream_) {
|
| - XEvent xevent;
|
| - XNextEvent(display_, &xevent);
|
| - ExtractCookieDataDispatchEvent(&xevent);
|
| +
|
| + // Read all available data.
|
| + XEventsQueued(display_, QueuedAfterReading);
|
| +
|
| + while (HasNextReply() && HasNextEvent() && continue_stream_) {
|
| + if (XLIB_SEQUENCE_COMPARE(NextReplySequence(), <=, NextEventSequence()))
|
| + ProcessNextReply();
|
| + else
|
| + ProcessNextEvent();
|
| }
|
| +
|
| + while (HasNextReply() && continue_stream_)
|
| + ProcessNextReply();
|
| +
|
| + while (HasNextEvent() && continue_stream_)
|
| + ProcessNextEvent();
|
| }
|
|
|
| void X11EventSource::DispatchXEventNow(XEvent* event) {
|
| @@ -220,6 +247,44 @@ X11EventSource::GetRootCursorLocationFromCurrentEvent() const {
|
| return base::nullopt;
|
| }
|
|
|
| +void X11EventSource::EnqueueRequest(Request* request) {
|
| + DCHECK(request);
|
| + request_queue_.emplace_back(request);
|
| + request->it_ = --request_queue_.end();
|
| +}
|
| +
|
| +void X11EventSource::DiscardRequest(Request* request) {
|
| + DCHECK(connection_);
|
| + if ((next_reply_ || next_error_) && request == request_queue_.front().get()) {
|
| + free(next_reply_);
|
| + free(next_error_);
|
| + next_reply_ = nullptr;
|
| + next_error_ = nullptr;
|
| + } else {
|
| + xcb_discard_reply(connection_, request->sequence());
|
| + }
|
| + request_queue_.erase(request->it_);
|
| +}
|
| +
|
| +bool X11EventSource::DispatchRequestNow(Request* request) {
|
| + DCHECK(connection_);
|
| +
|
| + bool success = false;
|
| + if ((next_reply_ || next_error_) && request == request_queue_.front().get()) {
|
| + success = !next_error_;
|
| + ProcessNextReply();
|
| + } else {
|
| + xcb_generic_error_t* error = nullptr;
|
| + void* reply = xcb_wait_for_reply(connection_, request->sequence(), &error);
|
| + success = !error;
|
| + ProcessRequest(request, reinterpret_cast<xcb_generic_reply_t*>(reply),
|
| + error);
|
| + free(reply);
|
| + free(error);
|
| + }
|
| + return success;
|
| +}
|
| +
|
| ////////////////////////////////////////////////////////////////////////////////
|
| // X11EventSource, protected
|
|
|
| @@ -283,6 +348,62 @@ void X11EventSource::BlockOnWindowStructureEvent(XID window, int type) {
|
| } while (event.type != type);
|
| }
|
|
|
| +bool X11EventSource::HasNextReply() {
|
| + if (request_queue_.empty() || xcb_connection_has_error(connection_))
|
| + return false;
|
| + if (next_reply_ || next_error_)
|
| + return true;
|
| +
|
| + return xcb_poll_for_reply(connection_, request_queue_.front()->sequence(),
|
| + &next_reply_, &next_error_);
|
| +}
|
| +
|
| +uint32_t X11EventSource::NextReplySequence() {
|
| + DCHECK(HasNextReply());
|
| + return request_queue_.front()->sequence();
|
| +}
|
| +
|
| +void X11EventSource::ProcessRequest(Request* request,
|
| + xcb_generic_reply_t* reply,
|
| + xcb_generic_error_t* error) {
|
| + base::WeakPtr<Request> weak_ptr = request->GetWeakPtr();
|
| + request->OnReply(reply, error);
|
| + // OnReply() may have chosen to discard this request already.
|
| + if (weak_ptr)
|
| + request_queue_.erase(request->it_);
|
| +}
|
| +
|
| +void X11EventSource::ProcessNextReply() {
|
| + DCHECK(HasNextReply());
|
| +
|
| + ProcessRequest(request_queue_.front().get(),
|
| + reinterpret_cast<xcb_generic_reply_t*>(next_reply_),
|
| + next_error_);
|
| +
|
| + free(next_reply_);
|
| + free(next_error_);
|
| + next_reply_ = nullptr;
|
| + next_error_ = nullptr;
|
| +}
|
| +
|
| +bool X11EventSource::HasNextEvent() {
|
| + return XEventsQueued(display_, QueuedAlready);
|
| +}
|
| +
|
| +uint32_t X11EventSource::NextEventSequence() {
|
| + DCHECK(HasNextEvent());
|
| + XEvent event;
|
| + XPeekEvent(display_, &event);
|
| + return event.xany.serial;
|
| +}
|
| +
|
| +void X11EventSource::ProcessNextEvent() {
|
| + DCHECK(HasNextEvent());
|
| + XEvent event;
|
| + XNextEvent(display_, &event);
|
| + ExtractCookieDataDispatchEvent(&event);
|
| +}
|
| +
|
| void X11EventSource::StopCurrentEventStream() {
|
| continue_stream_ = false;
|
| }
|
|
|