1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/wayland_bubble.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
#include "ui/platform_window/platform_window_init_properties.h"
namespace ui {
WaylandBubble::WaylandBubble(PlatformWindowDelegate* delegate,
WaylandConnection* connection,
WaylandWindow* parent)
: WaylandWindow(delegate, connection) {
set_parent_window(parent);
parent->AddBubble(this);
}
WaylandBubble::~WaylandBubble() {
if (parent_window()) {
parent_window()->RemoveBubble(this);
}
}
void WaylandBubble::Show(bool inactive) {
if (subsurface_) {
return;
}
UpdateWindowScale(false);
AddToParentAsSubsurface();
WaylandWindow::Show(inactive);
}
void WaylandBubble::Hide() {
if (!subsurface_) {
return;
}
WaylandWindow::Hide();
Deactivate();
subsurface_.reset();
connection()->Flush();
}
bool WaylandBubble::IsVisible() const {
return !!subsurface_;
}
void WaylandBubble::SetBoundsInDIP(const gfx::Rect& bounds_dip) {
// TODO(crbug.com/329145822): There will be occasional visual inconsistencies
// when scale factor changes due to async, either the size, or the offset.
// The toplevel parent and bubble are submitted in separate compositor frames.
// There is currently no guarantee that the 2 compositor frames arrive
// together atomically.
auto old_bounds_dip = GetBoundsInDIP();
WaylandWindow::SetBoundsInDIP(bounds_dip);
// TODO(crbug.com/329145822): Don't apply position immediately here, wait for
// ackconfigure, otherwise it might jitter if offset changes.
if (subsurface_ && old_bounds_dip != bounds_dip) {
SetSubsurfacePosition();
}
}
void WaylandBubble::SetInputRegion(
std::optional<std::vector<gfx::Rect>> region_px) {
if (accept_events_) {
root_surface()->set_input_region(region_px);
}
}
void WaylandBubble::Activate() {
if (subsurface_ && activatable_ && parent_window()) {
parent_window()->ActivateBubble(this);
}
WaylandWindow::Activate();
}
void WaylandBubble::Deactivate() {
if (IsActive()) {
parent_window()->ActivateBubble(nullptr);
}
WaylandWindow::Deactivate();
}
void WaylandBubble::UpdateWindowScale(bool update_bounds) {
WaylandWindow::UpdateWindowScale(update_bounds);
if (subsurface_) {
SetSubsurfacePosition();
}
}
void WaylandBubble::OnSequencePoint(int64_t seq) {
if (!subsurface_) {
return;
}
ProcessSequencePoint(seq);
MaybeApplyLatestStateRequest(/*force=*/false);
}
base::WeakPtr<WaylandWindow> WaylandBubble::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
bool WaylandBubble::IsActive() const {
bool is_active = activatable_ && parent_window() &&
parent_window()->IsActive() &&
parent_window()->active_bubble() == this;
CHECK(activatable_ || !is_active);
return is_active;
}
WaylandBubble* WaylandBubble::AsWaylandBubble() {
return this;
}
void WaylandBubble::AddToParentAsSubsurface() {
CHECK(parent_window());
// We need to make sure that window scale matches the parent window.
UpdateWindowScale(true);
subsurface_ =
root_surface()->CreateSubsurface(parent_window()->root_surface());
DCHECK(subsurface_);
SetSubsurfacePosition();
// The associaded widget of this platform_window has a ui::Compositor and
// receives BeginFrames separately from its parent widget. When this window
// commits a frame, it should not require a parent wl_surface commit for the
// frame to be visible.
wl_subsurface_set_desync(subsurface_.get());
// wl_subsurface will not receive configure events from server. As soon as the
// wl_subusrface handle is created, it is considered as configured. Need to
// manually set `received_configure_event_` so `frame_manager_` can start
// processing frames.
OnSurfaceConfigureEvent();
// Notify the observers the window has been configured. Please note that
// subsurface doesn't send ack configure events. Thus, notify the observers as
// soon as the subsurface is created.
connection()->window_manager()->NotifyWindowConfigured(this);
}
void WaylandBubble::SetSubsurfacePosition() {
// TODO(crbug.com/369213517): Handle ui scale before enabling this on Linux.
const auto bounds_dip_in_parent =
wl::TranslateWindowBoundsToParentDIP(this, parent_window());
wl_subsurface_set_position(subsurface_.get(), bounds_dip_in_parent.x(),
bounds_dip_in_parent.y());
parent_window()->root_surface()->Commit();
}
bool WaylandBubble::OnInitialize(PlatformWindowInitProperties properties,
PlatformWindowDelegate::State* state) {
DCHECK(parent_window());
// `window_state` is always `kNormal` on WaylandBubble.
state->window_state = PlatformWindowState::kNormal;
state->window_scale = parent_window()->applied_state().window_scale;
activatable_ = properties.activatable;
accept_events_ = properties.accept_events;
// For now kBubbles draw their own shadow, not setting input_region means
// shadow trap input as well.
if (!accept_events_) {
root_surface()->set_input_region(
std::optional<std::vector<gfx::Rect>>({gfx::Rect()}));
}
return true;
}
bool WaylandBubble::IsSurfaceConfigured() {
// Server will generate unconfigured_buffer error if we attach a buffer to an
// unconfigured surface for toplevel/popup window. WaylandBubble uses
// wl_subsurface role, so it is treated as configured as long as it has a
// wl_subsurface handle.
return parent_window() && parent_window()->IsSurfaceConfigured() &&
subsurface_;
}
void WaylandBubble::UpdateWindowMask() {
// Bubble window doesn't have a shape. Update the opaqueness.
auto region = IsOpaqueWindow() ? std::optional<std::vector<gfx::Rect>>(
{gfx::Rect(latched_state().size_px)})
: std::nullopt;
root_surface()->set_opaque_region(region);
}
} // namespace ui
|