summaryrefslogtreecommitdiff
path: root/chromium/ui/gfx/x
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/ui/gfx/x
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-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.gn117
-rw-r--r--chromium/ui/gfx/x/connection.cc222
-rw-r--r--chromium/ui/gfx/x/connection.h100
-rw-r--r--chromium/ui/gfx/x/connection_unittest.cc108
-rw-r--r--chromium/ui/gfx/x/event.cc103
-rw-r--r--chromium/ui/gfx/x/event.h92
-rw-r--r--chromium/ui/gfx/x/gen_xproto.py1124
-rw-r--r--chromium/ui/gfx/x/request_queue.cc29
-rw-r--r--chromium/ui/gfx/x/request_queue.h51
-rw-r--r--chromium/ui/gfx/x/x11.h21
-rw-r--r--chromium/ui/gfx/x/x11_atom_cache.cc151
-rw-r--r--chromium/ui/gfx/x/x11_atom_cache.h8
-rw-r--r--chromium/ui/gfx/x/x11_error_tracker.cc6
-rw-r--r--chromium/ui/gfx/x/x11_path.cc29
-rw-r--r--chromium/ui/gfx/x/x11_path.h8
-rw-r--r--chromium/ui/gfx/x/x11_types.cc6
-rw-r--r--chromium/ui/gfx/x/x11_types.h3
-rw-r--r--chromium/ui/gfx/x/xproto_internal.h104
-rw-r--r--chromium/ui/gfx/x/xproto_types.cc76
-rw-r--r--chromium/ui/gfx/x/xproto_types.h138
-rw-r--r--chromium/ui/gfx/x/xproto_util.cc6
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();