blob: f241d6c23855fad478813ff3029fa6a17d084d34 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GFX_X_WINDOW_CACHE_H_
#define UI_GFX_X_WINDOW_CACHE_H_
#include <memory>
#include <unordered_map>
#include <vector>
#include "base/component_export.h"
#include "base/containers/circular_deque.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/future.h"
#include "ui/gfx/x/shape.h"
#include "ui/gfx/x/xproto.h"
namespace x11 {
COMPONENT_EXPORT(X11)
Window GetWindowAtPoint(const gfx::Point& point_px,
const base::flat_set<Window>* ignore = nullptr);
class Connection;
class XScopedEventSelector;
class ScopedShapeEventSelector {
public:
ScopedShapeEventSelector(Connection* connection, Window window);
~ScopedShapeEventSelector();
private:
const raw_ptr<Connection> connection_;
const Window window_;
};
// Maintains a cache of the state of all X11 windows.
class COMPONENT_EXPORT(X11) WindowCache : public EventObserver {
public:
struct WindowInfo {
WindowInfo();
~WindowInfo();
Window parent = Window::None;
bool mapped = false;
// Properties
bool has_wm_name = false;
gfx::Insets gtk_frame_extents_px;
int16_t x_px = 0;
int16_t y_px = 0;
uint16_t width_px = 0;
uint16_t height_px = 0;
uint16_t border_width_px = 0;
// Child windows listed in lowest-to-highest stacking order.
// Although it is possible to restack windows, it is uncommon to do so,
// so we store children in a vector instead of a node-based structure.
std::vector<Window> children;
absl::optional<std::vector<Rectangle>> bounding_rects_px;
absl::optional<std::vector<Rectangle>> input_rects_px;
std::unique_ptr<XScopedEventSelector> events;
std::unique_ptr<ScopedShapeEventSelector> shape_events;
};
static WindowCache* instance() { return instance_; }
// If `track_events` is true, the WindowCache will keep the cache state synced
// with the server's state over time. It may be set to false if the cache is
// short-lived, if only a single GetWindowAtPoint call is made.
WindowCache(Connection* connection, Window root);
WindowCache(const WindowCache&) = delete;
WindowCache& operator=(const WindowCache&) = delete;
~WindowCache() override;
// Returns the window at the specified point or Window::None if no match could
// be found. `point_px` is in coordinates of the parent of `window`.
Window GetWindowAtPoint(gfx::Point point_px,
Window window,
const base::flat_set<Window>* ignore = nullptr);
// Blocks until all outstanding requests are processed.
void WaitUntilReady();
// Destroys |self| if no calls to GetWindowAtPoint() are made within
// a time window.
void BeginDestroyTimer(std::unique_ptr<WindowCache> self);
void SyncForTest();
const std::unordered_map<Window, WindowInfo>& windows() const {
return windows_;
}
private:
// This helper reduces boilerplate when adding requests.
template <typename Future, typename Callback, typename... Args>
void AddRequest(Future&& future, Callback&& callback, Args&&... args) {
future.OnResponse(base::BindOnce(callback, weak_factory_.GetWeakPtr(),
std::forward<Args>(args)...));
pending_requests_.push_back(std::move(future));
}
// EventObserver:
void OnEvent(const Event& event) override;
// Start caching the window tree starting at `window`. `parent` is set as
// the initial parent in the cache state.
void AddWindow(Window window, Window parent);
// Returns the WindowInfo for `window` or nullptr if `window` is not cached.
WindowInfo* GetInfo(Window window);
// Returns a vector of child windows or nullptr if `window` is not cached.
std::vector<Window>* GetChildren(Window window);
// Makes a GetProperty request with a callback to OnGetPropertyResponse().
void GetProperty(Window window, Atom property, uint32_t length);
// Common response handler that's called at the beginning of each On*Response.
// Returns the WindowInfo for `window` or nullptr if `window` is not cached
// or `has_reply` is false.
WindowInfo* OnResponse(Window window, bool has_reply);
void OnGetWindowAttributesResponse(Window window,
GetWindowAttributesResponse response);
void OnGetGeometryResponse(Window window, GetGeometryResponse response);
void OnQueryTreeResponse(Window window, QueryTreeResponse response);
void OnGetPropertyResponse(Window window,
Atom atom,
GetPropertyResponse response);
void OnGetRectanglesResponse(Window window,
Shape::Sk kind,
Shape::GetRectanglesResponse response);
void OnDestroyTimerExpired(std::unique_ptr<WindowCache> self);
static WindowCache* instance_;
const raw_ptr<Connection> connection_;
const Window root_;
const Atom gtk_frame_extents_;
std::unique_ptr<XScopedEventSelector> root_events_;
std::unordered_map<Window, WindowInfo> windows_;
base::circular_deque<FutureBase> pending_requests_;
// The latest event processed out-of-order, or nullopt if the latest event was
// processed in order.
absl::optional<uint32_t> last_processed_event_;
// True iff GetWindowAtPoint() was called since the last timer interval.
bool delete_when_destroy_timer_fires_ = false;
// Although only one instance of WindowCache may be created at a time, the
// instance will be created and destroyed as needed, so WeakPtrs are still
// necessary.
base::WeakPtrFactory<WindowCache> weak_factory_{this};
};
} // namespace x11
#endif // UI_GFX_X_WINDOW_CACHE_H_