910e62b5创建于 1月15日历史提交
// Copyright 2023 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/gfx/x/wm_sync.h"

#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/event.h"

namespace x11 {

namespace {

Window GetWindowForEvent(const Event& event) {
  if (auto* property = event.As<PropertyNotifyEvent>()) {
    return property->window;
  }
  if (auto* reparent = event.As<ReparentNotifyEvent>()) {
    return reparent->window;
  }
  if (auto* configure = event.As<ConfigureNotifyEvent>()) {
    return configure->window;
  }
  return Window::None;
}

}  // namespace

WmSync::WmSync(Connection* connection, base::OnceClosure on_synced)
    : WmSync(connection, std::move(on_synced), connection->CanSyncWithWm()) {}

WmSync::WmSync(Connection* connection,
               base::OnceClosure on_synced,
               bool sync_with_wm)
    : connection_(connection), on_synced_(std::move(on_synced)) {
  if (!sync_with_wm) {
    connection_->GetInputFocus().OnResponse(base::BindOnce(
        &WmSync::OnGetInputFocusResponse, weak_ptr_factory_.GetWeakPtr()));
    connection_->Flush();
    return;
  }

  constexpr EventMask event_mask =
      EventMask::StructureNotify | EventMask::PropertyChange;

  scoped_observation_.Observe(connection_);

  window_ = connection_->GenerateId<Window>();
  connection_->CreateWindow({
      .wid = window_,
      .parent = connection_->default_root(),
      // The window is never mapped, so the position doesn't matter.  However,
      // it's important that the following ConfigureWindow request has different
      // coordinates.
      .x = -10000,
      .y = -10000,
      .width = 10,
      .height = 10,
      .event_mask = event_mask,
  });
  window_events_ = connection_->ScopedSelectEvent(window_, event_mask);

  // Send a ConfigureWindow request. If a WM is running, the request will be
  // routed to the WM for it to service (or if a WM is not running, the X11
  // server will handle it).  Some time later, we'll observe the ConfigureNotify
  // event that signals the sync is complete.
  connection_->ConfigureWindow({
      .window = window_,
      .x = -200,
      .y = -200,
      .width = 20,
      .height = 20,
  });
  connection_->Flush();
}

WmSync::~WmSync() {
  Cleanup();
}

void WmSync::OnEvent(const Event& xevent) {
  if (window_ != Window::None && GetWindowForEvent(xevent) == window_) {
    Cleanup();
    std::move(on_synced_).Run();
    // `this` may be deleted.
  }
}

void WmSync::OnGetInputFocusResponse(GetInputFocusResponse response) {
  Cleanup();
  std::move(on_synced_).Run();
  // `this` may be deleted.
}

void WmSync::Cleanup() {
  if (window_ == Window::None) {
    return;
  }

  scoped_observation_.Reset();
  window_events_.Reset();
  connection_->DestroyWindow(window_);
  window_ = Window::None;
}

}  // namespace x11