You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.4 KiB
206 lines
5.4 KiB
/* |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
#include "x11_windowed_output.h" |
|
|
|
#include <config-kwin.h> |
|
|
|
#include "renderloop_p.h" |
|
#include "softwarevsyncmonitor.h" |
|
#include "x11_windowed_backend.h" |
|
|
|
#include <NETWM> |
|
|
|
#if HAVE_X11_XINPUT |
|
#include <X11/extensions/XInput2.h> |
|
#endif |
|
|
|
#include <QIcon> |
|
|
|
namespace KWin |
|
{ |
|
|
|
X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend) |
|
: Output(backend) |
|
, m_renderLoop(std::make_unique<RenderLoop>()) |
|
, m_vsyncMonitor(SoftwareVsyncMonitor::create()) |
|
, m_backend(backend) |
|
{ |
|
m_window = xcb_generate_id(m_backend->connection()); |
|
|
|
static int identifier = -1; |
|
identifier++; |
|
setInformation(Information{ |
|
.name = QStringLiteral("X11-%1").arg(identifier), |
|
}); |
|
|
|
connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &X11WindowedOutput::vblank); |
|
} |
|
|
|
X11WindowedOutput::~X11WindowedOutput() |
|
{ |
|
xcb_unmap_window(m_backend->connection(), m_window); |
|
xcb_destroy_window(m_backend->connection(), m_window); |
|
xcb_flush(m_backend->connection()); |
|
} |
|
|
|
QRegion X11WindowedOutput::exposedArea() const |
|
{ |
|
return m_exposedArea; |
|
} |
|
|
|
void X11WindowedOutput::addExposedArea(const QRect &rect) |
|
{ |
|
m_exposedArea += rect; |
|
} |
|
|
|
void X11WindowedOutput::clearExposedArea() |
|
{ |
|
m_exposedArea = QRegion(); |
|
} |
|
|
|
RenderLoop *X11WindowedOutput::renderLoop() const |
|
{ |
|
return m_renderLoop.get(); |
|
} |
|
|
|
SoftwareVsyncMonitor *X11WindowedOutput::vsyncMonitor() const |
|
{ |
|
return m_vsyncMonitor.get(); |
|
} |
|
|
|
void X11WindowedOutput::init(const QSize &pixelSize) |
|
{ |
|
const int refreshRate = 60000; // TODO: get refresh rate via randr |
|
m_renderLoop->setRefreshRate(refreshRate); |
|
m_vsyncMonitor->setRefreshRate(refreshRate); |
|
|
|
resize(pixelSize); |
|
setScale(m_backend->initialOutputScale()); |
|
|
|
const uint32_t eventMask = XCB_EVENT_MASK_KEY_PRESS |
|
| XCB_EVENT_MASK_KEY_RELEASE |
|
| XCB_EVENT_MASK_BUTTON_PRESS |
|
| XCB_EVENT_MASK_BUTTON_RELEASE |
|
| XCB_EVENT_MASK_POINTER_MOTION |
|
| XCB_EVENT_MASK_ENTER_WINDOW |
|
| XCB_EVENT_MASK_LEAVE_WINDOW |
|
| XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
| XCB_EVENT_MASK_EXPOSURE; |
|
|
|
const uint32_t values[] = { |
|
m_backend->screen()->black_pixel, |
|
eventMask, |
|
}; |
|
|
|
uint32_t valueMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; |
|
|
|
xcb_create_window(m_backend->connection(), |
|
XCB_COPY_FROM_PARENT, |
|
m_window, |
|
m_backend->screen()->root, |
|
0, 0, |
|
pixelSize.width(), pixelSize.height(), |
|
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, |
|
valueMask, values); |
|
|
|
// select xinput 2 events |
|
initXInputForWindow(); |
|
|
|
m_winInfo = std::make_unique<NETWinInfo>(m_backend->connection(), m_window, m_backend->screen()->root, |
|
NET::WMWindowType, NET::Properties2()); |
|
|
|
m_winInfo->setWindowType(NET::Normal); |
|
m_winInfo->setPid(QCoreApplication::applicationPid()); |
|
QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin")); |
|
auto addIcon = [&windowIcon, this](const QSize &size) { |
|
if (windowIcon.actualSize(size) != size) { |
|
return; |
|
} |
|
NETIcon icon; |
|
QImage windowImage = windowIcon.pixmap(size).toImage(); |
|
icon.data = windowImage.bits(); |
|
icon.size.width = size.width(); |
|
icon.size.height = size.height(); |
|
m_winInfo->setIcon(icon, false); |
|
}; |
|
addIcon(QSize(16, 16)); |
|
addIcon(QSize(32, 32)); |
|
addIcon(QSize(48, 48)); |
|
|
|
xcb_map_window(m_backend->connection(), m_window); |
|
} |
|
|
|
void X11WindowedOutput::initXInputForWindow() |
|
{ |
|
if (!m_backend->hasXInput()) { |
|
return; |
|
} |
|
#if HAVE_X11_XINPUT |
|
XIEventMask evmasks[1]; |
|
unsigned char mask1[XIMaskLen(XI_LASTEVENT)]; |
|
|
|
memset(mask1, 0, sizeof(mask1)); |
|
XISetMask(mask1, XI_TouchBegin); |
|
XISetMask(mask1, XI_TouchUpdate); |
|
XISetMask(mask1, XI_TouchOwnership); |
|
XISetMask(mask1, XI_TouchEnd); |
|
evmasks[0].deviceid = XIAllMasterDevices; |
|
evmasks[0].mask_len = sizeof(mask1); |
|
evmasks[0].mask = mask1; |
|
XISelectEvents(m_backend->display(), m_window, evmasks, 1); |
|
#endif |
|
} |
|
|
|
void X11WindowedOutput::resize(const QSize &pixelSize) |
|
{ |
|
auto mode = std::make_shared<OutputMode>(pixelSize, m_renderLoop->refreshRate()); |
|
setModesInternal({mode}, mode); |
|
} |
|
|
|
void X11WindowedOutput::setWindowTitle(const QString &title) |
|
{ |
|
m_winInfo->setName(title.toUtf8().constData()); |
|
} |
|
|
|
QPoint X11WindowedOutput::internalPosition() const |
|
{ |
|
return geometry().topLeft(); |
|
} |
|
|
|
void X11WindowedOutput::setHostPosition(const QPoint &pos) |
|
{ |
|
m_hostPosition = pos; |
|
} |
|
|
|
QPointF X11WindowedOutput::mapFromGlobal(const QPointF &pos) const |
|
{ |
|
return (pos - hostPosition() + internalPosition()) / scale(); |
|
} |
|
|
|
void X11WindowedOutput::vblank(std::chrono::nanoseconds timestamp) |
|
{ |
|
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop.get()); |
|
renderLoopPrivate->notifyFrameCompleted(timestamp); |
|
} |
|
|
|
bool X11WindowedOutput::usesSoftwareCursor() const |
|
{ |
|
return false; |
|
} |
|
|
|
void X11WindowedOutput::updateEnablement(bool enabled) |
|
{ |
|
if (enabled) { |
|
Q_EMIT m_backend->outputEnabled(this); |
|
} else { |
|
Q_EMIT m_backend->outputDisabled(this); |
|
} |
|
} |
|
|
|
} // namespace KWin
|
|
|