diff options
Diffstat (limited to 'chromium/ui/gfx/x/connection.cc')
-rw-r--r-- | chromium/ui/gfx/x/connection.cc | 222 |
1 files changed, 220 insertions, 2 deletions
diff --git a/chromium/ui/gfx/x/connection.cc b/chromium/ui/gfx/x/connection.cc index 6dd336cac57..af5b6bfb1ae 100644 --- a/chromium/ui/gfx/x/connection.cc +++ b/chromium/ui/gfx/x/connection.cc @@ -4,13 +4,44 @@ #include "ui/gfx/x/connection.h" +#include <X11/Xlib-xcb.h> +#include <X11/Xlib.h> +#include <xcb/xcb.h> + +#include <algorithm> + #include "base/command_line.h" +#include "ui/gfx/x/bigreq.h" +#include "ui/gfx/x/event.h" +#include "ui/gfx/x/randr.h" #include "ui/gfx/x/x11_switches.h" +#include "ui/gfx/x/xproto_types.h" namespace x11 { namespace { +// On the wire, sequence IDs are 16 bits. In xcb, they're usually extended to +// 32 and sometimes 64 bits. In Xlib, they're extended to unsigned long, which +// may be 32 or 64 bits depending on the platform. This function is intended to +// prevent bugs caused by comparing two differently sized sequences. Also +// handles rollover. To use, compare the result of this function with 0. For +// example, to compare seq1 <= seq2, use CompareSequenceIds(seq1, seq2) <= 0. +template <typename T, typename U> +auto CompareSequenceIds(T t, U u) { + static_assert(std::is_unsigned<T>::value, ""); + static_assert(std::is_unsigned<U>::value, ""); + // Cast to the smaller of the two types so that comparisons will always work. + // If we casted to the larger type, then the smaller type will be zero-padded + // and may incorrectly compare less than the other value. + using SmallerType = + typename std::conditional<sizeof(T) <= sizeof(U), T, U>::type; + SmallerType t0 = static_cast<SmallerType>(t); + SmallerType u0 = static_cast<SmallerType>(u); + using SignedType = typename std::make_signed<SmallerType>::type; + return static_cast<SignedType>(t0 - u0); +} + XDisplay* OpenNewXDisplay() { if (!XInitThreads()) return nullptr; @@ -23,10 +54,197 @@ XDisplay* OpenNewXDisplay() { } // namespace Connection* Connection::Get() { - static Connection* instance = new Connection(OpenNewXDisplay()); + static Connection* instance = new Connection; return instance; } -Connection::Connection(XDisplay* display) : XProto(display) {} +Connection::Connection() : XProto(this), display_(OpenNewXDisplay()) { + if (display_) { + XSetEventQueueOwner(display_, XCBOwnsEventQueue); + + setup_ = Read<Setup>( + reinterpret_cast<const uint8_t*>(xcb_get_setup(XcbConnection()))); + default_screen_ = &setup_.roots[DefaultScreenId()]; + default_root_depth_ = &*std::find_if( + default_screen_->allowed_depths.begin(), + default_screen_->allowed_depths.end(), [&](const Depth& depth) { + return depth.depth == default_screen_->root_depth; + }); + default_root_visual_ = &*std::find_if( + default_root_depth_->visuals.begin(), + default_root_depth_->visuals.end(), [&](const VisualType visual) { + return visual.visual_id == default_screen_->root_visual; + }); + } else { + // Default-initialize the setup data so we always have something to return. + setup_.roots.emplace_back(); + default_screen_ = &setup_.roots[0]; + default_screen_->allowed_depths.emplace_back(); + default_root_depth_ = &default_screen_->allowed_depths[0]; + default_root_depth_->visuals.emplace_back(); + default_root_visual_ = &default_root_depth_->visuals[0]; + } + + ExtensionManager::Init(this); + if (auto response = bigreq().Enable({}).Sync()) + extended_max_request_length_ = response->maximum_request_length; +} + +Connection::~Connection() { + if (display_) + XCloseDisplay(display_); +} + +xcb_connection_t* Connection::XcbConnection() { + if (!display()) + return nullptr; + return XGetXCBConnection(display()); +} + +Connection::Request::Request(unsigned int sequence, + FutureBase::ResponseCallback callback) + : sequence(sequence), callback(std::move(callback)) {} + +Connection::Request::Request(Request&& other) + : sequence(other.sequence), callback(std::move(other.callback)) {} + +Connection::Request::~Request() = default; + +bool Connection::HasNextResponse() const { + return !requests_.empty() && + CompareSequenceIds(XLastKnownRequestProcessed(display_), + requests_.front().sequence) >= 0; +} + +int Connection::DefaultScreenId() const { + // This is not part of the setup data as the server has no concept of a + // default screen. Instead, it's part of the display name. Eg in + // "localhost:0.0", the screen ID is the second "0". + return DefaultScreen(display_); +} + +bool Connection::Ready() const { + return display_ && !xcb_connection_has_error(XGetXCBConnection(display_)); +} + +void Connection::Flush() { + XFlush(display_); +} + +void Connection::Sync() { + GetInputFocus({}).Sync(); +} + +void Connection::ReadResponses() { + while (auto* event = xcb_poll_for_event(XcbConnection())) { + events_.emplace_back(event, this); + free(event); + } +} + +bool Connection::HasPendingResponses() const { + return !events_.empty() || HasNextResponse(); +} + +void Connection::Dispatch(Delegate* delegate) { + DCHECK(display_); + + auto process_next_response = [&] { + xcb_connection_t* connection = XGetXCBConnection(display_); + auto request = std::move(requests_.front()); + requests_.pop(); + + void* raw_reply = nullptr; + xcb_generic_error_t* raw_error = nullptr; + xcb_poll_for_reply(connection, request.sequence, &raw_reply, &raw_error); + + std::move(request.callback) + .Run(FutureBase::RawReply{reinterpret_cast<uint8_t*>(raw_reply)}, + FutureBase::RawError{raw_error}); + }; + + auto process_next_event = [&] { + DCHECK(!events_.empty()); + + Event event = std::move(events_.front()); + events_.pop_front(); + PreDispatchEvent(event); + delegate->DispatchXEvent(&event); + }; + + // Handle all pending events. + while (delegate->ShouldContinueStream()) { + Flush(); + ReadResponses(); + + if (HasNextResponse() && !events_.empty()) { + if (!events_.front().sequence_valid()) { + process_next_event(); + continue; + } + + auto next_response_sequence = requests_.front().sequence; + auto next_event_sequence = events_.front().sequence(); + + // All events have the sequence number of the last processed request + // included in them. So if a reply and an event have the same sequence, + // the reply must have been received first. + if (CompareSequenceIds(next_event_sequence, next_response_sequence) <= 0) + process_next_response(); + else + process_next_event(); + } else if (HasNextResponse()) { + process_next_response(); + } else if (!events_.empty()) { + process_next_event(); + } else { + break; + } + } +} + +void Connection::AddRequest(unsigned int sequence, + FutureBase::ResponseCallback callback) { + DCHECK(requests_.empty() || + CompareSequenceIds(requests_.back().sequence, sequence) < 0); + + requests_.emplace(sequence, std::move(callback)); +} + +void Connection::PreDispatchEvent(const Event& event) { + // This is adapted from XRRUpdateConfiguration. + if (auto* configure = event.As<x11::ConfigureNotifyEvent>()) { + int index = ScreenIndexFromRootWindow(configure->window); + if (index != -1) { + setup_.roots[index].width_in_pixels = configure->width; + setup_.roots[index].height_in_pixels = configure->height; + } + } else if (auto* screen = event.As<x11::RandR::ScreenChangeNotifyEvent>()) { + int index = ScreenIndexFromRootWindow(screen->root); + DCHECK_GE(index, 0); + bool portrait = static_cast<bool>( + screen->rotation & + (x11::RandR::Rotation::Rotate_90 | x11::RandR::Rotation::Rotate_270)); + if (portrait) { + setup_.roots[index].width_in_pixels = screen->height; + setup_.roots[index].height_in_pixels = screen->width; + setup_.roots[index].width_in_millimeters = screen->mheight; + setup_.roots[index].height_in_millimeters = screen->mwidth; + } else { + setup_.roots[index].width_in_pixels = screen->width; + setup_.roots[index].height_in_pixels = screen->height; + setup_.roots[index].width_in_millimeters = screen->mwidth; + setup_.roots[index].height_in_millimeters = screen->mheight; + } + } +} + +int Connection::ScreenIndexFromRootWindow(x11::Window root) const { + for (size_t i = 0; i < setup_.roots.size(); i++) { + if (setup_.roots[i].root == root) + return i; + } + return -1; +} } // namespace x11 |