blob: 9a90d99effc5dcefe9a0742009e10b23c00ee28d [file] [log] [blame]
// 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/gl/gl_surface_egl_x11.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/base/x/x11_display_util.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/xproto.h"
#include "ui/gfx/x/xproto_util.h"
#include "ui/gl/egl_util.h"
namespace gl {
namespace {
class XrandrIntervalOnlyVSyncProvider : public gfx::VSyncProvider {
public:
explicit XrandrIntervalOnlyVSyncProvider()
: interval_(base::TimeDelta::FromSeconds(1 / 60.)) {}
void GetVSyncParameters(UpdateVSyncCallback callback) override {
if (++calls_since_last_update_ >= kCallsBetweenUpdates) {
calls_since_last_update_ = 0;
interval_ = ui::GetPrimaryDisplayRefreshIntervalFromXrandr();
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), base::TimeTicks(), interval_));
}
bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase,
base::TimeDelta* interval) override {
return false;
}
bool SupportGetVSyncParametersIfAvailable() const override { return false; }
bool IsHWClock() const override { return false; }
private:
base::TimeDelta interval_;
static const int kCallsBetweenUpdates = 100;
int calls_since_last_update_ = kCallsBetweenUpdates;
};
} // namespace
NativeViewGLSurfaceEGLX11::NativeViewGLSurfaceEGLX11(x11::Window window)
: NativeViewGLSurfaceEGL(static_cast<uint32_t>(window), nullptr) {}
bool NativeViewGLSurfaceEGLX11::Initialize(GLSurfaceFormat format) {
if (!NativeViewGLSurfaceEGL::Initialize(format))
return false;
// Query all child windows and store them. ANGLE creates a child window when
// eglCreateWidnowSurface is called on X11 and expose events from this window
// need to be received by this class.
if (auto reply = x11::Connection::Get()
->QueryTree({static_cast<x11::Window>(window_)})
.Sync()) {
children_ = std::move(reply->children);
}
if (ui::X11EventSource::HasInstance()) {
dispatcher_set_ = true;
ui::X11EventSource::GetInstance()->AddXEventDispatcher(this);
}
return true;
}
void NativeViewGLSurfaceEGLX11::Destroy() {
NativeViewGLSurfaceEGL::Destroy();
if (dispatcher_set_ && ui::X11EventSource::HasInstance())
ui::X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
}
gfx::SwapResult NativeViewGLSurfaceEGLX11::SwapBuffers(
PresentationCallback callback) {
auto result = NativeViewGLSurfaceEGL::SwapBuffers(std::move(callback));
if (result == gfx::SwapResult::SWAP_FAILED)
return result;
// We need to restore the background pixel that we set to WhitePixel on
// views::DesktopWindowTreeHostX11::InitX11Window back to None for the
// XWindow associated to this surface after the first SwapBuffers has
// happened, to avoid showing a weird white background while resizing.
if (GetXNativeConnection()->Ready() && !has_swapped_buffers_) {
GetXNativeConnection()->ChangeWindowAttributes({
.window = static_cast<x11::Window>(window_),
.background_pixmap = x11::Pixmap::None,
});
GetXNativeConnection()->Flush();
has_swapped_buffers_ = true;
}
return result;
}
NativeViewGLSurfaceEGLX11::~NativeViewGLSurfaceEGLX11() {
Destroy();
}
x11::Connection* NativeViewGLSurfaceEGLX11::GetXNativeConnection() const {
return x11::Connection::Get();
}
std::unique_ptr<gfx::VSyncProvider>
NativeViewGLSurfaceEGLX11::CreateVsyncProviderInternal() {
return std::make_unique<XrandrIntervalOnlyVSyncProvider>();
}
bool NativeViewGLSurfaceEGLX11::DispatchXEvent(x11::Event* x11_event) {
// When ANGLE is used for EGL, it creates an X11 child window. Expose events
// from this window need to be forwarded to this class.
auto* expose = x11_event->As<x11::ExposeEvent>();
bool can_dispatch = expose && std::find(children_.begin(), children_.end(),
expose->window) != children_.end();
if (!can_dispatch)
return false;
auto expose_copy = *expose;
auto window = static_cast<x11::Window>(window_);
expose_copy.window = window;
x11::SendEvent(expose_copy, window, x11::EventMask::Exposure);
x11::Connection::Get()->Flush();
return true;
}
} // namespace gl