kwin/platform.cpp

652 lines
15 KiB
C++
Raw Normal View History

2020-08-02 22:22:19 +00:00
/*
KWin - the KDE window manager
This file is part of the KDE project.
2020-08-02 22:22:19 +00:00
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
2020-08-02 22:22:19 +00:00
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "platform.h"
#include "abstract_output.h"
#include <config-kwin.h>
#include "composite.h"
#include "cursor.h"
#include "effects.h"
#include <KCoreAddons>
#include "overlaywindow.h"
#include "outline.h"
#include "pointer_input.h"
#include "scene.h"
#include "screens.h"
#include "screenedge.h"
#include "wayland_server.h"
[colorcorrection] Night Color - blue light filter at nighttime With Wayland KWin needs to provide certain services, which were provided before that by the Xserver. One of these is gamma correction, which includes the - by many people beloved - functionality to reduce the blue light at nighttime. This patch provides the KWin part of that. It is self contained, but in the end will work in tandem with a lib in Plasma Workspace and a KCM in Plasma Desktop, which can be used to configure Night Color. * Three modi: ** Automatic: The location and sun timings are determined automatically (location data updates will be provided by the workspace) ** Location: The sun timings are determined by fixed location data ** Timings: The sun timings are set manually by the user * Color temperature value changes are smoothly applied: ** Configuration changes, which lead to other current values are changed in a quick way over a few seconds ** Changes on sunrise and sunset are applied slowly over the course of few minutes till several hours depending on the configuration * The current color value is set immediately at startup or after suspend phases and VT switches. There is no flickering. * All configuration is done via a DBus interface, changed values are tested on correctness and applied atomically * Self contained mechanism, speaks directly to the hardware by setting the gamma ramps on the CRTC * Currently working on DRM backend, extensible to other platform backends in the future * The code is written in a way to make the classes later easily extendable to also provide normal color correction, as it's currently done by KGamma on X Test Plan: Manually with the workspace parts and added integration tests in KWin using the virtual backend. BUG:371494 Reviewers: #kwin, graesslin Subscribers: kwin, plasma-devel, #kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D5928
2017-12-11 09:43:12 +00:00
#include <KWaylandServer/outputconfiguration_interface.h>
#include <KWaylandServer/outputchangeset.h>
#include <QX11Info>
2020-04-29 13:54:06 +00:00
#include <cerrno>
namespace KWin
{
2016-04-07 07:18:10 +00:00
Platform::Platform(QObject *parent)
: QObject(parent)
, m_eglDisplay(EGL_NO_DISPLAY)
{
setSoftwareCursorForced(false);
connect(Cursors::self(), &Cursors::currentCursorRendered, this, &Platform::cursorRendered);
}
2016-04-07 07:18:10 +00:00
Platform::~Platform()
{
}
PlatformCursorImage Platform::cursorImage() const
{
Cursor* cursor = Cursors::self()->currentCursor();
return PlatformCursorImage(cursor->image(), cursor->hotspot());
}
void Platform::hideCursor()
{
m_hideCursorCounter++;
if (m_hideCursorCounter == 1) {
doHideCursor();
}
}
void Platform::doHideCursor()
{
}
void Platform::showCursor()
{
m_hideCursorCounter--;
if (m_hideCursorCounter == 0) {
doShowCursor();
}
}
void Platform::doShowCursor()
{
}
2016-04-07 07:18:10 +00:00
OpenGLBackend *Platform::createOpenGLBackend()
{
return nullptr;
}
2016-04-07 07:18:10 +00:00
QPainterBackend *Platform::createQPainterBackend()
{
return nullptr;
}
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
XRenderBackend *Platform::createXRenderBackend()
{
return nullptr;
}
#endif
void Platform::prepareShutdown()
{
setOutputsEnabled(false);
}
Edge *Platform::createScreenEdge(ScreenEdges *edges)
{
return new Edge(edges);
}
void Platform::createPlatformCursor(QObject *parent)
{
new InputRedirectionCursor(parent);
}
void Platform::requestOutputsChange(KWaylandServer::OutputConfigurationInterface *config)
{
if (!m_supportsOutputChanges) {
qCWarning(KWIN_CORE) << "This backend does not support configuration changes.";
config->setFailed();
return;
}
using Enablement = KWaylandServer::OutputDeviceInterface::Enablement;
const auto changes = config->changes();
//process all non-disabling changes
for (auto it = changes.begin(); it != changes.end(); it++) {
const KWaylandServer::OutputChangeSet *changeset = it.value();
AbstractOutput* output = findOutput(it.key()->uuid());
if (!output) {
qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid();
continue;
}
qDebug(KWIN_CORE) << "Platform::requestOutputsChange enabling" << changeset << it.key()->uuid() << changeset->enabledChanged() << (changeset->enabled() == Enablement::Enabled);
if (changeset->enabledChanged() &&
changeset->enabled() == Enablement::Enabled) {
output->setEnabled(true);
}
output->applyChanges(changeset);
}
//process any disable requests
for (auto it = changes.begin(); it != changes.end(); it++) {
const KWaylandServer::OutputChangeSet *changeset = it.value();
if (changeset->enabledChanged() &&
changeset->enabled() == Enablement::Disabled) {
if (enabledOutputs().count() == 1) {
// TODO: check beforehand this condition and set failed otherwise
// TODO: instead create a dummy output?
qCWarning(KWIN_CORE) << "Not disabling final screen" << it.key()->uuid();
continue;
}
auto output = findOutput(it.key()->uuid());
if (!output) {
qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid();
continue;
}
qDebug(KWIN_CORE) << "Platform::requestOutputsChange disabling false" << it.key()->uuid();
output->setEnabled(false);
}
}
emit screens()->changed();
config->setApplied();
}
AbstractOutput *Platform::findOutput(int screenId)
{
return enabledOutputs().value(screenId);
}
AbstractOutput *Platform::findOutput(const QByteArray &uuid)
{
const auto outs = outputs();
auto it = std::find_if(outs.constBegin(), outs.constEnd(),
[uuid](AbstractOutput *output) {
return output->uuid() == uuid; }
);
if (it != outs.constEnd()) {
return *it;
}
return nullptr;
}
bool Platform::usesSoftwareCursor() const
{
return m_softwareCursor;
}
void Platform::setSoftwareCursor(bool set)
{
if (m_softwareCursor == set) {
return;
}
m_softwareCursor = set;
doSetSoftwareCursor();
if (m_softwareCursor) {
connect(Cursors::self(), &Cursors::positionChanged, this, &Platform::triggerCursorRepaint);
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &Platform::triggerCursorRepaint);
} else {
disconnect(Cursors::self(), &Cursors::positionChanged, this, &Platform::triggerCursorRepaint);
disconnect(Cursors::self(), &Cursors::currentCursorChanged, this, &Platform::triggerCursorRepaint);
}
triggerCursorRepaint();
}
void Platform::doSetSoftwareCursor()
{
}
bool Platform::isSoftwareCursorForced() const
{
return m_softwareCursorForced;
}
void Platform::setSoftwareCursorForced(bool forced)
{
if (qEnvironmentVariableIsSet("KWIN_FORCE_SW_CURSOR")) {
forced = true;
}
if (m_softwareCursorForced == forced) {
return;
}
m_softwareCursorForced = forced;
if (m_softwareCursorForced) {
setSoftwareCursor(true);
} else {
// Do not unset the software cursor yet, the platform will choose the right
// moment when it can be done. There is still a chance that we must continue
// using the software cursor.
}
}
2016-04-07 07:18:10 +00:00
void Platform::triggerCursorRepaint()
{
if (!Compositor::self()) {
return;
}
Compositor::self()->addRepaint(m_cursor.lastRenderedGeometry);
Compositor::self()->addRepaint(Cursors::self()->currentCursor()->geometry());
}
void Platform::cursorRendered(const QRect &geometry)
{
if (m_softwareCursor) {
m_cursor.lastRenderedGeometry = geometry;
}
}
2016-04-07 07:18:10 +00:00
void Platform::keyboardKeyPressed(quint32 key, quint32 time)
{
if (!input()) {
return;
}
input()->processKeyboardKey(key, InputRedirection::KeyboardKeyPressed, time);
}
2016-04-07 07:18:10 +00:00
void Platform::keyboardKeyReleased(quint32 key, quint32 time)
{
if (!input()) {
return;
}
input()->processKeyboardKey(key, InputRedirection::KeyboardKeyReleased, time);
}
2016-04-07 07:18:10 +00:00
void Platform::keyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
{
if (!input()) {
return;
}
input()->processKeyboardModifiers(modsDepressed, modsLatched, modsLocked, group);
}
2016-04-07 07:18:10 +00:00
void Platform::keymapChange(int fd, uint32_t size)
{
if (!input()) {
return;
}
input()->processKeymapChange(fd, size);
}
void Platform::pointerAxisHorizontal(qreal delta, quint32 time, qint32 discreteDelta, InputRedirection::PointerAxisSource source)
{
if (!input()) {
return;
}
input()->processPointerAxis(InputRedirection::PointerAxisHorizontal, delta, discreteDelta, source, time);
}
void Platform::pointerAxisVertical(qreal delta, quint32 time, qint32 discreteDelta, InputRedirection::PointerAxisSource source)
{
if (!input()) {
return;
}
input()->processPointerAxis(InputRedirection::PointerAxisVertical, delta, discreteDelta, source, time);
}
2016-04-07 07:18:10 +00:00
void Platform::pointerButtonPressed(quint32 button, quint32 time)
{
if (!input()) {
return;
}
input()->processPointerButton(button, InputRedirection::PointerButtonPressed, time);
}
2016-04-07 07:18:10 +00:00
void Platform::pointerButtonReleased(quint32 button, quint32 time)
{
if (!input()) {
return;
}
input()->processPointerButton(button, InputRedirection::PointerButtonReleased, time);
}
int Platform::touchPointCount()
{
if (!input()) {
return 0;
}
return input()->touchPointCount();
}
2016-04-07 07:18:10 +00:00
void Platform::pointerMotion(const QPointF &position, quint32 time)
{
if (!input()) {
return;
}
input()->processPointerMotion(position, time);
}
void Platform::cancelTouchSequence()
{
if (!input()) {
return;
}
input()->cancelTouchSequence();
}
2016-04-07 07:18:10 +00:00
void Platform::touchCancel()
{
if (!input()) {
return;
}
input()->cancelTouch();
}
2016-04-07 07:18:10 +00:00
void Platform::touchDown(qint32 id, const QPointF &pos, quint32 time)
{
if (!input()) {
return;
}
input()->processTouchDown(id, pos, time);
}
2016-04-07 07:18:10 +00:00
void Platform::touchFrame()
{
if (!input()) {
return;
}
input()->touchFrame();
}
2016-04-07 07:18:10 +00:00
void Platform::touchMotion(qint32 id, const QPointF &pos, quint32 time)
{
if (!input()) {
return;
}
input()->processTouchMotion(id, pos, time);
}
2016-04-07 07:18:10 +00:00
void Platform::touchUp(qint32 id, quint32 time)
{
if (!input()) {
return;
}
input()->processTouchUp(id, time);
}
void Platform::processSwipeGestureBegin(int fingerCount, quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processSwipeGestureBegin(fingerCount, time);
}
void Platform::processSwipeGestureUpdate(const QSizeF &delta, quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processSwipeGestureUpdate(delta, time);
}
void Platform::processSwipeGestureEnd(quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processSwipeGestureEnd(time);
}
void Platform::processSwipeGestureCancelled(quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processSwipeGestureCancelled(time);
}
void Platform::processPinchGestureBegin(int fingerCount, quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processPinchGestureBegin(fingerCount, time);
}
void Platform::processPinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processPinchGestureUpdate(scale, angleDelta, delta, time);
}
void Platform::processPinchGestureEnd(quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processPinchGestureEnd(time);
}
void Platform::processPinchGestureCancelled(quint32 time)
{
if (!input()) {
return;
}
input()->pointer()->processPinchGestureCancelled(time);
}
2016-04-07 07:18:10 +00:00
void Platform::repaint(const QRect &rect)
{
if (!Compositor::self()) {
return;
}
Compositor::self()->addRepaint(rect);
}
2016-04-07 07:18:10 +00:00
void Platform::setReady(bool ready)
{
if (m_ready == ready) {
return;
}
m_ready = ready;
emit readyChanged(m_ready);
}
bool Platform::isPerScreenRenderingEnabled() const
{
return m_isPerScreenRenderingEnabled;
}
void Platform::setPerScreenRenderingEnabled(bool enabled)
{
m_isPerScreenRenderingEnabled = enabled;
}
2020-11-19 08:52:29 +00:00
RenderLoop *Platform::renderLoop() const
{
return nullptr;
}
2016-04-07 07:18:10 +00:00
void Platform::warpPointer(const QPointF &globalPos)
{
Q_UNUSED(globalPos)
}
bool Platform::supportsSurfacelessContext() const
{
Compositor *compositor = Compositor::self();
if (Q_UNLIKELY(!compositor)) {
return false;
}
if (Scene *scene = compositor->scene()) {
return scene->supportsSurfacelessContext();
}
return false;
}
bool Platform::supportsNativeFence() const
{
if (Compositor *compositor = Compositor::self()) {
return compositor->scene()->supportsNativeFence();
}
return false;
}
EGLDisplay KWin::Platform::sceneEglDisplay() const
{
return m_eglDisplay;
}
void Platform::setSceneEglDisplay(EGLDisplay display)
{
m_eglDisplay = display;
}
2016-04-07 07:18:10 +00:00
QSize Platform::screenSize() const
{
return QSize();
}
2016-04-07 07:18:10 +00:00
QVector<QRect> Platform::screenGeometries() const
{
return QVector<QRect>({QRect(QPoint(0, 0), screenSize())});
}
QVector<qreal> Platform::screenScales() const
{
return QVector<qreal>({1});
}
bool Platform::requiresCompositing() const
{
return true;
}
bool Platform::compositingPossible() const
{
return true;
}
QString Platform::compositingNotPossibleReason() const
{
return QString();
}
bool Platform::openGLCompositingIsBroken() const
{
return false;
}
void Platform::createOpenGLSafePoint(OpenGLSafePoint safePoint)
{
Q_UNUSED(safePoint)
}
void Platform::startInteractiveWindowSelection(std::function<void(KWin::Toplevel*)> callback, const QByteArray &cursorName)
{
if (!input()) {
callback(nullptr);
return;
}
input()->startInteractiveWindowSelection(callback, cursorName);
}
void Platform::startInteractivePositionSelection(std::function<void(const QPoint &)> callback)
{
if (!input()) {
callback(QPoint(-1, -1));
return;
}
input()->startInteractivePositionSelection(callback);
}
void Platform::setupActionForGlobalAccel(QAction *action)
{
Q_UNUSED(action)
}
OverlayWindow *Platform::createOverlayWindow()
{
return nullptr;
}
static quint32 monotonicTime()
{
timespec ts;
const int result = clock_gettime(CLOCK_MONOTONIC, &ts);
if (result)
qCWarning(KWIN_CORE, "Failed to query monotonic time: %s", strerror(errno));
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000L;
}
void Platform::updateXTime()
{
switch (kwinApp()->operationMode()) {
case Application::OperationModeX11:
kwinApp()->setX11Time(QX11Info::getTimestamp(), Application::TimestampUpdate::Always);
break;
case Application::OperationModeXwayland:
kwinApp()->setX11Time(monotonicTime(), Application::TimestampUpdate::Always);
break;
default:
// Do not update the current X11 time stamp if it's the Wayland only session.
break;
}
}
OutlineVisual *Platform::createOutline(Outline *outline)
{
if (Compositor::compositing()) {
return new CompositedOutlineVisual(outline);
}
return nullptr;
}
Decoration::Renderer *Platform::createDecorationRenderer(Decoration::DecoratedClientImpl *client)
{
if (Compositor::self()->scene()) {
return Compositor::self()->scene()->createDecorationRenderer(client);
}
return nullptr;
}
void Platform::invertScreen()
{
if (effects) {
if (Effect *inverter = static_cast<EffectsHandlerImpl*>(effects)->provides(Effect::ScreenInversion)) {
qCDebug(KWIN_CORE) << "inverting screen using Effect plugin";
QMetaObject::invokeMethod(inverter, "toggleScreenInversion", Qt::DirectConnection);
}
}
}
void Platform::createEffectsHandler(Compositor *compositor, Scene *scene)
{
new EffectsHandlerImpl(compositor, scene);
}
QString Platform::supportInformation() const
{
return QStringLiteral("Name: %1\n").arg(metaObject()->className());
}
EGLContext Platform::sceneEglGlobalShareContext() const
{
return m_globalShareContext;
}
void Platform::setSceneEglGlobalShareContext(EGLContext context)
{
m_globalShareContext = context;
}
}