blob: 281c318287bd4087a76cd8a746bb26cc1473bf27 [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/gfx/x/xlib_support.h"
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "library_loaders/xlib_loader.h"
#include "library_loaders/xlib_xcb_loader.h"
namespace x11 {
namespace {
int XlibErrorHandler(void*, void*) {
DVLOG(1) << "Xlib error received";
return 0;
}
XlibLoader* GetXlibLoader() {
static base::NoDestructor<XlibLoader> xlib_loader;
return xlib_loader.get();
}
XlibXcbLoader* GetXlibXcbLoader() {
static base::NoDestructor<XlibXcbLoader> xlib_xcb_loader;
return xlib_xcb_loader.get();
}
} // namespace
DISABLE_CFI_ICALL
void InitXlib() {
auto* xlib_loader = GetXlibLoader();
if (xlib_loader->loaded())
return;
CHECK(xlib_loader->Load("libX11.so.6"));
auto* xlib_xcb_loader = GetXlibXcbLoader();
CHECK(xlib_xcb_loader->Load("libX11-xcb.so.1"));
CHECK(xlib_loader->XInitThreads());
// The default Xlib error handler calls exit(1), which we don't want. This
// shouldn't happen in the browser process since only XProto requests are
// made, but in the GPU process, GLX can make Xlib requests, so setting an
// error handler is necessary. Importantly, there's also an IO error handler,
// and Xlib always calls exit(1) with no way to change this behavior.
SetXlibErrorHandler();
}
DISABLE_CFI_ICALL
void SetXlibErrorHandler() {
GetXlibLoader()->XSetErrorHandler(XlibErrorHandler);
}
DISABLE_CFI_ICALL
void XlibFree(void* data) {
GetXlibLoader()->XFree(data);
}
DISABLE_CFI_ICALL
XlibDisplay::XlibDisplay(const std::string& address) {
InitXlib();
display_ = GetXlibLoader()->XOpenDisplay(address.empty() ? nullptr
: address.c_str());
}
DISABLE_CFI_ICALL
XlibDisplay::~XlibDisplay() {
if (!display_)
return;
auto* loader = GetXlibLoader();
// Events are not processed on |display_|, so if any client asks to receive
// events, they will just queue up and leak memory. This check makes sure
// |display_| never had any pending events before it is closed.
CHECK(!loader->XPending(display_));
loader->XCloseDisplay(display_);
}
DISABLE_CFI_ICALL
XlibDisplayWrapper::XlibDisplayWrapper(struct _XDisplay* display,
XlibDisplayType type)
: display_(display), type_(type) {
if (!display_)
return;
if (type == XlibDisplayType::kSyncing)
GetXlibLoader()->XSynchronize(display_, true);
}
DISABLE_CFI_ICALL
XlibDisplayWrapper::~XlibDisplayWrapper() {
if (!display_)
return;
if (type_ == XlibDisplayType::kFlushing)
GetXlibLoader()->XFlush(display_);
else if (type_ == XlibDisplayType::kSyncing)
GetXlibLoader()->XSynchronize(display_, false);
}
XlibDisplayWrapper::XlibDisplayWrapper(XlibDisplayWrapper&& other) {
display_ = other.display_;
type_ = other.type_;
other.display_ = nullptr;
other.type_ = XlibDisplayType::kNormal;
}
XlibDisplayWrapper& XlibDisplayWrapper::operator=(XlibDisplayWrapper&& other) {
display_ = other.display_;
type_ = other.type_;
other.display_ = nullptr;
other.type_ = XlibDisplayType::kNormal;
return *this;
}
DISABLE_CFI_ICALL
struct xcb_connection_t* XlibDisplayWrapper::GetXcbConnection() {
return GetXlibXcbLoader()->XGetXCBConnection(display_);
}
} // namespace x11