diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/ui/gfx/x | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/gfx/x')
-rw-r--r-- | chromium/ui/gfx/x/BUILD.gn | 117 | ||||
-rw-r--r-- | chromium/ui/gfx/x/connection.cc | 222 | ||||
-rw-r--r-- | chromium/ui/gfx/x/connection.h | 100 | ||||
-rw-r--r-- | chromium/ui/gfx/x/connection_unittest.cc | 108 | ||||
-rw-r--r-- | chromium/ui/gfx/x/event.cc | 103 | ||||
-rw-r--r-- | chromium/ui/gfx/x/event.h | 92 | ||||
-rw-r--r-- | chromium/ui/gfx/x/gen_xproto.py | 1124 | ||||
-rw-r--r-- | chromium/ui/gfx/x/request_queue.cc | 29 | ||||
-rw-r--r-- | chromium/ui/gfx/x/request_queue.h | 51 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11.h | 21 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11_atom_cache.cc | 151 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11_atom_cache.h | 8 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11_error_tracker.cc | 6 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11_path.cc | 29 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11_path.h | 8 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11_types.cc | 6 | ||||
-rw-r--r-- | chromium/ui/gfx/x/x11_types.h | 3 | ||||
-rw-r--r-- | chromium/ui/gfx/x/xproto_internal.h | 104 | ||||
-rw-r--r-- | chromium/ui/gfx/x/xproto_types.cc | 76 | ||||
-rw-r--r-- | chromium/ui/gfx/x/xproto_types.h | 138 | ||||
-rw-r--r-- | chromium/ui/gfx/x/xproto_util.cc | 6 |
21 files changed, 1858 insertions, 644 deletions
diff --git a/chromium/ui/gfx/x/BUILD.gn b/chromium/ui/gfx/x/BUILD.gn index 5369e38efed..24d36ecba8a 100644 --- a/chromium/ui/gfx/x/BUILD.gn +++ b/chromium/ui/gfx/x/BUILD.gn @@ -3,23 +3,12 @@ # found in the LICENSE file. import("//build/config/jumbo.gni") -import("//build/config/sysroot.gni") import("//build/config/ui.gni") import("//ui/ozone/ozone.gni") assert(use_x11 || ozone_platform_x11) -declare_args() { - xcbproto_path = "" -} - -if (xcbproto_path == "") { - if (use_sysroot) { - xcbproto_path = "$sysroot/usr/share/xcb" - } else { - xcbproto_path = "/usr/share/xcb" - } -} +xcbproto_path = "//third_party/xcbproto/src" config("x11_private_config") { cflags = [ @@ -32,54 +21,62 @@ config("x11_private_config") { defines = [ "IS_X11_IMPL" ] } -action_foreach("gen_xprotos") { +action("gen_xprotos") { visibility = [ ":xprotos" ] script = "gen_xproto.py" - sources = [ - "$xcbproto_path/bigreq.xml", - "$xcbproto_path/composite.xml", - "$xcbproto_path/damage.xml", - "$xcbproto_path/dpms.xml", - "$xcbproto_path/dri2.xml", - "$xcbproto_path/dri3.xml", - "$xcbproto_path/ge.xml", - "$xcbproto_path/glx.xml", - "$xcbproto_path/present.xml", - "$xcbproto_path/randr.xml", - "$xcbproto_path/record.xml", - "$xcbproto_path/render.xml", - "$xcbproto_path/res.xml", - "$xcbproto_path/screensaver.xml", - "$xcbproto_path/shape.xml", - "$xcbproto_path/shm.xml", - "$xcbproto_path/sync.xml", - "$xcbproto_path/xc_misc.xml", - "$xcbproto_path/xevie.xml", - "$xcbproto_path/xf86dri.xml", - "$xcbproto_path/xf86vidmode.xml", - "$xcbproto_path/xfixes.xml", - "$xcbproto_path/xinerama.xml", - "$xcbproto_path/xinput.xml", - "$xcbproto_path/xkb.xml", - "$xcbproto_path/xprint.xml", - "$xcbproto_path/xproto.xml", - "$xcbproto_path/xselinux.xml", - "$xcbproto_path/xtest.xml", - "$xcbproto_path/xv.xml", - "$xcbproto_path/xvmc.xml", + protos = [ + "bigreq", + "composite", + "damage", + "dpms", + "dri2", + "dri3", + "ge", + "glx", + "present", + "randr", + "record", + "render", + "res", + "screensaver", + "shape", + "shm", + "sync", + "xc_misc", + "xevie", + "xf86dri", + "xf86vidmode", + "xfixes", + "xinerama", + "xinput", + "xkb", + "xprint", + "xproto", + "xselinux", + "xtest", + "xv", + "xvmc", ] + sources = [] outputs = [ - "$target_gen_dir/{{source_name_part}}_undef.h", - "$target_gen_dir/{{source_name_part}}.h", - "$target_gen_dir/{{source_name_part}}.cc", + "$target_gen_dir/read_event.cc", + "$target_gen_dir/extension_manager.h", + "$target_gen_dir/extension_manager.cc", ] - args = [ "{{source}}" ] + rebase_path(outputs, root_build_dir) - if (use_sysroot) { - args += [ - "--sysroot", - rebase_path(sysroot, root_build_dir), + foreach(proto, protos) { + sources += [ "$xcbproto_path/src/${proto}.xml" ] + outputs += [ + "$target_gen_dir/${proto}_undef.h", + "$target_gen_dir/${proto}.h", + "$target_gen_dir/${proto}.cc", ] } + + args = rebase_path([ + xcbproto_path, + target_gen_dir, + ], + root_build_dir) + protos } component("xprotos") { @@ -90,12 +87,13 @@ component("xprotos") { sources = get_target_outputs(":gen_xprotos") + [ "xproto_internal.h", "xproto_types.h", - "request_queue.h", - "request_queue.cc", + "xproto_types.cc", "xproto_util.h", "xproto_util.cc", "connection.h", "connection.cc", + "event.h", + "event.cc", "x11_switches.cc", "x11_switches.h", ] @@ -131,3 +129,12 @@ jumbo_component("x") { ] public_deps = [ ":xprotos" ] } + +source_set("unit_test") { + testonly = true + sources = [ "connection_unittest.cc" ] + deps = [ + "//testing/gtest", + "//ui/gfx/x", + ] +} 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 diff --git a/chromium/ui/gfx/x/connection.h b/chromium/ui/gfx/x/connection.h index 2e9baf64464..a103b431d71 100644 --- a/chromium/ui/gfx/x/connection.h +++ b/chromium/ui/gfx/x/connection.h @@ -5,25 +5,113 @@ #ifndef UI_GFX_X_CONNECTION_H_ #define UI_GFX_X_CONNECTION_H_ +#include <list> +#include <queue> + #include "base/component_export.h" +#include "ui/gfx/x/event.h" +#include "ui/gfx/x/extension_manager.h" #include "ui/gfx/x/xproto.h" namespace x11 { -using Atom = XProto::Atom; -using Window = XProto::Window; - // Represents a socket to the X11 server. -class COMPONENT_EXPORT(X11) Connection : public XProto { +class COMPONENT_EXPORT(X11) Connection : public XProto, + public ExtensionManager { public: - // Gets or creates the singeton connection. + class Delegate { + public: + virtual bool ShouldContinueStream() const = 0; + virtual void DispatchXEvent(x11::Event* event) = 0; + + protected: + virtual ~Delegate() = default; + }; + + // Gets or creates the singleton connection. static Connection* Get(); + explicit Connection(); + ~Connection(); + Connection(const Connection&) = delete; Connection(Connection&&) = delete; + XDisplay* display() const { return display_; } + xcb_connection_t* XcbConnection(); + + uint32_t extended_max_request_length() const { + return extended_max_request_length_; + } + + const Setup& setup() const { return setup_; } + const Screen& default_screen() const { return *default_screen_; } + x11::Window default_root() const { return default_screen().root; } + const Depth& default_root_depth() const { return *default_root_depth_; } + const VisualType& default_root_visual() const { + return *default_root_visual_; + } + + int DefaultScreenId() const; + + template <typename T> + T GenerateId() { + return static_cast<T>(xcb_generate_id(XcbConnection())); + } + + // Is the connection up and error-free? + bool Ready() const; + + // Write all requests to the socket. + void Flush(); + + // Flush and block until the server has responded to all requests. + void Sync(); + + // Read all responses from the socket without blocking. + void ReadResponses(); + + // Are there any events, errors, or replies already buffered? + bool HasPendingResponses() const; + + // Dispatch any buffered events, errors, or replies. + void Dispatch(Delegate* delegate); + + // Access the event buffer. Clients can add, delete, or modify events. + std::list<Event>& events() { return events_; } + private: - explicit Connection(XDisplay* display); + friend class FutureBase; + + struct Request { + Request(unsigned int sequence, FutureBase::ResponseCallback callback); + Request(Request&& other); + ~Request(); + + const unsigned int sequence; + FutureBase::ResponseCallback callback; + }; + + void AddRequest(unsigned int sequence, FutureBase::ResponseCallback callback); + + bool HasNextResponse() const; + + void PreDispatchEvent(const Event& event); + + int ScreenIndexFromRootWindow(x11::Window root) const; + + XDisplay* const display_; + + uint32_t extended_max_request_length_ = 0; + + Setup setup_; + Screen* default_screen_ = nullptr; + Depth* default_root_depth_ = nullptr; + VisualType* default_root_visual_ = nullptr; + + std::list<Event> events_; + + std::queue<Request> requests_; }; } // namespace x11 diff --git a/chromium/ui/gfx/x/connection_unittest.cc b/chromium/ui/gfx/x/connection_unittest.cc new file mode 100644 index 00000000000..de2285911a4 --- /dev/null +++ b/chromium/ui/gfx/x/connection_unittest.cc @@ -0,0 +1,108 @@ +// Copyright 2020 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 "ui/gfx/x/connection.h" +#include "ui/gfx/x/xproto.h" + +#undef Bool + +#include <xcb/xcb.h> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace x11 { + +namespace { + +Window CreateWindow(Connection* connection) { + Window window = connection->GenerateId<Window>(); + auto create_window_future = connection->CreateWindow({ + .depth = connection->default_root_depth().depth, + .wid = window, + .parent = connection->default_screen().root, + .width = 1, + .height = 1, + .override_redirect = Bool32(true), + }); + auto create_window_response = create_window_future.Sync(); + EXPECT_FALSE(create_window_response.error); + return window; +} + +} // namespace + +// Connection setup and teardown. +TEST(X11ConnectionTest, Basic) { + Connection connection; + ASSERT_TRUE(connection.XcbConnection()); + EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection())); +} + +TEST(X11ConnectionTest, Request) { + Connection connection; + ASSERT_TRUE(connection.XcbConnection()); + EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection())); + + Window window = CreateWindow(&connection); + + auto attributes = connection.GetWindowAttributes({window}).Sync(); + ASSERT_TRUE(attributes); + EXPECT_EQ(attributes->map_state, MapState::Unmapped); + EXPECT_TRUE(attributes->override_redirect); + + auto geometry = connection.GetGeometry({window}).Sync(); + ASSERT_TRUE(geometry); + EXPECT_EQ(geometry->x, 0); + EXPECT_EQ(geometry->y, 0); + EXPECT_EQ(geometry->width, 1u); + EXPECT_EQ(geometry->height, 1u); +} + +TEST(X11ConnectionTest, Event) { + Connection connection; + ASSERT_TRUE(connection.XcbConnection()); + EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection())); + + Window window = CreateWindow(&connection); + + auto cwa_future = connection.ChangeWindowAttributes({ + .window = window, + .event_mask = EventMask::PropertyChange, + }); + EXPECT_FALSE(cwa_future.Sync().error); + + auto prop_future = connection.ChangeProperty({ + .window = static_cast<x11::Window>(window), + .property = x11::Atom::WM_NAME, + .type = x11::Atom::STRING, + .format = CHAR_BIT, + .data_len = 1, + .data = std::vector<uint8_t>{0}, + }); + EXPECT_FALSE(prop_future.Sync().error); + + connection.ReadResponses(); + ASSERT_EQ(connection.events().size(), 1u); + XEvent& event = connection.events().front().xlib_event(); + auto property_notify_opcode = PropertyNotifyEvent::opcode; + EXPECT_EQ(event.type, property_notify_opcode); + EXPECT_EQ(event.xproperty.atom, static_cast<uint32_t>(x11::Atom::WM_NAME)); + EXPECT_EQ(event.xproperty.state, static_cast<int>(Property::NewValue)); +} + +TEST(X11ConnectionTest, Error) { + Connection connection; + ASSERT_TRUE(connection.XcbConnection()); + EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection())); + + Window invalid_window = connection.GenerateId<Window>(); + + auto geometry = connection.GetGeometry({invalid_window}).Sync(); + ASSERT_FALSE(geometry); + xcb_generic_error_t* error = geometry.error.get(); + EXPECT_EQ(error->error_code, XCB_DRAWABLE); + EXPECT_EQ(error->resource_id, static_cast<uint32_t>(invalid_window)); +} + +} // namespace x11 diff --git a/chromium/ui/gfx/x/event.cc b/chromium/ui/gfx/x/event.cc new file mode 100644 index 00000000000..238e06bb656 --- /dev/null +++ b/chromium/ui/gfx/x/event.cc @@ -0,0 +1,103 @@ +// Copyright 2020 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 "ui/gfx/x/event.h" + +#include <X11/Xlibint.h> +#include <X11/extensions/XInput2.h> + +// Xlibint.h defines those as macros, which breaks the C++ versions in +// the std namespace. +#undef max +#undef min + +#include <cstring> + +#include "ui/gfx/x/connection.h" + +namespace x11 { + +Event::Event() = default; + +Event::Event(xcb_generic_event_t* xcb_event, + x11::Connection* connection, + bool sequence_valid) { + XDisplay* display = connection->display(); + + sequence_valid_ = sequence_valid; + sequence_ = xcb_event->full_sequence; + // KeymapNotify events are the only events that don't have a sequence. + if ((xcb_event->response_type & ~kSendEventMask) != + x11::KeymapNotifyEvent::opcode) { + // Rewrite the sequence to the last seen sequence so that Xlib doesn't + // think the sequence wrapped around. + xcb_event->sequence = XLastKnownRequestProcessed(display); + + // On the wire, events are 32 bytes except for generic events which are + // trailed by additional data. XCB inserts an extended 4-byte sequence + // between the 32-byte event and the additional data, so we need to shift + // the additional data over by 4 bytes so the event is back in its wire + // format, which is what Xlib and XProto are expecting. + if ((xcb_event->response_type & ~kSendEventMask) == + x11::GeGenericEvent::opcode) { + auto* ge = reinterpret_cast<xcb_ge_event_t*>(xcb_event); + memmove(&ge->full_sequence, &ge[1], ge->length * 4); + } + } + + // Xlib sometimes modifies |xcb_event|, so let it handle the event after + // we parse it with ReadEvent(). + ReadEvent(this, connection, reinterpret_cast<uint8_t*>(xcb_event)); + + _XEnq(display, reinterpret_cast<xEvent*>(xcb_event)); + if (!XEventsQueued(display, QueuedAlready)) { + // If Xlib gets an event it doesn't recognize (eg. from an + // extension it doesn't know about), it won't add the event to the + // queue. In this case, zero-out the event data. This will set + // the event type to 0, which does not correspond to any event. + // This is safe because event handlers should always check the + // event type before downcasting to a concrete event. + memset(&xlib_event_, 0, sizeof(xlib_event_)); + return; + } + XNextEvent(display, &xlib_event_); + if (xlib_event_.type == x11::GeGenericEvent::opcode) + XGetEventData(display, &xlib_event_.xcookie); +} + +Event::Event(Event&& event) { + memcpy(this, &event, sizeof(Event)); + memset(&event, 0, sizeof(Event)); +} + +Event& Event::operator=(Event&& event) { + Dealloc(); + memcpy(this, &event, sizeof(Event)); + memset(&event, 0, sizeof(Event)); + return *this; +} + +Event::~Event() { + Dealloc(); +} + +void Event::Dealloc() { + if (xlib_event_.type == x11::GeGenericEvent::opcode && + xlib_event_.xcookie.data) { + if (custom_allocated_xlib_event_) { + XIDeviceEvent* xiev = + static_cast<XIDeviceEvent*>(xlib_event_.xcookie.data); + delete[] xiev->valuators.mask; + delete[] xiev->valuators.values; + delete[] xiev->buttons.mask; + delete xiev; + } else { + XFreeEventData(xlib_event_.xcookie.display, &xlib_event_.xcookie); + } + } + if (deleter_) + deleter_(event_); +} + +} // namespace x11 diff --git a/chromium/ui/gfx/x/event.h b/chromium/ui/gfx/x/event.h new file mode 100644 index 00000000000..37073dbd584 --- /dev/null +++ b/chromium/ui/gfx/x/event.h @@ -0,0 +1,92 @@ +// Copyright 2020 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 UI_GFX_X_EVENT_H_ +#define UI_GFX_X_EVENT_H_ + +#include <X11/Xlib.h> +#include <xcb/xcb.h> + +#include <cstdint> +#include <utility> + +#include "base/component_export.h" + +namespace x11 { + +class Connection; +class Event; + +COMPONENT_EXPORT(X11) +void ReadEvent(Event* event, Connection* connection, const uint8_t* buffer); + +class COMPONENT_EXPORT(X11) Event { + public: + // Used to create events for testing. + template <typename T> + Event(XEvent* xlib_event, T&& xproto_event) { + sequence_valid_ = true; + sequence_ = xlib_event_.xany.serial; + custom_allocated_xlib_event_ = true; + xlib_event_ = *xlib_event; + type_id_ = T::type_id; + deleter_ = [](void* event) { delete reinterpret_cast<T*>(event); }; + event_ = new T(std::forward<T>(xproto_event)); + } + + Event(); + Event(xcb_generic_event_t* xcb_event, + Connection* connection, + bool sequence_valid = true); + + Event(const Event&) = delete; + Event& operator=(const Event&) = delete; + + Event(Event&& event); + Event& operator=(Event&& event); + + ~Event(); + + template <typename T> + T* As() { + if (type_id_ == T::type_id) + return reinterpret_cast<T*>(event_); + return nullptr; + } + + template <typename T> + const T* As() const { + return const_cast<Event*>(this)->As<T>(); + } + + bool sequence_valid() const { return sequence_valid_; } + uint32_t sequence() const { return sequence_; } + + const XEvent& xlib_event() const { return xlib_event_; } + XEvent& xlib_event() { return xlib_event_; } + + private: + friend void ReadEvent(Event* event, + Connection* connection, + const uint8_t* buffer); + + void Dealloc(); + + bool sequence_valid_ = false; + uint32_t sequence_ = 0; + + // Indicates if |xlib_event_| was allocated manually and therefore + // needs to be freed manually. + bool custom_allocated_xlib_event_ = false; + XEvent xlib_event_{}; + + // XProto event state. + int type_id_ = 0; + void (*deleter_)(void*) = nullptr; + void* event_ = nullptr; +}; + +} // namespace x11 + +#endif // UI_GFX_X_EVENT_H_ diff --git a/chromium/ui/gfx/x/gen_xproto.py b/chromium/ui/gfx/x/gen_xproto.py index 4a34901012d..24a2efcc245 100644 --- a/chromium/ui/gfx/x/gen_xproto.py +++ b/chromium/ui/gfx/x/gen_xproto.py @@ -23,15 +23,15 @@ # #include "base/component_export.h" # #include "ui/gfx/x/xproto_types.h" # -# typedef struct _XDisplay XDisplay; -# # namespace x11 { # +# class Connection; +# # class COMPONENT_EXPORT(X11) XProto { # public: -# explicit XProto(XDisplay* display); +# explicit XProto(Connection* connection); # -# XDisplay* display() { return display_; } +# Connection* connection() const { return connection_; } # # struct RGB { # uint16_t red{}; @@ -54,7 +54,7 @@ # Future<QueryColorsReply> QueryColors(const QueryColorsRequest& request); # # private: -# XDisplay* display_; +# Connection* const connection_; # }; # # } // namespace x11 @@ -66,12 +66,13 @@ # #include <xcb/xcb.h> # #include <xcb/xcbext.h> # -# #include "base/logging.h" +# #include "base/notreached.h" +# #include "base/check_op.h" # #include "ui/gfx/x/xproto_internal.h" # # namespace x11 { # -# XProto::XProto(XDisplay* display) : display_(display) {} +# XProto::XProto(Connection* connection) : connection_(connection) {} # # Future<XProto::QueryColorsReply> # XProto::QueryColors( @@ -102,7 +103,7 @@ # Write(&pixels_elem, &buf); # } # -# return x11::SendRequest<XProto::QueryColorsReply>(display_, &buf); +# return x11::SendRequest<XProto::QueryColorsReply>(connection_, &buf); # } # # template<> COMPONENT_EXPORT(X11) @@ -168,6 +169,8 @@ from __future__ import print_function import argparse import collections +import functools +import itertools import os import re import sys @@ -177,61 +180,106 @@ import types # so this global is unavoidable. output = collections.defaultdict(int) -UPPER_CASE_PATTERN = re.compile(r'^[A-Z0-9_]+$') - - -def adjust_type_case(name): - if UPPER_CASE_PATTERN.match(name): - SPECIAL = { - 'ANIMCURSORELT': 'AnimationCursorElement', - 'CA': 'ChangeAlarmAttribute', - 'CHAR2B': 'Char16', - 'CHARINFO': 'CharInfo', - 'COLORITEM': 'ColorItem', - 'COLORMAP': 'ColorMap', - 'CP': 'CreatePictureAttribute', - 'CW': 'CreateWindowAttribute', - 'DAMAGE': 'DamageId', - 'DIRECTFORMAT': 'DirectFormat', - 'DOTCLOCK': 'DotClock', - 'FBCONFIG': 'FbConfig', - 'FLOAT32': 'float', - 'FLOAT64': 'double', - 'FONTPROP': 'FontProperty', - 'GC': 'GraphicsContextAttribute', - 'GCONTEXT': 'GraphicsContext', - 'GLYPHINFO': 'GlyphInfo', - 'GLYPHSET': 'GlyphSet', - 'INDEXVALUE': 'IndexValue', - 'KB': 'Keyboard', - 'KEYCODE': 'KeyCode', - 'KEYCODE32': 'KeyCode32', - 'KEYSYM': 'KeySym', - 'LINEFIX': 'LineFix', - 'OP': 'Operation', - 'PBUFFER': 'PBuffer', - 'PCONTEXT': 'PContext', - 'PICTDEPTH': 'PictDepth', - 'PICTFORMAT': 'PictFormat', - 'PICTFORMINFO': 'PictFormInfo', - 'PICTSCREEN': 'PictScreen', - 'PICTVISUAL': 'PictVisual', - 'POINTFIX': 'PointFix', - 'SEGMENT': 'SEGMENT', - 'SPANFIX': 'SpanFix', - 'SUBPICTURE': 'SubPicture', - 'SYSTEMCOUNTER': 'SystemCounter', - 'TIMECOORD': 'TimeCoord', - 'TIMESTAMP': 'TimeStamp', - 'VISUALID': 'VisualId', - 'VISUALTYPE': 'VisualType', - 'WAITCONDITION': 'WaitCondition', - } - if name in SPECIAL: - return SPECIAL[name] +RENAME = { + 'ANIMCURSORELT': 'AnimationCursorElement', + 'CA': 'ChangeAlarmAttribute', + 'CHAR2B': 'Char16', + 'CHARINFO': 'CharInfo', + 'COLORITEM': 'ColorItem', + 'COLORMAP': 'ColorMap', + 'Connection': 'RandRConnection', + 'CP': 'CreatePictureAttribute', + 'CW': 'CreateWindowAttribute', + 'DAMAGE': 'DamageId', + 'DIRECTFORMAT': 'DirectFormat', + 'DOTCLOCK': 'DotClock', + 'FBCONFIG': 'FbConfig', + 'FLOAT32': 'float', + 'FLOAT64': 'double', + 'FONTPROP': 'FontProperty', + 'GC': 'GraphicsContextAttribute', + 'GCONTEXT': 'GraphicsContext', + 'GLYPHINFO': 'GlyphInfo', + 'GLYPHSET': 'GlyphSet', + 'INDEXVALUE': 'IndexValue', + 'KB': 'Keyboard', + 'KEYCODE': 'KeyCode', + 'KEYCODE32': 'KeyCode32', + 'KEYSYM': 'KeySym', + 'LINEFIX': 'LineFix', + 'OP': 'Operation', + 'PBUFFER': 'PBuffer', + 'PCONTEXT': 'PContext', + 'PICTDEPTH': 'PictDepth', + 'PICTFORMAT': 'PictFormat', + 'PICTFORMINFO': 'PictFormInfo', + 'PICTSCREEN': 'PictScreen', + 'PICTVISUAL': 'PictVisual', + 'POINTFIX': 'PointFix', + 'SPANFIX': 'SpanFix', + 'SUBPICTURE': 'SubPicture', + 'SYSTEMCOUNTER': 'SystemCounter', + 'TIMECOORD': 'TimeCoord', + 'TIMESTAMP': 'Time', + 'VISUALID': 'VisualId', + 'VISUALTYPE': 'VisualType', + 'WAITCONDITION': 'WaitCondition', +} + +READ_SPECIAL = set([ + ('xcb', 'Setup'), +]) + +WRITE_SPECIAL = set([ + ('xcb', 'ClientMessage'), + ('xcb', 'UnmapNotify'), + ('xcb', 'SelectionNotify'), +]) + + +def adjust_type_name(name): + if name in RENAME: + return RENAME[name] + # If there's an underscore, then this is either snake case or upper case. + if '_' in name: return ''.join([ token[0].upper() + token[1:].lower() for token in name.split('_') ]) + if name.isupper(): + name = name.lower() + # Now the only possibilities are caml case and pascal case. It could also + # be snake case with a single word, but that would be same as caml case. + # To convert all of these, just capitalize the first letter. + return name[0].upper() + name[1:] + + +# Given a list of event names like ["KeyPress", "KeyRelease"], returns a name +# suitable for use as a base event like "Key". +def event_base_name(names): + # If there's only one event in this group, the "common name" is just + # the event name. + if len(names) == 1: + return names[0] + + # Handle a few special cases where the longest common prefix is empty: eg. + # EnterNotify/LeaveNotify/FocusIn/FocusOut -> Crossing. + EVENT_NAMES = [ + ('TouchBegin', 'Device'), + ('RawTouchBegin', 'RawDevice'), + ('Enter', 'Crossing'), + ('EnterNotify', 'Crossing'), + ('DeviceButtonPress', 'LegacyDevice'), + ] + for name, rename in EVENT_NAMES: + if name in names: + return rename + + # Use the longest common prefix of the event names as the base name. + name = ''.join( + chars[0] + for chars in itertools.takewhile(lambda chars: len(set(chars)) == 1, + zip(*names))) + assert name return name @@ -251,17 +299,6 @@ class Indent: self.xproto.write(self.closing_line) -class NullContext: - def __init__(self): - pass - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_value, exc_traceback): - pass - - # Make all members of |obj|, given by |fields|, visible in # the local scope while this class is alive. class ScopedFields: @@ -302,20 +339,37 @@ def safe_name(name): return name -class GenXproto: - def __init__(self, args, xcbgen): +class FileWriter: + def __init__(self): + self.indent = 0 + + # Write a line to the current file. + def write(self, line=''): + indent = self.indent if line and not line.startswith('#') else 0 + print((' ' * indent) + line, file=self.file) + + +class GenXproto(FileWriter): + def __init__(self, proto, proto_dir, gen_dir, xcbgen, all_types): + FileWriter.__init__(self) + # Command line arguments - self.args = args + self.proto = proto + self.xml_filename = os.path.join(proto_dir, '%s.xml' % proto) + self.header_file = open(os.path.join(gen_dir, '%s.h' % proto), 'w') + self.source_file = open(os.path.join(gen_dir, '%s.cc' % proto), 'w') + self.undef_file = open(os.path.join(gen_dir, '%s_undef.h' % proto), + 'w') # Top-level xcbgen python module self.xcbgen = xcbgen + # Types for every module including this one + self.all_types = all_types + # The last used UID for making unique names self.prev_id = -1 - # Current indentation level - self.indent = 0 - # Current file to write to self.file = None @@ -331,7 +385,7 @@ class GenXproto: # Map from type names to a set of types. Certain types # like enums and simple types can alias each other. - self.types = collections.defaultdict(set) + self.types = collections.defaultdict(list) # Set of names of simple types to be replaced with enums self.replace_with_enum = set() @@ -339,10 +393,11 @@ class GenXproto: # Map of enums to their underlying types self.enum_types = collections.defaultdict(set) - # Write a line to the current file. - def write(self, line=''): - indent = self.indent if line and not line.startswith('#') else 0 - print((' ' * indent) + line, file=self.file) + # Map from (XML tag, XML name) to XML element + self.module_names = {} + + # Enums that represent bit masks. + self.bitenums = [] # Geenerate an ID suitable for use in temporary variable names. def new_uid(self, ): @@ -361,57 +416,70 @@ class GenXproto: return '' def rename_type(self, t, name): - name = list(name) - for i in range(1, len(name)): - name[i] = adjust_type_case(name[i]) - name[-1] += self.type_suffix(t) - return name - - # Given an xcbgen.xtypes.Type, returns a C++-namespace-qualified - # string that looks like Input::InputClass::Key. - def qualtype(self, t, name): # Work around a bug in xcbgen: ('int') should have been ('int',) if name == 'int': name = ('int', ) - name = self.rename_type(t, name) + name = list(name) if name[0] == 'xcb': # Use namespace x11 instead of xcb. name[0] = 'x11' - # We want the non-extension X11 structures to live in a class too. - if len(name) == 2: - name[1:1] = ['XProto'] + for i in range(1, len(name)): + name[i] = adjust_type_name(name[i]) + name[-1] += self.type_suffix(t) + return name + + # Given an unqualified |name| like ('Window') and a namespace like ['x11'], + # returns a fully qualified name like ('x11', 'Window'). + def qualify_type(self, name, namespace): + if tuple(namespace + name) in self.all_types: + return namespace + name + return self.qualify_type(name, namespace[:-1]) + + # Given an xcbgen.xtypes.Type, returns a C++-namespace-qualified + # string that looks like Input::InputClass::Key. + def qualtype(self, t, name): + name = self.rename_type(t, name) # Try to avoid adding namespace qualifiers if they're not necessary. chop = 0 for t1, t2 in zip(name, self.namespace): if t1 != t2: break + if self.qualify_type(name[chop + 1:], self.namespace) != name: + break chop += 1 return '::'.join(name[chop:]) def fieldtype(self, field): - return self.qualtype(field.type, field.field_type) + return self.qualtype(field.type, + field.enum if field.enum else field.field_type) + + def switch_fields(self, switch): + fields = [] + for case in switch.bitcases: + if case.field_name: + fields.append(case) + else: + fields.extend(case.type.fields) + return fields def add_field_to_scope(self, field, obj): - if not field.visible or not field.wire: + if not field.visible or (not field.wire and not field.isfd): + return 0 + + field_name = safe_name(field.field_name) + + if field.type.is_switch: + self.write('auto& %s = %s;' % (field_name, obj)) return 0 self.scope.append(field) - field_name = safe_name(field.field_name) - # There's one case where we would have generated: - # auto& enable = enable.enable; - # To prevent a compiler error from trying to use the variable - # in its own definition, save to a temporary variable first. - if field_name == obj: - tmp_id = self.new_uid() - self.write('auto& tmp%d = %s.%s;' % (tmp_id, obj, field_name)) - self.write('auto& %s = tmp%d;' % (field_name, tmp_id)) - elif field.for_list: - self.write('%s %s;' % (self.fieldtype(field), field_name)) + if field.for_list or field.for_switch: + self.write('%s %s{};' % (self.fieldtype(field), field_name)) else: self.write('auto& %s = %s.%s;' % (field_name, obj, field_name)) @@ -432,9 +500,9 @@ class GenXproto: # Work around conflicts caused by Xlib's liberal use of macros. def undef(self, name): - print('#ifdef %s' % name, file=self.args.undeffile) - print('#undef %s' % name, file=self.args.undeffile) - print('#endif', file=self.args.undeffile) + print('#ifdef %s' % name, file=self.undef_file) + print('#undef %s' % name, file=self.undef_file) + print('#endif', file=self.undef_file) def expr(self, expr): if expr.op == 'popcount': @@ -476,16 +544,53 @@ class GenXproto: assert expr.lenfield_name return expr.lenfield_name + def get_xidunion_element(self, name): + key = ('xidunion', name[-1]) + return self.module_names.get(key, None) + + def declare_xidunion(self, xidunion, xidname): + names = [type_element.text for type_element in xidunion] + types = list(set([self.module.get_type(name) for name in names])) + assert len(types) == 1 + value_type = types[0] + value_typename = self.qualtype(value_type, value_type.name) + with Indent(self, 'struct %s {' % xidname, '};'): + self.write('%s() : value{} {}' % xidname) + self.write() + for name in names: + cpp_name = self.module.get_type_name(name) + typename = self.qualtype(value_type, cpp_name) + self.write('%s(%s value) : value{static_cast<%s>(value)} {}' % + (xidname, typename, value_typename)) + self.write( + 'operator %s() const { return static_cast<%s>(value); }' % + (typename, typename)) + self.write() + self.write('%s value{};' % value_typename) + def declare_simple(self, item, name): # The underlying type of an enum must be integral, so avoid defining # FLOAT32 or FLOAT64. Usages are renamed to float and double instead. renamed = tuple(self.rename_type(item, name)) - if name[-1] not in ('FLOAT32', 'FLOAT64' - ) and renamed not in self.replace_with_enum: - self.write( - 'enum class %s : %s {};' % - (adjust_type_case(name[-1]), self.qualtype(item, item.name))) + if (name[-1] in ('FLOAT32', 'FLOAT64') + or renamed in self.replace_with_enum): + return + elif name[-1] == 'FP1616': + # Xcbproto defines FP1616 as uint32_t instead of a struct of + # two 16-bit ints, which is how it's intended to be used. + with Indent(self, 'struct Fp1616 {', '};'): + self.write('int16_t integral;') + self.write('uint16_t frac;') self.write() + return + + xidunion = self.get_xidunion_element(name) + if xidunion: + self.declare_xidunion(xidunion, renamed[-1]) + else: + self.write('enum class %s : %s {};' % + (renamed[-1], self.qualtype(item, item.name))) + self.write() def copy_primitive(self, name): self.write('%s(&%s, &buf);' % @@ -495,28 +600,36 @@ class GenXproto: type_name = self.fieldtype(field) name = safe_name(field.field_name) + def copy_basic(): + self.write('%s %s;' % (type_name, name)) + self.copy_primitive(name) + if name in ('major_opcode', 'minor_opcode'): assert not self.is_read - is_ext = any( - [f.field_name == 'minor_opcode' for f in field.parent.fields]) - if is_ext and name == 'major_opcode': - self.write('// Caller fills in extension major opcode.') - self.write('Pad(&buf, sizeof(%s));' % type_name) + is_ext = self.module.namespace.is_ext + self.write( + '%s %s = %s;' % + (type_name, name, 'info_.major_opcode' if is_ext + and name == 'major_opcode' else field.parent[0].opcode)) + self.copy_primitive(name) + elif name == 'response_type': + if self.is_read: + copy_basic() else: - self.write('%s %s = %s;' % - (type_name, name, field.parent.opcode)) + container_type, container_name = field.parent + assert container_type.is_event + opcode = container_type.opcodes[container_name] + self.write('%s %s = %s;' % (type_name, name, opcode)) self.copy_primitive(name) - elif name in ('response_type', 'sequence', 'extension'): + elif name in ('extension', 'error_code', 'event_type'): assert self.is_read - self.write('%s %s;' % (type_name, name)) - self.copy_primitive(name) + copy_basic() elif name == 'length': if not self.is_read: self.write('// Caller fills in length for writes.') self.write('Pad(&buf, sizeof(%s));' % type_name) else: - self.write('%s %s;' % (type_name, name)) - self.copy_primitive(name) + copy_basic() else: assert field.type.is_expr assert (not isinstance(field.type, self.xcbgen.xtypes.Enum)) @@ -527,48 +640,52 @@ class GenXproto: def declare_case(self, case): assert case.type.is_case != case.type.is_bitcase - with (Indent(self, 'struct {', '} %s;' % safe_name(case.field_name)) - if case.field_name else NullContext()): - for case_field in case.type.fields: - self.declare_field(case_field) - - def copy_case(self, case, switch_var): - op = 'CaseEq' if case.type.is_case else 'BitAnd' + fields = [ + field for case_field in case.type.fields + for field in self.declare_field(case_field) + ] + if not case.field_name: + return fields + name = safe_name(case.field_name) + with Indent(self, 'struct %s_t {' % name, '};'): + for field in fields: + self.write('%s %s{};' % field) + return [(name + '_t', name)] + + def copy_case(self, case, switch_name): + op = 'CaseEq' if case.type.is_case else 'CaseAnd' condition = ' || '.join([ - '%s(%s, %s)' % (op, switch_var, self.expr(expr)) + '%s(%s_expr, %s)' % (op, switch_name, self.expr(expr)) for expr in case.type.expr ]) with Indent(self, 'if (%s) {' % condition, '}'): - with (ScopedFields(self, case.field_name, case.type.fields) - if case.field_name else NullContext()): + if case.field_name: + fields = [case] + obj = '(*%s.%s)' % (switch_name, safe_name(case.field_name)) + else: + fields = case.type.fields + obj = '*' + switch_name + for case_field in fields: + name = safe_name(case_field.field_name) + if case_field.visible and self.is_read: + self.write('%s.%s.emplace();' % (switch_name, name)) + with ScopedFields(self, obj, case.type.fields): for case_field in case.type.fields: - assert case_field.wire self.copy_field(case_field) def declare_switch(self, field): - t = field.type - name = safe_name(field.field_name) - - with Indent(self, 'struct {', '} %s;' % name): - for case in t.bitcases: - self.declare_case(case) + return [('base::Optional<%s>' % field_type, field_name) + for case in field.type.bitcases + for field_type, field_name in self.declare_case(case)] def copy_switch(self, field): t = field.type name = safe_name(field.field_name) - scope_fields = [] + self.write('auto %s_expr = %s;' % (name, self.expr(t.expr))) for case in t.bitcases: - if case.field_name: - scope_fields.append(case) - else: - scope_fields.extend(case.type.fields) - with Indent(self, '{', '}'), ScopedFields(self, name, scope_fields): - switch_var = name + '_expr' - self.write('auto %s = %s;' % (switch_var, self.expr(t.expr))) - for case in t.bitcases: - self.copy_case(case, switch_var) + self.copy_case(case, name) def declare_list(self, field): t = field.type @@ -588,7 +705,7 @@ class GenXproto: type_name = 'std::string' else: type_name = 'std::vector<%s>' % type_name - self.write('%s %s{};' % (type_name, name)) + return [(type_name, name)] def copy_list(self, field): t = field.type @@ -604,35 +721,60 @@ class GenXproto: with Indent(self, 'for (auto& %s_elem : %s) {' % (name, name), '}'): elem_name = name + '_elem' elem_type = t.member - if elem_type.is_simple or elem_type.is_union: - assert (not isinstance(elem_type, self.xcbgen.xtypes.Enum)) - self.copy_primitive(elem_name) - else: - assert elem_type.is_container - self.copy_container(elem_type, elem_name) + elem_field = self.xcbgen.expr.Field(elem_type, field.field_type, + elem_name, field.visible, + field.wire, field.auto, + field.enum, field.isfd) + elem_field.for_list = None + elem_field.for_switch = None + self.copy_field(elem_field) + + def generate_switch_var(self, field): + name = safe_name(field.field_name) + for case in field.for_switch.type.bitcases: + case_field = case if case.field_name else case.type.fields[0] + self.write('SwitchVar(%s, %s.%s.has_value(), %s, &%s);' % + (self.expr(case.type.expr[0]), + safe_name(field.for_switch.field_name), + safe_name(case_field.field_name), + 'true' if case.type.is_bitcase else 'false', name)) def declare_field(self, field): t = field.type name = safe_name(field.field_name) - if not field.wire or not field.visible or field.for_list: - return + if not field.visible or field.for_list or field.for_switch: + return [] if t.is_switch: - self.declare_switch(field) - elif t.is_list: - self.declare_list(field) - else: - self.write( - '%s %s{};' % - (self.qualtype(field.type, field.enum - if field.enum else field.field_type), name)) + return self.declare_switch(field) + if t.is_list: + return self.declare_list(field) + return [(self.fieldtype(field), name)] def copy_field(self, field): + if not field.wire and not field.isfd: + return + t = field.type + renamed = tuple(self.rename_type(field.type, field.field_type)) + if t.is_list: + t.member = self.all_types.get(renamed, t.member) + else: + t = self.all_types.get(renamed, t) name = safe_name(field.field_name) self.write('// ' + name) + + # If this is a generated field, initialize the value of the field + # variable from the given context. + if not self.is_read: + if field.for_list: + self.write('%s = %s.size();' % + (name, safe_name(field.for_list.field_name))) + if field.for_switch: + self.generate_switch_var(field) + if t.is_pad: if t.align > 1: assert t.nmemb == 1 @@ -642,11 +784,6 @@ class GenXproto: self.write('Pad(&buf, %d);' % t.nmemb) elif not field.visible: self.copy_special_field(field) - elif field.for_list: - if not self.is_read: - self.write('%s = %s.size();' % - (name, safe_name(field.for_list.field_name))) - self.copy_primitive(name) elif t.is_switch: self.copy_switch(field) elif t.is_list: @@ -656,6 +793,9 @@ class GenXproto: elif t.is_container: with Indent(self, '{', '}'): self.copy_container(t, name) + elif t.is_fd: + # TODO(https://crbug.com/1066670): Copy FDs out of band. + self.write('NOTIMPLEMENTED();') else: assert t.is_simple if field.enum: @@ -663,6 +803,8 @@ class GenXproto: else: self.copy_primitive(name) + self.write() + def declare_enum(self, enum): def declare_enum_entry(name, value): name = safe_name(name) @@ -672,7 +814,7 @@ class GenXproto: self.undef(enum.name[-1]) with Indent( self, 'enum class %s : %s {' % - (adjust_type_case(enum.name[-1]), self.enum_types[enum.name][0] + (adjust_type_name(enum.name[-1]), self.enum_types[enum.name][0] if enum.name in self.enum_types else 'int'), '};'): bitnames = set([name for name, _ in enum.bits]) for name, value in enum.values: @@ -686,7 +828,7 @@ class GenXproto: # The size of enum types may be different depending on the # context, so they should always be casted to the contextual # underlying type before calling Read() or Write(). - underlying_type = self.fieldtype(field) + underlying_type = self.qualtype(field.type, field.type.name) tmp_name = 'tmp%d' % self.new_uid() real_name = safe_name(field.field_name) self.write('%s %s;' % (underlying_type, tmp_name)) @@ -699,30 +841,84 @@ class GenXproto: self.write('%s = static_cast<%s>(%s);' % (real_name, enum_type, tmp_name)) - def declare_container(self, struct): - name = struct.name[-1] + self.type_suffix(struct) + def declare_fields(self, fields): + for field in fields: + for field_type_name in self.declare_field(field): + self.write('%s %s{};' % field_type_name) + + def declare_event(self, event, name): + event_name = name[-1] + 'Event' + self.undef(event_name) + with Indent(self, 'struct %s {' % adjust_type_name(event_name), '};'): + self.write('static constexpr int type_id = %d;' % event.type_id) + if len(event.opcodes) == 1: + self.write('static constexpr uint8_t opcode = %s;' % + event.opcodes[name]) + else: + with Indent(self, 'enum Opcode {', '} opcode{};'): + items = [(int(x), y) + for (y, x) in event.enum_opcodes.items()] + for opcode, opname in sorted(items): + self.undef(opname) + self.write('%s = %s,' % (opname, opcode)) + self.write('bool send_event{};') + self.declare_fields(event.fields) + self.write() + + def declare_container(self, struct, struct_name): + name = struct_name[-1] + self.type_suffix(struct) self.undef(name) - with Indent(self, 'struct %s {' % adjust_type_case(name), '};'): - for field in struct.fields: - self.declare_field(field) + with Indent(self, 'struct %s {' % adjust_type_name(name), '};'): + self.declare_fields(struct.fields) self.write() def copy_container(self, struct, name): assert not struct.is_union with ScopedFields(self, name, struct.fields): for field in struct.fields: - if field.wire: - self.copy_field(field) - self.write() + self.copy_field(field) + + def read_special_container(self, struct, name): + self.namespace = ['x11'] + name = self.qualtype(struct, name) + self.write('template <> COMPONENT_EXPORT(X11)') + self.write('%s Read<%s>(' % (name, name)) + with Indent(self, ' const uint8_t* buffer) {', '}'): + self.write('ReadBuffer buf{buffer, 0UL};') + self.write('%s obj;' % name) + self.write() + self.is_read = True + self.copy_container(struct, 'obj') + self.write('return obj;') + self.write() + + def write_special_container(self, struct, name): + self.namespace = ['x11'] + name = self.qualtype(struct, name) + self.write('template <> COMPONENT_EXPORT(X11)') + self.write('std::vector<uint8_t> Write<%s>(' % name) + with Indent(self, ' const %s& obj) {' % name, '}'): + self.write('WriteBuffer buf;') + self.write() + self.is_read = False + self.copy_container(struct, 'obj') + self.write('return buf;') + self.write() def declare_union(self, union): name = union.name[-1] + if union.elt.tag == 'eventstruct': + # There's only one of these in all of the protocol descriptions. + # It's just used to represent any 32-byte event for XInput. + self.write('using %s = std::array<uint8_t, 32>;' % name) + return with Indent(self, 'union %s {' % name, '};'): self.write('%s() { memset(this, 0, sizeof(*this)); }' % name) self.write() for field in union.fields: - type_name = self.fieldtype(field) - self.write('%s %s;' % (type_name, safe_name(field.field_name))) + field_type_names = self.declare_field(field) + assert len(field_type_names) == 1 + self.write('%s %s;' % field_type_names[0]) self.write( 'static_assert(std::is_trivially_copyable<%s>::value, "");' % name) self.write() @@ -730,26 +926,30 @@ class GenXproto: def declare_request(self, request): method_name = request.name[-1] request_name = method_name + 'Request' - reply_name = method_name + 'Reply' + reply_name = method_name + 'Reply' if request.reply else 'void' - self.declare_container(request) - if request.reply: - self.declare_container(request.reply) - else: - reply_name = 'void' + in_class = self.namespace == ['x11', self.class_name] - self.write('using %sResponse = Response<%s>;' % - (method_name, reply_name)) - self.write() + if not in_class or self.module.namespace.is_ext: + self.declare_container(request, request.name) + if request.reply: + self.declare_container(request.reply, request.reply.name) - self.write('Future<%s> %s(' % (reply_name, method_name)) - self.write(' const %s& request);' % request_name) - self.write() + self.write('using %sResponse = Response<%s>;' % + (method_name, reply_name)) + self.write() + + if in_class: + self.write('Future<%s> %s(' % (reply_name, method_name)) + self.write(' const %s& request);' % request_name) + self.write() def define_request(self, request): method_name = '%s::%s' % (self.class_name, request.name[-1]) - request_name = method_name + 'Request' - reply_name = method_name + 'Reply' + prefix = (method_name + if self.module.namespace.is_ext else request.name[-1]) + request_name = prefix + 'Request' + reply_name = prefix + 'Reply' reply = request.reply if not reply: @@ -758,6 +958,12 @@ class GenXproto: self.write('Future<%s>' % reply_name) self.write('%s(' % method_name) with Indent(self, ' const %s& request) {' % request_name, '}'): + cond = '!connection_->Ready()' + if self.module.namespace.is_ext: + cond += ' || !present()' + self.write('if (%s)' % cond) + self.write(' return {};') + self.write() self.namespace = ['x11', self.class_name] self.write('WriteBuffer buf;') self.write() @@ -765,7 +971,7 @@ class GenXproto: self.copy_container(request, 'request') self.write('Align(&buf, 4);') self.write() - self.write('return x11::SendRequest<%s>(display_, &buf);' % + self.write('return x11::SendRequest<%s>(connection_, &buf);' % reply_name) self.write() @@ -789,14 +995,38 @@ class GenXproto: self.write('return reply;') self.write() + def define_event(self, event, name): + self.namespace = ['x11'] + name = self.qualtype(event, name) + self.write('template <> COMPONENT_EXPORT(X11)') + self.write('void ReadEvent<%s>(' % name) + with Indent(self, ' %s* event_, const uint8_t* buffer) {' % name, + '}'): + self.write('ReadBuffer buf{buffer, 0UL};') + self.write() + self.is_read = True + self.copy_container(event, '(*event_)') + self.write() + + def define_type(self, item, name): + if name in READ_SPECIAL: + self.read_special_container(item, name) + if name in WRITE_SPECIAL: + self.write_special_container(item, name) + if isinstance(item, self.xcbgen.xtypes.Request): + self.define_request(item) + elif item.is_event: + self.define_event(item, name) + def declare_type(self, item, name): if item.is_union: self.declare_union(item) elif isinstance(item, self.xcbgen.xtypes.Request): self.declare_request(item) + elif item.is_event: + self.declare_event(item, name) elif item.is_container: - item.name = name - self.declare_container(item) + self.declare_container(item, name) elif isinstance(item, self.xcbgen.xtypes.Enum): self.declare_enum(item) else: @@ -823,72 +1053,141 @@ class GenXproto: if enums: assert len(enums) == 1 enum = enums[0] - field.enum = self.module.get_type(enum).name if enums else None + field.enum = self.module.get_type(enum).name self.enum_types[enum].add(field.type.name) + else: + field.enum = None def resolve_type(self, t, name): renamed = tuple(self.rename_type(t, name)) - if t in self.types[renamed]: - return - self.types[renamed].add(t) + assert renamed[0] == 'x11' + assert t not in self.types[renamed] + self.types[renamed].append(t) + self.all_types[renamed] = t + + if isinstance(t, self.xcbgen.xtypes.Enum): + self.bitenums.append((t, name)) if not t.is_container: return - if t.is_switch: - fields = {} - for case in t.bitcases: - if case.field_name: - fields[case.field_name] = case - else: - for field in case.type.fields: - fields[field.field_name] = field - else: - fields = {field.field_name: field for field in t.fields} + fields = { + field.field_name: field + for field in (self.switch_fields(t) if t.is_switch else t.fields) + } self.resolve_element(t.elt, fields) for field in fields.values(): - field.parent = t - field.for_list = None + if field.field_name == 'sequence': + field.visible = True + field.parent = (t, name) + # |for_list| and |for_switch| may have already been set when + # processing other fields in this structure. + field.for_list = getattr(field, 'for_list', None) + field.for_switch = getattr(field, 'for_switch', None) + + for is_type, for_type in ((field.type.is_list, 'for_list'), + (field.type.is_switch, 'for_switch')): + if not is_type: + continue + expr = field.type.expr + field_name = expr.lenfield_name + if (expr.op in (None, 'calculate_len') + and field_name in fields): + setattr(fields[field_name], for_type, field) + if field.type.is_switch or field.type.is_case_or_bitcase: self.resolve_type(field.type, field.field_type) - elif field.type.is_list: - self.resolve_type(field.type.member, field.type.member.name) - expr = field.type.expr - if not expr.op and expr.lenfield_name in fields: - fields[expr.lenfield_name].for_list = field - else: - self.resolve_type(field.type, field.type.name) if isinstance(t, self.xcbgen.xtypes.Request) and t.reply: self.resolve_type(t.reply, t.reply.name) + # Multiple event names may map to the same underlying event. For these + # cases, we want to avoid duplicating the event structure. Instead, put + # all of these events under one structure with an additional opcode field + # to indicate the type of event. + def uniquify_events(self): + # Manually merge some events in XInput. These groups of 8 events have + # idential structure, and are merged as XIDeviceEvent in Xlib. To avoid + # duplication, and to ease the transition from Xlib to XProto, we merge + # the events here too. + # TODO(thomasanderson): We should avoid adding workarounds for xcbproto. + # Instead, the protocol files should be modified directly. However, + # some of the changes we want to make change the API, so the changes + # should be made in a fork in //third_party rather than upstreamed. + MERGE = [ + ([ + 'KeyPress', 'KeyRelease', 'ButtonPress', 'ButtonRelease', + 'Motion', 'TouchBegin', 'TouchUpdate', 'TouchEnd' + ], []), + ([ + 'RawKeyPress', 'RawKeyRelease', 'RawButtonPress', + 'RawButtonRelease', 'RawMotion', 'RawTouchBegin', + 'RawTouchUpdate', 'RawTouchEnd' + ], []), + ] + for i, (name, t) in enumerate(self.module.all): + if t.is_event and name[1] == 'Input': + for names, event in MERGE: + if name[-1] in names: + if event: + event[0].opcodes.update(t.opcodes) + self.module.all[i] = name, event[0] + else: + event.append(t) + + types = [] + events = set() + for name, t in self.module.all: + if not t.is_event or len(t.opcodes) == 1: + types.append((name, t)) + continue + + renamed = tuple(self.rename_type(t, name)) + self.all_types[renamed] = t + if t in events: + continue + events.add(t) + + names = [name[-1] for name in t.opcodes.keys()] + name = name[:-1] + (event_base_name(names), ) + types.append((name, t)) + + t.enum_opcodes = {} + for opname in t.opcodes: + opcode = t.opcodes[opname] + opname = opname[-1] + if opname.startswith(name[-1]): + opname = opname[len(name[-1]):] + t.enum_opcodes[opname] = opcode + self.module.all = types + # Perform preprocessing like renaming, reordering, and adding additional # data fields. def resolve(self): - for name, t in self.module.all: + self.class_name = (adjust_type_name(self.module.namespace.ext_name) + if self.module.namespace.is_ext else 'XProto') + + self.uniquify_events() + + for i, (name, t) in enumerate(self.module.all): + # Work around a name conflict: the type ScreenSaver has the same + # name as the extension, so rename the type. + if name == ('xcb', 'ScreenSaver'): + name = ('xcb', 'ScreenSaverMode') + t.name = name + self.module.all[i] = (name, t) self.resolve_type(t, name) - to_delete = [] - for enum in self.enum_types: - types = self.enum_types[enum] + for enum, types in list(self.enum_types.items()): if len(types) == 1: self.enum_types[enum] = list(types)[0] else: - to_delete.append(enum) - for x in to_delete: - del self.enum_types[x] + del self.enum_types[enum] for t in self.types: - # Lots of fields have types like uint8_t. Ignore these. - if len(t) == 1: - continue - l = list(self.types[t]) - # For some reason, FDs always have distint types so they appear - # duplicated in the set. If the set contains only FDs, then bail. - if all(x.is_fd for x in l): - continue + l = self.types[t] if len(l) == 1: continue @@ -908,26 +1207,34 @@ class GenXproto: self.replace_with_enum.add(t) self.enum_types[enum.name] = simple.name + for node in self.module.namespace.root: + if 'name' in node.attrib: + key = (node.tag, node.attrib['name']) + assert key not in self.module_names + self.module_names[key] = node + # The order of types in xcbproto's xml files are inconsistent, so sort - # them in the order {type aliases, enums, structs, requests/replies}. - def type_order_priority(item): + # them in the order {type aliases, enums, xidunions, structs, + # requests/replies}. + def type_order_priority(module_type): + name, item = module_type if item.is_simple: - return 0 + return 2 if self.get_xidunion_element(name) else 0 if isinstance(item, self.xcbgen.xtypes.Enum): return 1 if isinstance(item, self.xcbgen.xtypes.Request): - return 3 - return 2 + return 4 + return 3 - def cmp((_1, item1), (_2, item2)): - return type_order_priority(item1) - type_order_priority(item2) + def cmp(type1, type2): + return type_order_priority(type1) - type_order_priority(type2) # sort() is guaranteed to be stable. - self.module.all.sort(cmp=cmp) + self.module.all.sort(key=functools.cmp_to_key(cmp)) def gen_header(self): - self.file = self.args.headerfile - include_guard = self.args.headerfile.name.replace('/', '_').replace( + self.file = self.header_file + include_guard = self.header_file.name.replace('/', '_').replace( '.', '_').upper() + '_' self.write('#ifndef ' + include_guard) self.write('#define ' + include_guard) @@ -939,37 +1246,86 @@ class GenXproto: self.write('#include <vector>') self.write() self.write('#include "base/component_export.h"') + self.write('#include "base/optional.h"') self.write('#include "ui/gfx/x/xproto_types.h"') - for direct_import in self.module.direct_imports: + imports = set(self.module.direct_imports) + if self.module.namespace.is_ext: + imports.add(('xproto', 'xproto')) + for direct_import in sorted(list(imports)): self.write('#include "%s.h"' % direct_import[-1]) self.write('#include "%s_undef.h"' % self.module.namespace.header) self.write() - self.write('typedef struct _XDisplay XDisplay;') - self.write() self.write('namespace x11 {') self.write() + self.write('class Connection;') + self.write() + + self.namespace = ['x11'] + if not self.module.namespace.is_ext: + for (name, item) in self.module.all: + self.declare_type(item, name) name = self.class_name self.undef(name) with Indent(self, 'class COMPONENT_EXPORT(X11) %s {' % name, '};'): self.namespace = ['x11', self.class_name] self.write('public:') - self.write('explicit %s(XDisplay* display);' % name) + if self.module.namespace.is_ext: + self.write('static constexpr unsigned major_version = %s;' % + self.module.namespace.major_version) + self.write('static constexpr unsigned minor_version = %s;' % + self.module.namespace.minor_version) + self.write() + self.write(name + '(Connection* connection,') + self.write(' const x11::QueryExtensionReply& info);') + self.write() + with Indent(self, 'uint8_t present() const {', '}'): + self.write('return info_.present;') + with Indent(self, 'uint8_t major_opcode() const {', '}'): + self.write('return info_.major_opcode;') + with Indent(self, 'uint8_t first_event() const {', '}'): + self.write('return info_.first_event;') + with Indent(self, 'uint8_t first_error() const {', '}'): + self.write('return info_.first_error;') + else: + self.write('explicit %s(Connection* connection);' % name) self.write() - self.write('XDisplay* display() { return display_; }') + self.write( + 'Connection* connection() const { return connection_; }') self.write() for (name, item) in self.module.all: - self.declare_type(item, name) + if self.module.namespace.is_ext: + self.declare_type(item, name) + elif isinstance(item, self.xcbgen.xtypes.Request): + self.declare_request(item) self.write('private:') - self.write('XDisplay* const display_;') + self.write('x11::Connection* const connection_;') + if self.module.namespace.is_ext: + self.write('x11::QueryExtensionReply info_{};') self.write() self.write('} // namespace x11') self.write() + self.namespace = [] + + def binop(op, name): + self.write('inline constexpr %s operator%s(' % (name, op)) + with Indent(self, ' {0} l, {0} r)'.format(name) + ' {', '}'): + self.write('using T = std::underlying_type_t<%s>;' % name) + self.write('return static_cast<%s>(' % name) + self.write(' static_cast<T>(l) %s static_cast<T>(r));' % op) + self.write() + + for enum, name in self.bitenums: + name = self.qualtype(enum, name) + binop('|', name) + binop('&', name) + + self.write() self.write('#endif // ' + include_guard) def gen_source(self): - self.file = self.args.sourcefile + self.file = self.source_file self.write('#include "%s.h"' % self.module.namespace.header) self.write() self.write('#include <xcb/xcb.h>') @@ -980,46 +1336,246 @@ class GenXproto: self.write() self.write('namespace x11 {') self.write() - name = self.class_name - self.write('%s::%s(XDisplay* display) : display_(display) {}' % - (name, name)) + ctor = '%s::%s' % (self.class_name, self.class_name) + if self.module.namespace.is_ext: + self.write(ctor + '(x11::Connection* connection,') + self.write(' const x11::QueryExtensionReply& info)') + self.write(' : connection_(connection), info_(info) {}') + else: + self.write(ctor + + '(Connection* connection) : connection_(connection) {}') self.write() for (name, item) in self.module.all: - if isinstance(item, self.xcbgen.xtypes.Request): - self.define_request(item) + self.define_type(item, name) self.write('} // namespace x11') - def generate(self): - self.module = self.xcbgen.state.Module(self.args.xmlfile.name, None) + def parse(self): + self.module = self.xcbgen.state.Module(self.xml_filename, None) self.module.register() self.module.resolve() - self.resolve() - self.class_name = (adjust_type_case(self.module.namespace.ext_name) - if self.module.namespace.is_ext else 'XProto') + def generate(self): self.gen_header() self.gen_source() +class GenExtensionManager(FileWriter): + def __init__(self, gen_dir, genprotos): + FileWriter.__init__(self) + + self.gen_dir = gen_dir + self.genprotos = genprotos + self.extensions = [ + proto for proto in genprotos if proto.module.namespace.is_ext + ] + + def gen_header(self): + self.file = open(os.path.join(self.gen_dir, 'extension_manager.h'), + 'w') + self.write('#ifndef UI_GFX_X_EXTENSION_MANAGER_H_') + self.write('#define UI_GFX_X_EXTENSION_MANAGER_H_') + self.write() + self.write('#include <memory>') + self.write() + self.write('#include "base/component_export.h"') + self.write() + self.write('// Avoid conflicts caused by the GenericEvent macro.') + self.write('#include "ui/gfx/x/ge_undef.h"') + self.write() + self.write('namespace x11 {') + self.write() + self.write('class Connection;') + self.write() + for genproto in self.genprotos: + self.write('class %s;' % genproto.class_name) + self.write() + with Indent(self, 'class COMPONENT_EXPORT(X11) ExtensionManager {', + '};'): + self.write('public:') + self.write('ExtensionManager();') + self.write('~ExtensionManager();') + self.write() + for extension in self.extensions: + name = extension.proto + self.write('%s& %s() { return *%s_; }' % + (extension.class_name, name, name)) + self.write() + self.write('protected:') + self.write('void Init(Connection* conn);') + self.write() + self.write('private:') + for extension in self.extensions: + self.write('std::unique_ptr<%s> %s_;' % + (extension.class_name, extension.proto)) + self.write() + self.write('} // namespace x11') + self.write() + self.write('#endif // UI_GFX_X_EXTENSION_MANAGER_H_') + + def gen_source(self): + self.file = open(os.path.join(self.gen_dir, 'extension_manager.cc'), + 'w') + self.write('#include "ui/gfx/x/extension_manager.h"') + self.write() + self.write('#include "ui/gfx/x/connection.h"') + self.write('#include "ui/gfx/x/xproto_internal.h"') + for genproto in self.genprotos: + self.write('#include "ui/gfx/x/%s.h"' % genproto.proto) + self.write() + self.write('namespace x11 {') + self.write() + init = 'void ExtensionManager::Init' + with Indent(self, init + '(Connection* conn) {', '}'): + for extension in self.extensions: + self.write( + 'auto %s_future = conn->QueryExtension({"%s"});' % + (extension.proto, extension.module.namespace.ext_xname)) + self.write() + for extension in self.extensions: + name = extension.proto + self.write( + '%s_ = MakeExtension<%s>(conn, std::move(%s_future));' % + (name, extension.class_name, name)) + self.write() + self.write('ExtensionManager::ExtensionManager() = default;') + self.write('ExtensionManager::~ExtensionManager() = default;') + self.write() + self.write('} // namespace x11') + + +class GenReadEvent(FileWriter): + def __init__(self, gen_dir, genprotos): + FileWriter.__init__(self) + + self.gen_dir = gen_dir + self.genprotos = genprotos + + self.events = [] + for proto in self.genprotos: + for name, item in proto.module.all: + if item.is_event: + self.events.append((name, item, proto)) + + def event_condition(self, event, typename, proto): + ext = 'conn->%s()' % proto.proto + + conds = [] + if not proto.module.namespace.is_ext: + # Core protocol event + opcode = 'evtype' + elif event.is_ge_event: + # GenericEvent extension event + conds.extend([ + 'evtype == GeGenericEvent::opcode', + '%s.present()' % ext, + 'ge->extension == %s.major_opcode()' % ext, + ]) + opcode = 'ge->event_type' + else: + # Extension event + opcode = 'evtype - %s.first_event()' % ext + conds.append('%s.present()' % ext) + + if len(event.opcodes) == 1: + conds.append('%s == %s::opcode' % (opcode, typename)) + else: + conds.append('(%s)' % ' || '.join([ + '%s == %s::%s' % (opcode, typename, opname) + for opname in event.enum_opcodes.keys() + ])) + + return ' && '.join(conds), opcode + + def gen_event(self, name, event, proto): + # We can't ever have a plain generic event. It must be a concrete + # event provided by an extension. + if name == ('xcb', 'GeGeneric'): + return + + name = [adjust_type_name(part) for part in name[1:]] + typename = '::'.join(name) + 'Event' + + cond, opcode = self.event_condition(event, typename, proto) + with Indent(self, 'if (%s) {' % cond, '}'): + self.write('event->type_id_ = %d;' % event.type_id) + with Indent(self, 'event->deleter_ = [](void* event) {', '};'): + self.write('delete reinterpret_cast<%s*>(event);' % typename) + self.write('auto* event_ = new %s;' % typename) + self.write('ReadEvent(event_, buf);') + if len(event.opcodes) > 1: + self.write('{0} = static_cast<decltype({0})>({1});'.format( + 'event_->opcode', opcode)) + self.write('event_->send_event = send_event;') + self.write('event->event_ = event_;') + self.write('return;') + self.write() + + def gen_source(self): + self.file = open(os.path.join(self.gen_dir, 'read_event.cc'), 'w') + self.write('#include "ui/gfx/x/event.h"') + self.write() + self.write('#include "ui/gfx/x/connection.h"') + for genproto in self.genprotos: + self.write('#include "ui/gfx/x/%s.h"' % genproto.proto) + self.write() + self.write('namespace x11 {') + self.write() + self.write('void ReadEvent(') + args = 'Event* event, Connection* conn, const uint8_t* buf' + with Indent(self, ' %s) {' % args, '}'): + cast = 'auto* %s = reinterpret_cast<const %s*>(buf);' + self.write(cast % ('ev', 'xcb_generic_event_t')) + self.write(cast % ('ge', 'xcb_ge_generic_event_t')) + self.write('auto evtype = ev->response_type & ~kSendEventMask;') + self.write('bool send_event = ev->response_type & kSendEventMask;') + self.write() + for name, event, proto in self.events: + self.gen_event(name, event, proto) + self.write('NOTREACHED();') + self.write() + self.write('} // namespace x11') + + def main(): parser = argparse.ArgumentParser() - parser.add_argument('xmlfile', type=argparse.FileType('r')) - parser.add_argument('undeffile', type=argparse.FileType('w')) - parser.add_argument('headerfile', type=argparse.FileType('w')) - parser.add_argument('sourcefile', type=argparse.FileType('w')) - parser.add_argument('--sysroot') + parser.add_argument('xcbproto_dir', type=str) + parser.add_argument('gen_dir', type=str) + parser.add_argument('protos', type=str, nargs='*') args = parser.parse_args() - if args.sysroot: - path = os.path.join(args.sysroot, 'usr', 'lib', 'python2.7', - 'dist-packages') - sys.path.insert(1, path) - + sys.path.insert(1, args.xcbproto_dir) import xcbgen.xtypes import xcbgen.state - generator = GenXproto(args, xcbgen) - generator.generate() + all_types = {} + proto_src_dir = os.path.join(args.xcbproto_dir, 'src') + genprotos = [ + GenXproto(proto, proto_src_dir, args.gen_dir, xcbgen, all_types) + for proto in args.protos + ] + for genproto in genprotos: + genproto.parse() + for genproto in genprotos: + genproto.resolve() + + # Give each event a unique type ID. This is used by x11::Event to + # implement downcasting for events. + type_id = 1 + for proto in genprotos: + for _, item in proto.module.all: + if item.is_event: + item.type_id = type_id + type_id += 1 + + for genproto in genprotos: + genproto.generate() + + gen_extension_manager = GenExtensionManager(args.gen_dir, genprotos) + gen_extension_manager.gen_header() + gen_extension_manager.gen_source() + + gen_read_event = GenReadEvent(args.gen_dir, genprotos) + gen_read_event.gen_source() return 0 diff --git a/chromium/ui/gfx/x/request_queue.cc b/chromium/ui/gfx/x/request_queue.cc deleted file mode 100644 index 9bd3ab2f248..00000000000 --- a/chromium/ui/gfx/x/request_queue.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020 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 "ui/gfx/x/request_queue.h" - -#include "base/check_op.h" - -namespace x11 { - -// static -RequestQueue* RequestQueue::instance_ = nullptr; - -RequestQueue::RequestQueue() { - DCHECK(!instance_); - instance_ = this; -} - -RequestQueue::~RequestQueue() { - DCHECK_EQ(instance_, this); - instance_ = nullptr; -} - -// static -RequestQueue* RequestQueue::GetInstance() { - return instance_; -} - -} // namespace x11 diff --git a/chromium/ui/gfx/x/request_queue.h b/chromium/ui/gfx/x/request_queue.h deleted file mode 100644 index b48b1c031de..00000000000 --- a/chromium/ui/gfx/x/request_queue.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020 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 UI_GFX_X_REQUEST_QUEUE_H_ -#define UI_GFX_X_REQUEST_QUEUE_H_ - -#include <xcb/xcb.h> - -#include <memory> - -#include "base/callback_forward.h" -#include "base/component_export.h" -#include "base/memory/free_deleter.h" - -namespace ui { -class X11EventSource; -} - -namespace x11 { - -// This interface allows //ui/gfx/x to call into //ui/events/platform/x11 which -// is at a higher layer. It should not be used by client code. -class COMPONENT_EXPORT(X11) RequestQueue { - private: - friend class ui::X11EventSource; - template <typename T> - friend class Future; - - using Reply = std::unique_ptr<uint8_t, base::FreeDeleter>; - using Error = std::unique_ptr<xcb_generic_error_t, base::FreeDeleter>; - using ResponseCallback = base::OnceCallback<void(Reply reply, Error error)>; - - RequestQueue(); - virtual ~RequestQueue(); - - // Adds a request to the queue. |is_void| indicates if a reply is generated - // for this request. |sequence| is the ID of the request. |callback| will - // be called upon request completion (or failure). - virtual void AddRequest(bool is_void, - unsigned int sequence, - ResponseCallback callback) = 0; - - static RequestQueue* GetInstance(); - - static RequestQueue* instance_; -}; - -} // namespace x11 - -#endif // UI_GFX_X_REQUEST_QUEUE_H_ diff --git a/chromium/ui/gfx/x/x11.h b/chromium/ui/gfx/x/x11.h index b163f788d98..2cae7c5b3ee 100644 --- a/chromium/ui/gfx/x/x11.h +++ b/chromium/ui/gfx/x/x11.h @@ -31,11 +31,8 @@ extern "C" { #include <X11/extensions/XShm.h> #include <X11/extensions/XTest.h> #include <X11/extensions/Xfixes.h> -#include <X11/extensions/Xrandr.h> #include <X11/extensions/Xrender.h> #include <X11/extensions/record.h> -#include <X11/extensions/scrnsaver.h> -#include <X11/extensions/shape.h> #include <X11/extensions/sync.h> // Define XK_xxx before the #include of <X11/keysym.h> so that <X11/keysym.h> @@ -76,6 +73,9 @@ extern "C" { #include <X11/Sunkeysym.h> #include <X11/XF86keysym.h> #include <X11/keysym.h> +} + +#include "ui/gfx/x/connection.h" // These commonly used names are undefined and if necessary recreated // in the x11 namespace below. This is the main purpose of this header @@ -97,14 +97,13 @@ extern "C" { #undef DeviceAdded // Defined by X11/extensions/XI.h to 0 #undef DeviceMode // Defined by X11/extensions/XI.h to 1 #undef DeviceRemoved // Defined by X11/extensions/XI.h to 1 -#undef FocusIn // Defined by X.h to 9 -#undef FocusOut // Defined by X.h to 10 -#undef None // Defined by X11/X.h to 0L -#undef True // Defined by X11/Xlib.h to 1 -#undef False // Defined by X11/Xlib.h to 0 -#undef CurrentTime // Defined by X11/X.h to 0L -#undef Success // Defined by X11/X.h to 0 -} +#undef FocusIn // Defined by X.h to 9 +#undef FocusOut // Defined by X.h to 10 +#undef None // Defined by X11/X.h to 0L +#undef True // Defined by X11/Xlib.h to 1 +#undef False // Defined by X11/Xlib.h to 0 +#undef CurrentTime // Defined by X11/X.h to 0L +#undef Success // Defined by X11/X.h to 0 // The x11 namespace allows to scope X11 constants and types that // would be problematic at the default preprocessor level. diff --git a/chromium/ui/gfx/x/x11_atom_cache.cc b/chromium/ui/gfx/x/x11_atom_cache.cc index 75f18c1b57a..2e09c095eea 100644 --- a/chromium/ui/gfx/x/x11_atom_cache.cc +++ b/chromium/ui/gfx/x/x11_atom_cache.cc @@ -20,76 +20,76 @@ namespace { struct { const char* atom_name; - Atom atom_value; + x11::Atom atom_value; } const kPredefinedAtoms[] = { - // {"PRIMARY", XA_PRIMARY}, - // {"SECONDARY", XA_SECONDARY}, - // {"ARC", XA_ARC}, - {"ATOM", XA_ATOM}, - // {"BITMAP", XA_BITMAP}, - {"CARDINAL", XA_CARDINAL}, - // {"COLORMAP", XA_COLORMAP}, - // {"CURSOR", XA_CURSOR}, - // {"CUT_BUFFER0", XA_CUT_BUFFER0}, - // {"CUT_BUFFER1", XA_CUT_BUFFER1}, - // {"CUT_BUFFER2", XA_CUT_BUFFER2}, - // {"CUT_BUFFER3", XA_CUT_BUFFER3}, - // {"CUT_BUFFER4", XA_CUT_BUFFER4}, - // {"CUT_BUFFER5", XA_CUT_BUFFER5}, - // {"CUT_BUFFER6", XA_CUT_BUFFER6}, - // {"CUT_BUFFER7", XA_CUT_BUFFER7}, - // {"DRAWABLE", XA_DRAWABLE}, - // {"FONT", XA_FONT}, - // {"INTEGER", XA_INTEGER}, - // {"PIXMAP", XA_PIXMAP}, - // {"POINT", XA_POINT}, - // {"RECTANGLE", XA_RECTANGLE}, - // {"RESOURCE_MANAGER", XA_RESOURCE_MANAGER}, - // {"RGB_COLOR_MAP", XA_RGB_COLOR_MAP}, - // {"RGB_BEST_MAP", XA_RGB_BEST_MAP}, - // {"RGB_BLUE_MAP", XA_RGB_BLUE_MAP}, - // {"RGB_DEFAULT_MAP", XA_RGB_DEFAULT_MAP}, - // {"RGB_GRAY_MAP", XA_RGB_GRAY_MAP}, - // {"RGB_GREEN_MAP", XA_RGB_GREEN_MAP}, - // {"RGB_RED_MAP", XA_RGB_RED_MAP}, - {"STRING", XA_STRING}, - // {"VISUALID", XA_VISUALID}, - // {"WINDOW", XA_WINDOW}, - // {"WM_COMMAND", XA_WM_COMMAND}, - // {"WM_HINTS", XA_WM_HINTS}, - // {"WM_CLIENT_MACHINE", XA_WM_CLIENT_MACHINE}, - // {"WM_ICON_NAME", XA_WM_ICON_NAME}, - // {"WM_ICON_SIZE", XA_WM_ICON_SIZE}, - // {"WM_NAME", XA_WM_NAME}, - // {"WM_NORMAL_HINTS", XA_WM_NORMAL_HINTS}, - // {"WM_SIZE_HINTS", XA_WM_SIZE_HINTS}, - // {"WM_ZOOM_HINTS", XA_WM_ZOOM_HINTS}, - // {"MIN_SPACE", XA_MIN_SPACE}, - // {"NORM_SPACE", XA_NORM_SPACE}, - // {"MAX_SPACE", XA_MAX_SPACE}, - // {"END_SPACE", XA_END_SPACE}, - // {"SUPERSCRIPT_X", XA_SUPERSCRIPT_X}, - // {"SUPERSCRIPT_Y", XA_SUPERSCRIPT_Y}, - // {"SUBSCRIPT_X", XA_SUBSCRIPT_X}, - // {"SUBSCRIPT_Y", XA_SUBSCRIPT_Y}, - // {"UNDERLINE_POSITION", XA_UNDERLINE_POSITION}, - // {"UNDERLINE_THICKNESS", XA_UNDERLINE_THICKNESS}, - // {"STRIKEOUT_ASCENT", XA_STRIKEOUT_ASCENT}, - // {"STRIKEOUT_DESCENT", XA_STRIKEOUT_DESCENT}, - // {"ITALIC_ANGLE", XA_ITALIC_ANGLE}, - // {"X_HEIGHT", XA_X_HEIGHT}, - // {"QUAD_WIDTH", XA_QUAD_WIDTH}, - // {"WEIGHT", XA_WEIGHT}, - // {"POINT_SIZE", XA_POINT_SIZE}, - // {"RESOLUTION", XA_RESOLUTION}, - // {"COPYRIGHT", XA_COPYRIGHT}, - // {"NOTICE", XA_NOTICE}, - // {"FONT_NAME", XA_FONT_NAME}, - // {"FAMILY_NAME", XA_FAMILY_NAME}, - // {"FULL_NAME", XA_FULL_NAME}, - // {"CAP_HEIGHT", XA_CAP_HEIGHT}, - {"WM_CLASS", XA_WM_CLASS}, - // {"WM_TRANSIENT_FOR", XA_WM_TRANSIENT_FOR}, + // {"PRIMARY", x11::Atom::PRIMARY}, + // {"SECONDARY", x11::Atom::SECONDARY}, + // {"ARC", x11::Atom::ARC}, + {"ATOM", x11::Atom::ATOM}, + // {"BITMAP", x11::Atom::BITMAP}, + {"CARDINAL", x11::Atom::CARDINAL}, + // {"COLORMAP", x11::Atom::COLORMAP}, + // {"CURSOR", x11::Atom::CURSOR}, + // {"CUT_BUFFER0", x11::Atom::CUT_BUFFER0}, + // {"CUT_BUFFER1", x11::Atom::CUT_BUFFER1}, + // {"CUT_BUFFER2", x11::Atom::CUT_BUFFER2}, + // {"CUT_BUFFER3", x11::Atom::CUT_BUFFER3}, + // {"CUT_BUFFER4", x11::Atom::CUT_BUFFER4}, + // {"CUT_BUFFER5", x11::Atom::CUT_BUFFER5}, + // {"CUT_BUFFER6", x11::Atom::CUT_BUFFER6}, + // {"CUT_BUFFER7", x11::Atom::CUT_BUFFER7}, + // {"DRAWABLE", x11::Atom::DRAWABLE}, + // {"FONT", x11::Atom::FONT}, + // {"INTEGER", x11::Atom::INTEGER}, + // {"PIXMAP", x11::Atom::PIXMAP}, + // {"POINT", x11::Atom::POINT}, + // {"RECTANGLE", x11::Atom::RECTANGLE}, + // {"RESOURCE_MANAGER", x11::Atom::RESOURCE_MANAGER}, + // {"RGB_COLOR_MAP", x11::Atom::RGB_COLOR_MAP}, + // {"RGB_BEST_MAP", x11::Atom::RGB_BEST_MAP}, + // {"RGB_BLUE_MAP", x11::Atom::RGB_BLUE_MAP}, + // {"RGB_DEFAULT_MAP", x11::Atom::RGB_DEFAULT_MAP}, + // {"RGB_GRAY_MAP", x11::Atom::RGB_GRAY_MAP}, + // {"RGB_GREEN_MAP", x11::Atom::RGB_GREEN_MAP}, + // {"RGB_RED_MAP", x11::Atom::RGB_RED_MAP}, + {"STRING", x11::Atom::STRING}, + // {"VISUALID", x11::Atom::VISUALID}, + // {"WINDOW", x11::Atom::WINDOW}, + // {"WM_COMMAND", x11::Atom::WM_COMMAND}, + // {"WM_HINTS", x11::Atom::WM_HINTS}, + // {"WM_CLIENT_MACHINE", x11::Atom::WM_CLIENT_MACHINE}, + // {"WM_ICON_NAME", x11::Atom::WM_ICON_NAME}, + // {"WM_ICON_SIZE", x11::Atom::WM_ICON_SIZE}, + // {"WM_NAME", x11::Atom::WM_NAME}, + // {"WM_NORMAL_HINTS", x11::Atom::WM_NORMAL_HINTS}, + // {"WM_SIZE_HINTS", x11::Atom::WM_SIZE_HINTS}, + // {"WM_ZOOM_HINTS", x11::Atom::WM_ZOOM_HINTS}, + // {"MIN_SPACE", x11::Atom::MIN_SPACE}, + // {"NORM_SPACE", x11::Atom::NORM_SPACE}, + // {"MAX_SPACE", x11::Atom::MAX_SPACE}, + // {"END_SPACE", x11::Atom::END_SPACE}, + // {"SUPERSCRIPT_X", x11::Atom::SUPERSCRIPT_X}, + // {"SUPERSCRIPT_Y", x11::Atom::SUPERSCRIPT_Y}, + // {"SUBSCRIPT_X", x11::Atom::SUBSCRIPT_X}, + // {"SUBSCRIPT_Y", x11::Atom::SUBSCRIPT_Y}, + // {"UNDERLINE_POSITION", x11::Atom::UNDERLINE_POSITION}, + // {"UNDERLINE_THICKNESS", x11::Atom::UNDERLINE_THICKNESS}, + // {"STRIKEOUT_ASCENT", x11::Atom::STRIKEOUT_ASCENT}, + // {"STRIKEOUT_DESCENT", x11::Atom::STRIKEOUT_DESCENT}, + // {"ITALIC_ANGLE", x11::Atom::ITALIC_ANGLE}, + // {"X_HEIGHT", x11::Atom::X_HEIGHT}, + // {"QUAD_WIDTH", x11::Atom::QUAD_WIDTH}, + // {"WEIGHT", x11::Atom::WEIGHT}, + // {"POINT_SIZE", x11::Atom::POINT_SIZE}, + // {"RESOLUTION", x11::Atom::RESOLUTION}, + // {"COPYRIGHT", x11::Atom::COPYRIGHT}, + // {"NOTICE", x11::Atom::NOTICE}, + // {"FONT_NAME", x11::Atom::FONT_NAME}, + // {"FAMILY_NAME", x11::Atom::FAMILY_NAME}, + // {"FULL_NAME", x11::Atom::FULL_NAME}, + // {"CAP_HEIGHT", x11::Atom::CAP_HEIGHT}, + {"WM_CLASS", x11::Atom::WM_CLASS}, + // {"WM_TRANSIENT_FOR", x11::Atom::WM_TRANSIENT_FOR}, }; constexpr const char* kAtomsToCache[] = { @@ -244,7 +244,7 @@ constexpr int kCacheCount = base::size(kAtomsToCache); namespace gfx { -XAtom GetAtom(const char* name) { +x11::Atom GetAtom(const std::string& name) { return X11AtomCache::GetInstance()->GetAtom(name); } @@ -256,27 +256,26 @@ X11AtomCache::X11AtomCache() : connection_(x11::Connection::Get()) { for (const auto& predefined_atom : kPredefinedAtoms) cached_atoms_[predefined_atom.atom_name] = predefined_atom.atom_value; - std::vector<x11::Future<x11::XProto::InternAtomReply>> requests; + std::vector<x11::Future<x11::InternAtomReply>> requests; requests.reserve(kCacheCount); for (const char* name : kAtomsToCache) requests.push_back(connection_->InternAtom({.name = name})); for (size_t i = 0; i < kCacheCount; ++i) { if (auto response = requests[i].Sync()) - cached_atoms_[kAtomsToCache[i]] = static_cast<XAtom>(response->atom); + cached_atoms_[kAtomsToCache[i]] = static_cast<x11::Atom>(response->atom); } } X11AtomCache::~X11AtomCache() = default; -XAtom X11AtomCache::GetAtom(const char* name) const { - DCHECK(name); +x11::Atom X11AtomCache::GetAtom(const std::string& name) const { const auto it = cached_atoms_.find(name); if (it != cached_atoms_.end()) return it->second; - XAtom atom = 0; + x11::Atom atom = x11::Atom::None; if (auto response = connection_->InternAtom({.name = name}).Sync()) { - atom = static_cast<XAtom>(response->atom); + atom = static_cast<x11::Atom>(response->atom); cached_atoms_.emplace(name, atom); } else { static int error_count = 0; diff --git a/chromium/ui/gfx/x/x11_atom_cache.h b/chromium/ui/gfx/x/x11_atom_cache.h index b04608a783c..53fb799875f 100644 --- a/chromium/ui/gfx/x/x11_atom_cache.h +++ b/chromium/ui/gfx/x/x11_atom_cache.h @@ -24,7 +24,7 @@ class Connection; namespace gfx { // Gets the X atom for default display corresponding to atom_name. -GFX_EXPORT XAtom GetAtom(const char* atom_name); +GFX_EXPORT x11::Atom GetAtom(const std::string& atom_name); // Pre-caches all Atoms on first use to minimize roundtrips to the X11 // server. By default, GetAtom() will CHECK() that atoms accessed through @@ -35,7 +35,7 @@ class GFX_EXPORT X11AtomCache { static X11AtomCache* GetInstance(); private: - friend XAtom GetAtom(const char* atom_name); + friend x11::Atom GetAtom(const std::string& atom_name); friend struct base::DefaultSingletonTraits<X11AtomCache>; X11AtomCache(); @@ -43,12 +43,12 @@ class GFX_EXPORT X11AtomCache { // Returns the pre-interned Atom without having to go to the x server. // On failure, x11::None is returned. - XAtom GetAtom(const char*) const; + x11::Atom GetAtom(const std::string&) const; x11::Connection* connection_; // Using std::map, as it is possible for thousands of atoms to be registered. - mutable std::map<std::string, XAtom> cached_atoms_; + mutable std::map<std::string, x11::Atom> cached_atoms_; DISALLOW_COPY_AND_ASSIGN(X11AtomCache); }; diff --git a/chromium/ui/gfx/x/x11_error_tracker.cc b/chromium/ui/gfx/x/x11_error_tracker.cc index a6f054f5ba2..6592ae04838 100644 --- a/chromium/ui/gfx/x/x11_error_tracker.cc +++ b/chromium/ui/gfx/x/x11_error_tracker.cc @@ -10,7 +10,7 @@ namespace { unsigned char g_x11_error_code = 0; -static gfx::X11ErrorTracker* g_handler = NULL; +static gfx::X11ErrorTracker* g_handler = nullptr; int X11ErrorHandler(Display* display, XErrorEvent* error) { g_x11_error_code = error->error_code; @@ -24,7 +24,7 @@ namespace gfx { X11ErrorTracker::X11ErrorTracker() { // This is a non-exhaustive check for incorrect usage. It disallows nested // X11ErrorTracker instances on the same thread. - DCHECK(g_handler == NULL); + DCHECK(g_handler == nullptr); g_handler = this; XSync(GetXDisplay(), False); old_handler_ = XSetErrorHandler(X11ErrorHandler); @@ -32,7 +32,7 @@ X11ErrorTracker::X11ErrorTracker() { } X11ErrorTracker::~X11ErrorTracker() { - g_handler = NULL; + g_handler = nullptr; XSetErrorHandler(old_handler_); } diff --git a/chromium/ui/gfx/x/x11_path.cc b/chromium/ui/gfx/x/x11_path.cc index 4a171dbc564..937e206007e 100644 --- a/chromium/ui/gfx/x/x11_path.cc +++ b/chromium/ui/gfx/x/x11_path.cc @@ -12,8 +12,9 @@ namespace gfx { -Region CreateRegionFromSkRegion(const SkRegion& region) { - Region result = XCreateRegion(); +std::unique_ptr<std::vector<x11::Rectangle>> CreateRegionFromSkRegion( + const SkRegion& region) { + auto result = std::make_unique<std::vector<x11::Rectangle>>(); for (SkRegion::Iterator i(region); !i.done(); i.next()) { XRectangle rect; @@ -21,23 +22,23 @@ Region CreateRegionFromSkRegion(const SkRegion& region) { rect.y = i.rect().y(); rect.width = i.rect().width(); rect.height = i.rect().height(); - XUnionRectWithRegion(&rect, result, result); + result->push_back({ + .x = i.rect().x(), + .y = i.rect().y(), + .width = i.rect().width(), + .height = i.rect().height(), + }); } return result; } -Region CreateRegionFromSkPath(const SkPath& path) { - int point_count = path.getPoints(nullptr, 0); - std::unique_ptr<SkPoint[]> points(new SkPoint[point_count]); - path.getPoints(points.get(), point_count); - std::unique_ptr<XPoint[]> x11_points(new XPoint[point_count]); - for (int i = 0; i < point_count; ++i) { - x11_points[i].x = SkScalarRoundToInt(points[i].fX); - x11_points[i].y = SkScalarRoundToInt(points[i].fY); - } - - return XPolygonRegion(x11_points.get(), point_count, EvenOddRule); +std::unique_ptr<std::vector<x11::Rectangle>> CreateRegionFromSkPath( + const SkPath& path) { + SkRegion clip{path.getBounds().roundOut()}; + SkRegion region; + region.setPath(path, clip); + return CreateRegionFromSkRegion(region); } } // namespace gfx diff --git a/chromium/ui/gfx/x/x11_path.h b/chromium/ui/gfx/x/x11_path.h index a85e67a287e..9e5d4be9bae 100644 --- a/chromium/ui/gfx/x/x11_path.h +++ b/chromium/ui/gfx/x/x11_path.h @@ -6,7 +6,7 @@ #define UI_GFX_X_X11_PATH_H_ #include "ui/gfx/gfx_export.h" -#include "ui/gfx/x/x11_types.h" +#include "ui/gfx/x/xproto.h" class SkPath; class SkRegion; @@ -15,11 +15,13 @@ namespace gfx { // Creates a new XRegion given |region|. The caller is responsible for // destroying the returned region. -GFX_EXPORT XRegion* CreateRegionFromSkRegion(const SkRegion& region); +GFX_EXPORT std::unique_ptr<std::vector<x11::Rectangle>> +CreateRegionFromSkRegion(const SkRegion& region); // Creates a new XRegion given |path|. The caller is responsible for destroying // the returned region. -GFX_EXPORT XRegion* CreateRegionFromSkPath(const SkPath& path); +GFX_EXPORT std::unique_ptr<std::vector<x11::Rectangle>> CreateRegionFromSkPath( + const SkPath& path); } // namespace gfx diff --git a/chromium/ui/gfx/x/x11_types.cc b/chromium/ui/gfx/x/x11_types.cc index ebbe076ab6b..ad41a13dff3 100644 --- a/chromium/ui/gfx/x/x11_types.cc +++ b/chromium/ui/gfx/x/x11_types.cc @@ -77,10 +77,10 @@ void PutARGBImage(XDisplay* display, image.width = data_width; image.height = data_height; - image.format = static_cast<int>(x11::XProto::ImageFormat::ZPixmap); - image.byte_order = static_cast<int>(x11::XProto::ImageOrder::LSBFirst); + image.format = static_cast<int>(x11::ImageFormat::ZPixmap); + image.byte_order = static_cast<int>(x11::ImageOrder::LSBFirst); image.bitmap_unit = 8; - image.bitmap_bit_order = static_cast<int>(x11::XProto::ImageOrder::LSBFirst); + image.bitmap_bit_order = static_cast<int>(x11::ImageOrder::LSBFirst); image.depth = depth; image.bits_per_pixel = pixmap_bpp; image.bytes_per_line = data_width * pixmap_bpp / 8; diff --git a/chromium/ui/gfx/x/x11_types.h b/chromium/ui/gfx/x/x11_types.h index 8ea0ca4ca49..a1886764342 100644 --- a/chromium/ui/gfx/x/x11_types.h +++ b/chromium/ui/gfx/x/x11_types.h @@ -10,9 +10,8 @@ #include <memory> #include "ui/gfx/gfx_export.h" +#include "ui/gfx/x/connection.h" -typedef unsigned long XAtom; -typedef unsigned long XID; typedef unsigned long VisualID; typedef struct _XcursorImage XcursorImage; typedef union _XEvent XEvent; diff --git a/chromium/ui/gfx/x/xproto_internal.h b/chromium/ui/gfx/x/xproto_internal.h index f76ebe5ecac..32c68b4f801 100644 --- a/chromium/ui/gfx/x/xproto_internal.h +++ b/chromium/ui/gfx/x/xproto_internal.h @@ -20,7 +20,9 @@ #include <type_traits> #include "base/component_export.h" +#include "base/logging.h" #include "base/optional.h" +#include "ui/gfx/x/connection.h" #include "ui/gfx/x/xproto_types.h" namespace x11 { @@ -49,11 +51,17 @@ struct ReadBuffer { }; template <typename T> -void Write(const T* t, WriteBuffer* buf) { - static_assert(std::is_trivially_copyable<T>::value, ""); +void VerifyAlignment(T* t, size_t offset) { // On the wire, X11 types are always aligned to their size. This is a sanity // check to ensure padding etc are working properly. - DCHECK_EQ(buf->size() % sizeof(*t), 0UL); + if (sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8) + DCHECK_EQ(offset % sizeof(*t), 0UL); +} + +template <typename T> +void Write(const T* t, WriteBuffer* buf) { + static_assert(std::is_trivially_copyable<T>::value, ""); + VerifyAlignment(t, buf->size()); const uint8_t* start = reinterpret_cast<const uint8_t*>(t); std::copy(start, start + sizeof(*t), std::back_inserter(*buf)); } @@ -61,9 +69,7 @@ void Write(const T* t, WriteBuffer* buf) { template <typename T> void Read(T* t, ReadBuffer* buf) { static_assert(std::is_trivially_copyable<T>::value, ""); - // On the wire, X11 types are always aligned to their size. This is a sanity - // check to ensure padding etc are working properly. - DCHECK_EQ(buf->offset % sizeof(*t), 0UL); + VerifyAlignment(t, buf->offset); memcpy(t, buf->data + buf->offset, sizeof(*t)); buf->offset += sizeof(*t); } @@ -85,12 +91,11 @@ inline void Align(ReadBuffer* buf, size_t align) { } template <typename Reply> -Future<Reply> SendRequest(XDisplay* display, WriteBuffer* buf) { +Future<Reply> SendRequest(x11::Connection* connection, WriteBuffer* buf) { // Clang crashes when the value of |is_void| is inlined below, // so keep this variable outside of |xpr|. constexpr bool is_void = std::is_void<Reply>::value; xcb_protocol_request_t xpr{ - .count = 1, .ext = nullptr, .isvoid = is_void, }; @@ -101,23 +106,46 @@ Future<Reply> SendRequest(XDisplay* display, WriteBuffer* buf) { uint16_t length; }; - auto* header = reinterpret_cast<RequestHeader*>(buf->data()); + struct ExtendedRequestHeader { + RequestHeader header; + uint32_t long_length; + }; + static_assert(sizeof(ExtendedRequestHeader) == 8, ""); + + auto* old_header = reinterpret_cast<RequestHeader*>(buf->data()); + ExtendedRequestHeader new_header{*old_header, 0}; + // Requests are always a multiple of 4 bytes on the wire. Because of this, // the length field represents the size in chunks of 4 bytes. DCHECK_EQ(buf->size() % 4, 0UL); - DCHECK_LE(buf->size() / 4, std::numeric_limits<uint16_t>::max()); - header->length = buf->size() / 4; + size_t size32 = buf->size() / 4; + + struct iovec io[4]; + memset(&io, 0, sizeof(io)); + if (size32 < connection->setup().maximum_request_length) { + xpr.count = 1; + old_header->length = size32; + io[2].iov_base = buf->data(); + io[2].iov_len = buf->size(); + } else if (size32 < connection->extended_max_request_length()) { + xpr.count = 2; + DCHECK_EQ(new_header.header.length, 0U); + new_header.long_length = size32 + 1; + io[2].iov_base = &new_header; + io[2].iov_len = sizeof(ExtendedRequestHeader); + io[3].iov_base = buf->data() + sizeof(RequestHeader); + io[3].iov_len = buf->size() - sizeof(RequestHeader); + } else { + LOG(ERROR) << "Cannot send request of length " << buf->size(); + return {nullptr, base::nullopt}; + } - struct iovec io[3]; - io[2].iov_base = buf->data(); - io[2].iov_len = buf->size(); + xcb_connection_t* conn = connection->XcbConnection(); auto flags = XCB_REQUEST_CHECKED | XCB_REQUEST_RAW; - - xcb_connection_t* conn = XGetXCBConnection(display); auto sequence = xcb_send_request(conn, flags, &io[2], &xpr); if (xcb_connection_has_error(conn)) return {nullptr, base::nullopt}; - return {display, sequence}; + return {connection, sequence}; } // Helper function for xcbproto popcount. Given an integral type, returns the @@ -143,19 +171,51 @@ bool CaseEq(T t, S s) { return t == static_cast<decltype(t)>(s); } -// Helper function for xcbproto bitcase and & expressions. Checks if the -// bitmasks |t| and |s| have any intersection. +// Helper function for xcbproto bitcase expressions. Checks if the bitmasks |t| +// and |s| have any intersection. +template <typename T, typename S> +bool CaseAnd(T t, S s) { + return static_cast<EnumBaseType<T>>(t) & static_cast<EnumBaseType<T>>(s); +} + +// Helper function for xcbproto & expressions. Computes |t| & |s|. template <typename T, typename S> -bool BitAnd(T t, S s) { +auto BitAnd(T t, S s) { return static_cast<EnumBaseType<T>>(t) & static_cast<EnumBaseType<T>>(s); } -// Helper function for ~ expressions. +// Helper function for xcbproto ~ expressions. template <typename T> -bool BitNot(T t) { +auto BitNot(T t) { return ~static_cast<EnumBaseType<T>>(t); } +// Helper function for generating switch values. |switch_var| is the value to +// modify. |enum_val| is the value to set |switch_var| to if this is a regular +// case, or the bit to be set in |switch_var| if this is a bit case. This +// function is a no-op when |condition| is false. +template <typename T> +auto SwitchVar(T enum_val, bool condition, bool is_bitcase, T* switch_var) { + using EnumInt = EnumBaseType<T>; + if (!condition) + return; + EnumInt switch_int = static_cast<EnumInt>(*switch_var); + if (is_bitcase) { + *switch_var = static_cast<T>(switch_int | static_cast<EnumInt>(enum_val)); + } else { + DCHECK(!switch_int); + *switch_var = enum_val; + } +} + +template <typename T> +std::unique_ptr<T> MakeExtension(Connection* connection, + Future<QueryExtensionReply> future) { + auto reply = future.Sync(); + return std::make_unique<T>(connection, + reply ? *reply.reply : QueryExtensionReply{}); +} + } // namespace x11 #endif // UI_GFX_X_XPROTO_INTERNAL_H_ diff --git a/chromium/ui/gfx/x/xproto_types.cc b/chromium/ui/gfx/x/xproto_types.cc new file mode 100644 index 00000000000..0fc634cecf3 --- /dev/null +++ b/chromium/ui/gfx/x/xproto_types.cc @@ -0,0 +1,76 @@ +// Copyright 2020 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 "ui/gfx/x/xproto_types.h" + +#include "ui/gfx/x/connection.h" + +namespace x11 { + +FutureBase::FutureBase(Connection* connection, + base::Optional<unsigned int> sequence) + : connection_(connection), sequence_(sequence) {} + +// If a user-defined response-handler is not installed before this object goes +// out of scope, a default response handler will be installed. The default +// handler throws away the reply and prints the error if there is one. +FutureBase::~FutureBase() { + if (!sequence_) + return; + + OnResponseImpl(base::BindOnce( + [](Connection* connection, RawReply reply, RawError error) { + if (!error) + return; + + x11::LogErrorEventDescription(XErrorEvent({ + .type = error->response_type, + .display = connection->display(), + .resourceid = error->resource_id, + .serial = error->full_sequence, + .error_code = error->error_code, + .request_code = error->major_code, + .minor_code = error->minor_code, + })); + }, + connection_)); +} + +FutureBase::FutureBase(FutureBase&& future) + : connection_(future.connection_), sequence_(future.sequence_) { + future.connection_ = nullptr; + future.sequence_ = base::nullopt; +} + +FutureBase& FutureBase::operator=(FutureBase&& future) { + connection_ = future.connection_; + sequence_ = future.sequence_; + future.connection_ = nullptr; + future.sequence_ = base::nullopt; + return *this; +} + +void FutureBase::SyncImpl(Error** raw_error, uint8_t** raw_reply) { + if (!sequence_) + return; + *raw_reply = reinterpret_cast<uint8_t*>( + xcb_wait_for_reply(connection_->XcbConnection(), *sequence_, raw_error)); + sequence_ = base::nullopt; +} + +void FutureBase::SyncImpl(Error** raw_error) { + if (!sequence_) + return; + *raw_error = xcb_request_check(connection_->XcbConnection(), {*sequence_}); + sequence_ = base::nullopt; +} + +void FutureBase::OnResponseImpl(ResponseCallback callback) { + if (!sequence_) + return; + connection_->AddRequest(*sequence_, std::move(callback)); + sequence_ = base::nullopt; +} + +} // namespace x11 diff --git a/chromium/ui/gfx/x/xproto_types.h b/chromium/ui/gfx/x/xproto_types.h index 9e5dfe09534..415c2146481 100644 --- a/chromium/ui/gfx/x/xproto_types.h +++ b/chromium/ui/gfx/x/xproto_types.h @@ -16,25 +16,37 @@ #include "base/callback.h" #include "base/memory/free_deleter.h" #include "base/optional.h" -#include "ui/gfx/x/request_queue.h" #include "ui/gfx/x/xproto_util.h" typedef struct _XDisplay XDisplay; namespace x11 { +class Connection; + +constexpr uint8_t kSendEventMask = 0x80; + namespace detail { template <typename Reply> std::unique_ptr<Reply> ReadReply(const uint8_t* buffer); -} +} // namespace detail using Error = xcb_generic_error_t; template <class Reply> class Future; +template <typename T> +T Read(const uint8_t* buf); + +template <typename T> +std::vector<uint8_t> Write(const T& t); + +template <typename T> +void ReadEvent(T* event, const uint8_t* buf); + template <typename Reply> struct Response { operator bool() const { return reply.get(); } @@ -62,66 +74,48 @@ struct Response<void> { : error(std::move(error)) {} }; +class COMPONENT_EXPORT(X11) FutureBase { + public: + using RawReply = std::unique_ptr<uint8_t, base::FreeDeleter>; + using RawError = std::unique_ptr<xcb_generic_error_t, base::FreeDeleter>; + using ResponseCallback = + base::OnceCallback<void(RawReply reply, RawError error)>; + + FutureBase(const FutureBase&) = delete; + FutureBase& operator=(const FutureBase&) = delete; + + protected: + FutureBase(Connection* connection, base::Optional<unsigned int> sequence); + ~FutureBase(); + + FutureBase(FutureBase&& future); + FutureBase& operator=(FutureBase&& future); + + void SyncImpl(Error** raw_error, uint8_t** raw_reply); + void SyncImpl(Error** raw_error); + + void OnResponseImpl(ResponseCallback callback); + + private: + Connection* connection_; + base::Optional<unsigned int> sequence_; +}; + // An x11::Future wraps an asynchronous response from the X11 server. The // response may be waited-for with Sync(), or asynchronously handled by // installing a response handler using OnResponse(). template <typename Reply> -class Future { +class Future : public FutureBase { public: using Callback = base::OnceCallback<void(Response<Reply> response)>; - using RQ = RequestQueue; - - // If a user-defined response-handler is not installed before this object goes - // out of scope, a default response handler will be installed. The default - // handler throws away the reply and prints the error if there is one. - ~Future() { - if (!sequence_) - return; - - EnqueueRequest(base::BindOnce( - [](XDisplay* display, RQ::Reply reply, RQ::Error error) { - if (!error) - return; - - x11::LogErrorEventDescription(XErrorEvent({ - .type = error->response_type, - .display = display, - .resourceid = error->resource_id, - .serial = error->full_sequence, - .error_code = error->error_code, - .request_code = error->major_code, - .minor_code = error->minor_code, - })); - }, - display_)); - } - - Future(const Future&) = delete; - Future& operator=(const Future&) = delete; - - Future(Future&& future) - : display_(future.display_), sequence_(future.sequence_) { - future.display_ = nullptr; - future.sequence_ = base::nullopt; - } - Future& operator=(Future&& future) { - display_ = future.display_; - sequence_ = future.sequence_; - future.display_ = nullptr; - future.sequence_ = base::nullopt; - } - xcb_connection_t* connection() { return XGetXCBConnection(display_); } + Future() : FutureBase(nullptr, base::nullopt) {} // Blocks until we receive the response from the server. Returns the response. Response<Reply> Sync() { - if (!sequence_) - return {{}, {}}; - Error* raw_error = nullptr; - uint8_t* raw_reply = reinterpret_cast<uint8_t*>( - xcb_wait_for_reply(connection(), *sequence_, &raw_error)); - sequence_ = base::nullopt; + uint8_t* raw_reply = nullptr; + SyncImpl(&raw_error, &raw_reply); std::unique_ptr<Reply> reply; if (raw_reply) { @@ -138,47 +132,37 @@ class Future { // Installs |callback| to be run when the response is received. void OnResponse(Callback callback) { - if (!sequence_) - return; - // This intermediate callback handles the conversion from |raw_reply| to a // real Reply object before feeding the result to |callback|. This means // |callback| must be bound as the first argument of the intermediate // function. - auto wrapper = [](Callback callback, RQ::Reply raw_reply, RQ::Error error) { + auto wrapper = [](Callback callback, RawReply raw_reply, RawError error) { std::unique_ptr<Reply> reply = raw_reply ? detail::ReadReply<Reply>(raw_reply.get()) : nullptr; std::move(callback).Run({std::move(reply), std::move(error)}); }; - EnqueueRequest(base::BindOnce(wrapper, std::move(callback))); + OnResponseImpl(base::BindOnce(wrapper, std::move(callback))); + } - sequence_ = base::nullopt; + void IgnoreError() { + OnResponse(base::BindOnce([](Response<Reply>) {})); } private: template <typename R> - friend Future<R> SendRequest(XDisplay*, std::vector<uint8_t>*); - - Future(XDisplay* display, base::Optional<unsigned int> sequence) - : display_(display), sequence_(sequence) {} - - void EnqueueRequest(RQ::ResponseCallback callback) { - RQ::GetInstance()->AddRequest(std::is_void<Reply>::value, *sequence_, - std::move(callback)); - } + friend Future<R> SendRequest(Connection*, std::vector<uint8_t>*); - XDisplay* display_; - base::Optional<unsigned int> sequence_; + Future(Connection* connection, base::Optional<unsigned int> sequence) + : FutureBase(connection, sequence) {} }; // Sync() specialization for requests that don't generate replies. The returned // response will only contain an error if there was one. template <> inline Response<void> Future<void>::Sync() { - if (!sequence_) - return Response<void>(nullptr); + Error* raw_error = nullptr; + SyncImpl(&raw_error); - Error* raw_error = xcb_request_check(connection(), {*sequence_}); std::unique_ptr<Error, base::FreeDeleter> error; if (raw_error) error.reset(raw_error); @@ -190,18 +174,18 @@ inline Response<void> Future<void>::Sync() { // response argument to |callback| will only contain an error if there was one. template <> inline void Future<void>::OnResponse(Callback callback) { - if (!sequence_) - return; - // See Future<Reply>::OnResponse() for an explanation of why // this wrapper is necessary. - auto wrapper = [](Callback callback, RQ::Reply reply, RQ::Error error) { + auto wrapper = [](Callback callback, RawReply reply, RawError error) { DCHECK(!reply); std::move(callback).Run(Response<void>{std::move(error)}); }; - EnqueueRequest(base::BindOnce(wrapper, std::move(callback))); + OnResponseImpl(base::BindOnce(wrapper, std::move(callback))); +} - sequence_ = base::nullopt; +template <> +inline void Future<void>::IgnoreError() { + OnResponse(base::BindOnce([](Response<void>) {})); } } // namespace x11 diff --git a/chromium/ui/gfx/x/xproto_util.cc b/chromium/ui/gfx/x/xproto_util.cc index 495e4a64221..e08bf4f6b5a 100644 --- a/chromium/ui/gfx/x/xproto_util.cc +++ b/chromium/ui/gfx/x/xproto_util.cc @@ -4,8 +4,10 @@ #include "ui/gfx/x/xproto_util.h" +#include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" +#include "ui/gfx/x/connection.h" #include "ui/gfx/x/xproto.h" namespace x11 { @@ -21,7 +23,7 @@ void LogErrorEventDescription(const XErrorEvent& error_event) { char request_str[256]; XDisplay* dpy = error_event.display; - XProto conn{dpy}; + x11::Connection* conn = x11::Connection::Get(); XGetErrorText(dpy, error_event.error_code, error_str, sizeof(error_str)); strncpy(request_str, "Unknown", sizeof(request_str)); @@ -30,7 +32,7 @@ void LogErrorEventDescription(const XErrorEvent& error_event) { XGetErrorDatabaseText(dpy, "XRequest", num.c_str(), "Unknown", request_str, sizeof(request_str)); } else { - if (auto response = conn.ListExtensions({}).Sync()) { + if (auto response = conn->ListExtensions({}).Sync()) { for (const auto& str : response->names) { int ext_code, first_event, first_error; const char* name = str.name.c_str(); |