kwin/src/effects.cpp

2540 lines
74 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: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
2020-08-02 22:22:19 +00:00
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "effects.h"
#include <config-kwin.h>
#include "effectloader.h"
#include "effectsadaptor.h"
#include "output.h"
#if KWIN_BUILD_ACTIVITIES
#include "activities.h"
#endif
#include "cursor.h"
#include "deleted.h"
#include "group.h"
#include "input_event.h"
#include "internalwindow.h"
#include "osd.h"
#include "pointer_input.h"
#include "renderbackend.h"
#include "unmanaged.h"
#include "x11window.h"
#if KWIN_BUILD_TABBOX
#include "tabbox.h"
#endif
#include "screenedge.h"
#include "screens.h"
#include "scripting/scriptedeffect.h"
#if KWIN_BUILD_SCREENLOCKER
#include "screenlockerwatcher.h"
#endif
#include "composite.h"
#include "decorations/decorationbridge.h"
#include "inputmethod.h"
#include "inputpanelv1window.h"
#include "kwinglutils.h"
#include "kwinoffscreenquickview.h"
#include "platform.h"
2022-01-13 13:54:03 +00:00
#include "utils/xcbutils.h"
#include "virtualdesktops.h"
#include "wayland_server.h"
#include "waylandwindow.h"
#include "window_property_notify_x11_filter.h"
#include "workspace.h"
#include <Plasma/Theme>
#include <KDecoration2/Decoration>
#include <KDecoration2/DecorationSettings>
#include <QDebug>
#include <QMouseEvent>
#include <QWheelEvent>
namespace KWin
{
//---------------------
// Static
static QByteArray readWindowProperty(xcb_window_t win, xcb_atom_t atom, xcb_atom_t type, int format)
2011-01-30 14:34:42 +00:00
{
if (win == XCB_WINDOW_NONE) {
return QByteArray();
}
uint32_t len = 32768;
2011-01-30 14:34:42 +00:00
for (;;) {
Xcb::Property prop(false, win, atom, XCB_ATOM_ANY, 0, len);
if (prop.isNull()) {
// get property failed
return QByteArray();
}
if (prop->bytes_after > 0) {
len *= 2;
continue;
}
return prop.toByteArray(format, type);
}
2011-01-30 14:34:42 +00:00
}
static void deleteWindowProperty(xcb_window_t win, long int atom)
2011-01-30 14:34:42 +00:00
{
if (win == XCB_WINDOW_NONE) {
return;
}
xcb_delete_property(kwinApp()->x11Connection(), win, atom);
2011-01-30 14:34:42 +00:00
}
static xcb_atom_t registerSupportProperty(const QByteArray &propertyName)
{
auto c = kwinApp()->x11Connection();
if (!c) {
return XCB_ATOM_NONE;
}
// get the atom for the propertyName
ScopedCPointer<xcb_intern_atom_reply_t> atomReply(xcb_intern_atom_reply(c,
xcb_intern_atom_unchecked(c, false, propertyName.size(), propertyName.constData()),
nullptr));
if (atomReply.isNull()) {
return XCB_ATOM_NONE;
}
// announce property on root window
unsigned char dummy = 0;
xcb_change_property(c, XCB_PROP_MODE_REPLACE, kwinApp()->x11RootWindow(), atomReply->atom, atomReply->atom, 8, 1, &dummy);
// TODO: add to _NET_SUPPORTED
return atomReply->atom;
}
//---------------------
EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
: EffectsHandler(Compositor::self()->backend()->compositingType())
, keyboard_grab_effect(nullptr)
, fullscreen_effect(nullptr)
, m_compositor(compositor)
, m_scene(scene)
, m_effectLoader(new EffectLoader(this))
, m_trackingCursorChanges(0)
2011-01-30 14:34:42 +00:00
{
qRegisterMetaType<QVector<KWin::EffectWindow *>>();
connect(m_effectLoader, &AbstractEffectLoader::effectLoaded, this, [this](Effect *effect, const QString &name) {
effect_order.insert(effect->requestedEffectChainPosition(), EffectPair(name, effect));
loaded_effects << EffectPair(name, effect);
effectsChanged();
});
m_effectLoader->setConfig(kwinApp()->config());
new EffectsAdaptor(this);
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.registerObject(QStringLiteral("/Effects"), this);
Workspace *ws = Workspace::self();
VirtualDesktopManager *vds = VirtualDesktopManager::self();
connect(ws, &Workspace::showingDesktopChanged, this, [this](bool showing, bool animated) {
if (animated) {
Q_EMIT showingDesktopChanged(showing);
}
});
2022-04-22 17:39:12 +00:00
connect(ws, &Workspace::currentDesktopChanged, this, [this](int old, Window *c) {
2022-04-15 12:23:44 +00:00
const int newDesktop = VirtualDesktopManager::self()->current();
if (old != 0 && newDesktop != old) {
Q_EMIT desktopChanged(old, newDesktop, c ? c->effectWindow() : nullptr);
// TODO: remove in 4.10
Q_EMIT desktopChanged(old, newDesktop);
}
2022-04-15 12:23:44 +00:00
});
2022-04-22 17:39:12 +00:00
connect(ws, &Workspace::currentDesktopChanging, this, [this](uint currentDesktop, QPointF offset, KWin::Window *c) {
2022-04-15 12:23:44 +00:00
Q_EMIT desktopChanging(currentDesktop, offset, c ? c->effectWindow() : nullptr);
});
connect(ws, &Workspace::currentDesktopChangingCancelled, this, [this]() {
Q_EMIT desktopChangingCancelled();
});
2022-04-22 17:39:12 +00:00
connect(ws, &Workspace::desktopPresenceChanged, this, [this](Window *c, int old) {
if (!c->effectWindow()) {
return;
}
Q_EMIT desktopPresenceChanged(c->effectWindow(), old, c->desktop());
});
connect(ws, &Workspace::windowAdded, this, [this](Window *c) {
if (c->readyForPainting()) {
slotClientShown(c);
} else {
2022-04-22 17:39:12 +00:00
connect(c, &Window::windowShown, this, &EffectsHandlerImpl::slotClientShown);
}
});
connect(ws, &Workspace::unmanagedAdded, this, [this](Unmanaged *u) {
// it's never initially ready but has synthetic 50ms delay
2022-04-22 17:39:12 +00:00
connect(u, &Window::windowShown, this, &EffectsHandlerImpl::slotUnmanagedShown);
});
connect(ws, &Workspace::internalWindowAdded, this, [this](InternalWindow *client) {
setupClientConnections(client);
Q_EMIT windowAdded(client->effectWindow());
});
connect(ws, &Workspace::windowActivated, this, [this](KWin::Window *c) {
Q_EMIT windowActivated(c ? c->effectWindow() : nullptr);
});
connect(ws, &Workspace::deletedRemoved, this, [this](KWin::Deleted *d) {
Q_EMIT windowDeleted(d->effectWindow());
elevated_windows.removeAll(d->effectWindow());
});
connect(ws->sessionManager(), &SessionManager::stateChanged, this, &KWin::EffectsHandler::sessionStateChanged);
connect(vds, &VirtualDesktopManager::countChanged, this, &EffectsHandler::numberDesktopsChanged);
connect(Cursors::self()->mouse(), &Cursor::mouseChanged, this, &EffectsHandler::mouseChanged);
connect(Screens::self(), &Screens::sizeChanged, this, &EffectsHandler::virtualScreenSizeChanged);
connect(Screens::self(), &Screens::geometryChanged, this, &EffectsHandler::virtualScreenGeometryChanged);
#if KWIN_BUILD_ACTIVITIES
if (Activities *activities = Activities::self()) {
connect(activities, &Activities::added, this, &EffectsHandler::activityAdded);
connect(activities, &Activities::removed, this, &EffectsHandler::activityRemoved);
connect(activities, &Activities::currentChanged, this, &EffectsHandler::currentActivityChanged);
}
#endif
connect(ws, &Workspace::stackingOrderChanged, this, &EffectsHandler::stackingOrderChanged);
#if KWIN_BUILD_TABBOX
TabBox::TabBox *tabBox = TabBox::TabBox::self();
connect(tabBox, &TabBox::TabBox::tabBoxAdded, this, &EffectsHandler::tabBoxAdded);
connect(tabBox, &TabBox::TabBox::tabBoxUpdated, this, &EffectsHandler::tabBoxUpdated);
connect(tabBox, &TabBox::TabBox::tabBoxClosed, this, &EffectsHandler::tabBoxClosed);
connect(tabBox, &TabBox::TabBox::tabBoxKeyEvent, this, &EffectsHandler::tabBoxKeyEvent);
#endif
connect(ScreenEdges::self(), &ScreenEdges::approaching, this, &EffectsHandler::screenEdgeApproaching);
#if KWIN_BUILD_SCREENLOCKER
connect(ScreenLockerWatcher::self(), &ScreenLockerWatcher::locked, this, &EffectsHandler::screenLockingChanged);
connect(ScreenLockerWatcher::self(), &ScreenLockerWatcher::aboutToLock, this, &EffectsHandler::screenAboutToLock);
#endif
connect(kwinApp(), &Application::x11ConnectionChanged, this, [this]() {
registered_atoms.clear();
for (auto it = m_propertiesForEffects.keyBegin(); it != m_propertiesForEffects.keyEnd(); it++) {
const auto atom = registerSupportProperty(*it);
if (atom == XCB_ATOM_NONE) {
continue;
}
m_compositor->keepSupportProperty(atom);
m_managedProperties.insert(*it, atom);
registerPropertyType(atom, true);
}
if (kwinApp()->x11Connection()) {
m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
} else {
m_x11WindowPropertyNotify.reset();
}
Q_EMIT xcbConnectionChanged();
});
if (kwinApp()->x11Connection()) {
m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
}
// connect all clients
2022-04-22 17:39:12 +00:00
for (Window *client : ws->allClientList()) {
if (client->readyForPainting()) {
setupClientConnections(client);
} else {
2022-04-22 17:39:12 +00:00
connect(client, &Window::windowShown, this, &EffectsHandlerImpl::slotClientShown);
}
}
for (Unmanaged *u : ws->unmanagedList()) {
setupUnmanagedConnections(u);
}
for (InternalWindow *client : ws->internalWindows()) {
setupClientConnections(client);
}
connect(kwinApp()->platform(), &Platform::outputEnabled, this, &EffectsHandlerImpl::slotOutputEnabled);
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &EffectsHandlerImpl::slotOutputDisabled);
const QVector<Output *> outputs = kwinApp()->platform()->enabledOutputs();
for (Output *output : outputs) {
slotOutputEnabled(output);
}
connect(InputMethod::self(), &InputMethod::panelChanged, this, &EffectsHandlerImpl::inputPanelChanged);
reconfigure();
2011-01-30 14:34:42 +00:00
}
EffectsHandlerImpl::~EffectsHandlerImpl()
{
unloadAllEffects();
}
void EffectsHandlerImpl::unloadAllEffects()
2011-01-30 14:34:42 +00:00
{
for (const EffectPair &pair : qAsConst(loaded_effects)) {
destroyEffect(pair.second);
}
effect_order.clear();
m_effectLoader->clear();
effectsChanged();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::setupClientConnections(Window *c)
{
2022-04-22 17:39:12 +00:00
connect(c, &Window::windowClosed, this, &EffectsHandlerImpl::slotWindowClosed);
connect(c, static_cast<void (Window::*)(KWin::Window *, MaximizeMode)>(&Window::clientMaximizedStateChanged),
this, &EffectsHandlerImpl::slotClientMaximized);
2022-04-22 17:39:12 +00:00
connect(c, &Window::clientStartUserMovedResized, this, [this](Window *c) {
Q_EMIT windowStartUserMovedResized(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::clientStepUserMovedResized, this, [this](Window *c, const QRect &geometry) {
Q_EMIT windowStepUserMovedResized(c->effectWindow(), geometry);
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::clientFinishUserMovedResized, this, [this](Window *c) {
Q_EMIT windowFinishUserMovedResized(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::opacityChanged, this, &EffectsHandlerImpl::slotOpacityChanged);
connect(c, &Window::clientMinimized, this, [this](Window *c, bool animate) {
// TODO: notify effects even if it should not animate?
if (animate) {
Q_EMIT windowMinimized(c->effectWindow());
}
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::clientUnminimized, this, [this](Window *c, bool animate) {
// TODO: notify effects even if it should not animate?
if (animate) {
Q_EMIT windowUnminimized(c->effectWindow());
}
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::modalChanged, this, &EffectsHandlerImpl::slotClientModalityChanged);
connect(c, &Window::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged);
connect(c, &Window::frameGeometryChanged, this, &EffectsHandlerImpl::slotFrameGeometryChanged);
connect(c, &Window::damaged, this, &EffectsHandlerImpl::slotWindowDamaged);
connect(c, &Window::unresponsiveChanged, this, [this, c](bool unresponsive) {
Q_EMIT windowUnresponsiveChanged(c->effectWindow(), unresponsive);
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::windowShown, this, [this](Window *c) {
Q_EMIT windowShown(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::windowHidden, this, [this](Window *c) {
Q_EMIT windowHidden(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::keepAboveChanged, this, [this, c](bool above) {
Q_UNUSED(above)
Q_EMIT windowKeepAboveChanged(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::keepBelowChanged, this, [this, c](bool below) {
Q_UNUSED(below)
Q_EMIT windowKeepBelowChanged(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::fullScreenChanged, this, [this, c]() {
Q_EMIT windowFullScreenChanged(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::visibleGeometryChanged, this, [this, c]() {
Q_EMIT windowExpandedGeometryChanged(c->effectWindow());
});
2022-04-22 17:39:12 +00:00
connect(c, &Window::decorationChanged, this, [this, c]() {
Q_EMIT windowDecorationChanged(c->effectWindow());
});
}
void EffectsHandlerImpl::setupUnmanagedConnections(Unmanaged *u)
{
connect(u, &Unmanaged::windowClosed, this, &EffectsHandlerImpl::slotWindowClosed);
connect(u, &Unmanaged::opacityChanged, this, &EffectsHandlerImpl::slotOpacityChanged);
connect(u, &Unmanaged::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged);
connect(u, &Unmanaged::frameGeometryChanged, this, &EffectsHandlerImpl::slotFrameGeometryChanged);
connect(u, &Unmanaged::damaged, this, &EffectsHandlerImpl::slotWindowDamaged);
connect(u, &Unmanaged::visibleGeometryChanged, this, [this, u]() {
Q_EMIT windowExpandedGeometryChanged(u->effectWindow());
});
}
void EffectsHandlerImpl::reconfigure()
2011-01-30 14:34:42 +00:00
{
m_effectLoader->queryAndLoadAll();
2011-01-30 14:34:42 +00:00
}
// the idea is that effects call this function again which calls the next one
void EffectsHandlerImpl::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
2011-01-30 14:34:42 +00:00
{
if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
Provide expected presentation time to effects Effects are given the interval between two consecutive frames. The main flaw of this approach is that if the Compositor transitions from the idle state to "active" state, i.e. when there is something to repaint, effects may see a very large interval between the last painted frame and the current. In order to address this issue, the Scene invalidates the timer that is used to measure time between consecutive frames before the Compositor is about to become idle. While this works perfectly fine with Xinerama-style rendering, with per screen rendering, determining whether the compositor is about to idle is rather a tedious task mostly because a single output can't be used for the test. Furthermore, since the Compositor schedules pointless repaints just to ensure that it's idle, it might take several attempts to figure out whether the scene timer must be invalidated if you use (true) per screen rendering. Ideally, all effects should use a timeline helper that is aware of the underlying render loop and its timings. However, this option is off the table because it will involve a lot of work to implement it. Alternative and much simpler option is to pass the expected presentation time to effects rather than time between consecutive frames. This means that effects are responsible for determining how much animation timelines have to be advanced. Typically, an effect would have to store the presentation timestamp provided in either prePaint{Screen,Window} and use it in the subsequent prePaint{Screen,Window} call to estimate the amount of time passed between the next and the last frames. Unfortunately, this is an API incompatible change. However, it shouldn't take a lot of work to port third-party binary effects, which don't use the AnimationEffect class, to the new API. On the bright side, we no longer need to be concerned about the Compositor getting idle. We do still try to determine whether the Compositor is about to idle, primarily, because the OpenGL render backend swaps buffers on present, but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
(*m_currentPaintScreenIterator++)->prePaintScreen(data, presentTime);
--m_currentPaintScreenIterator;
}
2011-01-30 14:34:42 +00:00
// no special final code
}
void EffectsHandlerImpl::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
2011-01-30 14:34:42 +00:00
{
if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
(*m_currentPaintScreenIterator++)->paintScreen(mask, region, data);
--m_currentPaintScreenIterator;
} else {
m_scene->finalPaintScreen(mask, region, data);
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::postPaintScreen()
2011-01-30 14:34:42 +00:00
{
if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
(*m_currentPaintScreenIterator++)->postPaintScreen();
--m_currentPaintScreenIterator;
}
2011-01-30 14:34:42 +00:00
// no special final code
}
void EffectsHandlerImpl::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
2011-01-30 14:34:42 +00:00
{
if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
Provide expected presentation time to effects Effects are given the interval between two consecutive frames. The main flaw of this approach is that if the Compositor transitions from the idle state to "active" state, i.e. when there is something to repaint, effects may see a very large interval between the last painted frame and the current. In order to address this issue, the Scene invalidates the timer that is used to measure time between consecutive frames before the Compositor is about to become idle. While this works perfectly fine with Xinerama-style rendering, with per screen rendering, determining whether the compositor is about to idle is rather a tedious task mostly because a single output can't be used for the test. Furthermore, since the Compositor schedules pointless repaints just to ensure that it's idle, it might take several attempts to figure out whether the scene timer must be invalidated if you use (true) per screen rendering. Ideally, all effects should use a timeline helper that is aware of the underlying render loop and its timings. However, this option is off the table because it will involve a lot of work to implement it. Alternative and much simpler option is to pass the expected presentation time to effects rather than time between consecutive frames. This means that effects are responsible for determining how much animation timelines have to be advanced. Typically, an effect would have to store the presentation timestamp provided in either prePaint{Screen,Window} and use it in the subsequent prePaint{Screen,Window} call to estimate the amount of time passed between the next and the last frames. Unfortunately, this is an API incompatible change. However, it shouldn't take a lot of work to port third-party binary effects, which don't use the AnimationEffect class, to the new API. On the bright side, we no longer need to be concerned about the Compositor getting idle. We do still try to determine whether the Compositor is about to idle, primarily, because the OpenGL render backend swaps buffers on present, but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
(*m_currentPaintWindowIterator++)->prePaintWindow(w, data, presentTime);
--m_currentPaintWindowIterator;
}
2011-01-30 14:34:42 +00:00
// no special final code
}
void EffectsHandlerImpl::paintWindow(EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
2011-01-30 14:34:42 +00:00
{
if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
(*m_currentPaintWindowIterator++)->paintWindow(w, mask, region, data);
--m_currentPaintWindowIterator;
} else {
m_scene->finalPaintWindow(static_cast<EffectWindowImpl *>(w), mask, region, data);
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::paintEffectFrame(EffectFrame *frame, const QRegion &region, double opacity, double frameOpacity)
2011-01-30 14:34:42 +00:00
{
if (m_currentPaintEffectFrameIterator != m_activeEffects.constEnd()) {
(*m_currentPaintEffectFrameIterator++)->paintEffectFrame(frame, region, opacity, frameOpacity);
--m_currentPaintEffectFrameIterator;
2011-01-30 14:34:42 +00:00
} else {
const EffectFrameImpl *frameImpl = static_cast<const EffectFrameImpl *>(frame);
2011-01-30 14:34:42 +00:00
frameImpl->finalRender(region, opacity, frameOpacity);
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::postPaintWindow(EffectWindow *w)
2011-01-30 14:34:42 +00:00
{
if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
(*m_currentPaintWindowIterator++)->postPaintWindow(w);
--m_currentPaintWindowIterator;
}
2011-01-30 14:34:42 +00:00
// no special final code
}
Effect *EffectsHandlerImpl::provides(Effect::Feature ef)
2011-01-30 14:34:42 +00:00
{
for (int i = 0; i < loaded_effects.size(); ++i) {
if (loaded_effects.at(i).second->provides(ef)) {
return loaded_effects.at(i).second;
}
}
return nullptr;
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::drawWindow(EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
2011-01-30 14:34:42 +00:00
{
if (m_currentDrawWindowIterator != m_activeEffects.constEnd()) {
(*m_currentDrawWindowIterator++)->drawWindow(w, mask, region, data);
--m_currentDrawWindowIterator;
} else {
m_scene->finalDrawWindow(static_cast<EffectWindowImpl *>(w), mask, region, data);
}
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::hasDecorationShadows() const
2011-01-30 14:34:42 +00:00
{
return false;
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::decorationsHaveAlpha() const
2011-01-30 14:34:42 +00:00
{
return true;
2011-01-30 14:34:42 +00:00
}
// start another painting pass
void EffectsHandlerImpl::startPaint()
2011-01-30 14:34:42 +00:00
{
m_activeEffects.clear();
m_activeEffects.reserve(loaded_effects.count());
for (QVector<KWin::EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->isActive()) {
m_activeEffects << it->second;
}
}
m_currentDrawWindowIterator = m_activeEffects.constBegin();
m_currentPaintWindowIterator = m_activeEffects.constBegin();
m_currentPaintScreenIterator = m_activeEffects.constBegin();
m_currentPaintEffectFrameIterator = m_activeEffects.constBegin();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotClientMaximized(KWin::Window *c, MaximizeMode maxMode)
2011-01-30 14:34:42 +00:00
{
bool horizontal = false;
bool vertical = false;
switch (maxMode) {
case MaximizeHorizontal:
horizontal = true;
break;
case MaximizeVertical:
vertical = true;
break;
case MaximizeFull:
horizontal = true;
vertical = true;
break;
case MaximizeRestore: // fall through
default:
// default - nothing to do
break;
}
if (EffectWindowImpl *w = c->effectWindow()) {
Q_EMIT windowMaximizedStateChanged(w, horizontal, vertical);
}
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotOpacityChanged(Window *t, qreal oldOpacity)
2011-01-30 14:34:42 +00:00
{
if (t->opacity() == oldOpacity || !t->effectWindow()) {
return;
2011-03-06 09:30:23 +00:00
}
Q_EMIT windowOpacityChanged(t->effectWindow(), oldOpacity, (qreal)t->opacity());
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotClientShown(KWin::Window *t)
{
Q_ASSERT(t->isClient());
2022-04-22 17:39:12 +00:00
disconnect(t, &Window::windowShown, this, &EffectsHandlerImpl::slotClientShown);
setupClientConnections(t);
Q_EMIT windowAdded(t->effectWindow());
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotUnmanagedShown(KWin::Window *t)
{ // regardless, unmanaged windows are -yet?- not synced anyway
Q_ASSERT(qobject_cast<Unmanaged *>(t));
Unmanaged *u = static_cast<Unmanaged *>(t);
setupUnmanagedConnections(u);
Q_EMIT windowAdded(u->effectWindow());
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotWindowClosed(KWin::Window *c, KWin::Deleted *d)
2011-01-30 14:34:42 +00:00
{
c->disconnect(this);
if (d) {
Q_EMIT windowClosed(d->effectWindow());
}
}
void EffectsHandlerImpl::slotClientModalityChanged()
{
Q_EMIT windowModalityChanged(static_cast<X11Window *>(sender())->effectWindow());
}
void EffectsHandlerImpl::slotCurrentTabAboutToChange(EffectWindow *from, EffectWindow *to)
2011-01-30 14:34:42 +00:00
{
Q_EMIT currentTabAboutToChange(from, to);
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::slotTabAdded(EffectWindow *w, EffectWindow *to)
2011-01-30 14:34:42 +00:00
{
Q_EMIT tabAdded(w, to);
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::slotTabRemoved(EffectWindow *w, EffectWindow *leaderOfFormerGroup)
2011-01-30 14:34:42 +00:00
{
Q_EMIT tabRemoved(w, leaderOfFormerGroup);
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotWindowDamaged(Window *t, const QRegion &r)
2011-01-30 14:34:42 +00:00
{
if (!t->effectWindow()) {
// can happen during tear down of window
return;
}
Q_EMIT windowDamaged(t->effectWindow(), r);
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotGeometryShapeChanged(Window *t, const QRect &old)
2011-01-30 14:34:42 +00:00
{
// during late cleanup effectWindow() may be already NULL
// in some functions that may still call this
if (t == nullptr || t->effectWindow() == nullptr) {
return;
}
Q_EMIT windowGeometryShapeChanged(t->effectWindow(), old);
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
void EffectsHandlerImpl::slotFrameGeometryChanged(Window *toplevel, const QRect &oldGeometry)
{
// effectWindow() might be nullptr during tear down of the client.
if (toplevel->effectWindow()) {
Q_EMIT windowFrameGeometryChanged(toplevel->effectWindow(), oldGeometry);
}
}
void EffectsHandlerImpl::setActiveFullScreenEffect(Effect *e)
2011-01-30 14:34:42 +00:00
{
if (fullscreen_effect == e) {
return;
}
const bool activeChanged = (e == nullptr || fullscreen_effect == nullptr);
fullscreen_effect = e;
Q_EMIT activeFullScreenEffectChanged();
if (activeChanged) {
Q_EMIT hasActiveFullScreenEffectChanged();
}
2011-01-30 14:34:42 +00:00
}
Effect *EffectsHandlerImpl::activeFullScreenEffect() const
2011-01-30 14:34:42 +00:00
{
return fullscreen_effect;
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::hasActiveFullScreenEffect() const
{
return fullscreen_effect;
}
bool EffectsHandlerImpl::grabKeyboard(Effect *effect)
2011-01-30 14:34:42 +00:00
{
if (keyboard_grab_effect != nullptr) {
return false;
}
if (!doGrabKeyboard()) {
return false;
}
keyboard_grab_effect = effect;
return true;
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::doGrabKeyboard()
{
return true;
}
void EffectsHandlerImpl::ungrabKeyboard()
2011-01-30 14:34:42 +00:00
{
Q_ASSERT(keyboard_grab_effect != nullptr);
doUngrabKeyboard();
keyboard_grab_effect = nullptr;
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::doUngrabKeyboard()
{
}
void EffectsHandlerImpl::grabbedKeyboardEvent(QKeyEvent *e)
2011-01-30 14:34:42 +00:00
{
if (keyboard_grab_effect != nullptr) {
2011-01-30 14:34:42 +00:00
keyboard_grab_effect->grabbedKeyboardEvent(e);
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::startMouseInterception(Effect *effect, Qt::CursorShape shape)
{
if (m_grabbedMouseEffects.contains(effect)) {
return;
}
m_grabbedMouseEffects.append(effect);
if (m_grabbedMouseEffects.size() != 1) {
return;
}
doStartMouseInterception(shape);
}
void EffectsHandlerImpl::doStartMouseInterception(Qt::CursorShape shape)
{
input()->pointer()->setEffectsOverrideCursor(shape);
}
void EffectsHandlerImpl::stopMouseInterception(Effect *effect)
{
if (!m_grabbedMouseEffects.contains(effect)) {
return;
}
m_grabbedMouseEffects.removeAll(effect);
if (m_grabbedMouseEffects.isEmpty()) {
doStopMouseInterception();
}
}
void EffectsHandlerImpl::doStopMouseInterception()
{
input()->pointer()->removeEffectsOverrideCursor();
}
bool EffectsHandlerImpl::isMouseInterception() const
{
return m_grabbedMouseEffects.count() > 0;
}
bool EffectsHandlerImpl::touchDown(qint32 id, const QPointF &pos, quint32 time)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->touchDown(id, pos, time)) {
return true;
}
}
return false;
}
bool EffectsHandlerImpl::touchMotion(qint32 id, const QPointF &pos, quint32 time)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->touchMotion(id, pos, time)) {
return true;
}
}
return false;
}
bool EffectsHandlerImpl::touchUp(qint32 id, quint32 time)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->touchUp(id, time)) {
return true;
}
}
return false;
}
bool EffectsHandlerImpl::tabletToolEvent(TabletEvent *event)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->tabletToolEvent(event)) {
return true;
}
}
return false;
}
bool EffectsHandlerImpl::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->tabletToolButtonEvent(button, pressed, tabletToolId.m_uniqueId)) {
return true;
}
}
return false;
}
bool EffectsHandlerImpl::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->tabletPadButtonEvent(button, pressed, tabletPadId.data)) {
return true;
}
}
return false;
}
bool EffectsHandlerImpl::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->tabletPadStripEvent(number, position, isFinger, tabletPadId.data)) {
return true;
}
}
return false;
}
bool EffectsHandlerImpl::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
{
// TODO: reverse call order?
for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if (it->second->tabletPadRingEvent(number, position, isFinger, tabletPadId.data)) {
return true;
}
}
return false;
}
void EffectsHandlerImpl::registerGlobalShortcut(const QKeySequence &shortcut, QAction *action)
{
input()->registerShortcut(shortcut, action);
}
void EffectsHandlerImpl::registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
{
input()->registerPointerShortcut(modifiers, pointerButtons, action);
}
void EffectsHandlerImpl::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
{
input()->registerAxisShortcut(modifiers, axis, action);
}
void EffectsHandlerImpl::registerRealtimeTouchpadSwipeShortcut(SwipeDirection dir, uint fingerCount, QAction *onUp, std::function<void(qreal)> progressCallback)
{
input()->registerRealtimeTouchpadSwipeShortcut(dir, fingerCount, onUp, progressCallback);
}
void EffectsHandlerImpl::registerTouchpadSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action)
Add support for global touchpad swipe gestures Summary: This change adds global touchpad swipe gestures to the GlobalShortcutsManager and hooks up the swipe gestures as defined at the Plasma Affenfels sprint: * swipe up: Desktop Grid * swipe down: Present Windows * swipe left: previous virtual desktop * swipe right: next virtual desktop The main work is handled by two new classes: SwipeGesture and GestureRecognizer. This is implemented in a way that it can be extended to also recognize touch screen gestures and pinch gestures. The SwipeGesture defines what is required for the gesture to trigger. Currently this includes the minimum and maximum number of fingers participating in the gesture and the direction. The gesture gets registered in the GestureRecognizer. The events for the gesture are fed into the GestureRecognizer. It evaluates which gestures could trigger and tracks them for every update of the gesture. In the process of the gesture tracking the GestureRecognizer emits signals on the Gesture: * started: when the Gesture gets considered for a sequence * cancelled: the Gesture no longer matches the sequence * triggered: the sequence ended and the Gesture still matches The remaining changes are related to hook up the existing shortcut framework with the new touchpad gestures. The GlobalShortcutManager gained support for it, InputRedirection and EffectsHandler offer methods to register a QAction. VirtualDesktopManager, PresentWindows and DesktopGrid are adjusted to support the gesture. Reviewers: #kwin, #plasma_on_wayland Subscribers: plasma-devel Tags: #plasma_on_wayland Differential Revision: https://phabricator.kde.org/D5097
2017-03-18 10:00:30 +00:00
{
input()->registerTouchpadSwipeShortcut(direction, fingerCount, action);
}
void EffectsHandlerImpl::registerRealtimeTouchpadPinchShortcut(PinchDirection dir, uint fingerCount, QAction *onUp, std::function<void(qreal)> progressCallback)
{
input()->registerRealtimeTouchpadPinchShortcut(dir, fingerCount, onUp, progressCallback);
}
void EffectsHandlerImpl::registerTouchpadPinchShortcut(PinchDirection direction, uint fingerCount, QAction *action)
{
input()->registerTouchpadPinchShortcut(direction, fingerCount, action);
Add support for global touchpad swipe gestures Summary: This change adds global touchpad swipe gestures to the GlobalShortcutsManager and hooks up the swipe gestures as defined at the Plasma Affenfels sprint: * swipe up: Desktop Grid * swipe down: Present Windows * swipe left: previous virtual desktop * swipe right: next virtual desktop The main work is handled by two new classes: SwipeGesture and GestureRecognizer. This is implemented in a way that it can be extended to also recognize touch screen gestures and pinch gestures. The SwipeGesture defines what is required for the gesture to trigger. Currently this includes the minimum and maximum number of fingers participating in the gesture and the direction. The gesture gets registered in the GestureRecognizer. The events for the gesture are fed into the GestureRecognizer. It evaluates which gestures could trigger and tracks them for every update of the gesture. In the process of the gesture tracking the GestureRecognizer emits signals on the Gesture: * started: when the Gesture gets considered for a sequence * cancelled: the Gesture no longer matches the sequence * triggered: the sequence ended and the Gesture still matches The remaining changes are related to hook up the existing shortcut framework with the new touchpad gestures. The GlobalShortcutManager gained support for it, InputRedirection and EffectsHandler offer methods to register a QAction. VirtualDesktopManager, PresentWindows and DesktopGrid are adjusted to support the gesture. Reviewers: #kwin, #plasma_on_wayland Subscribers: plasma-devel Tags: #plasma_on_wayland Differential Revision: https://phabricator.kde.org/D5097
2017-03-18 10:00:30 +00:00
}
void EffectsHandlerImpl::registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action)
{
input()->registerTouchscreenSwipeShortcut(direction, fingerCount, action);
}
void *EffectsHandlerImpl::getProxy(QString name)
2011-01-30 14:34:42 +00:00
{
for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if ((*it).first == name) {
return (*it).second->proxy();
}
}
return nullptr;
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::startMousePolling()
2011-01-30 14:34:42 +00:00
{
if (Cursors::self()->mouse()) {
Cursors::self()->mouse()->startMousePolling();
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::stopMousePolling()
2011-01-30 14:34:42 +00:00
{
if (Cursors::self()->mouse()) {
Cursors::self()->mouse()->stopMousePolling();
}
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::hasKeyboardGrab() const
2011-01-30 14:34:42 +00:00
{
return keyboard_grab_effect != nullptr;
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void EffectsHandlerImpl::registerPropertyType(long atom, bool reg)
{
if (reg) {
++registered_atoms[atom]; // initialized to 0 if not present yet
} else {
if (--registered_atoms[atom] == 0) {
2011-01-30 14:34:42 +00:00
registered_atoms.remove(atom);
}
}
2011-01-30 14:34:42 +00:00
}
xcb_atom_t EffectsHandlerImpl::announceSupportProperty(const QByteArray &propertyName, Effect *effect)
{
PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
if (it != m_propertiesForEffects.end()) {
// property has already been registered for an effect
// just append Effect and return the atom stored in m_managedProperties
if (!it.value().contains(effect)) {
it.value().append(effect);
}
return m_managedProperties.value(propertyName, XCB_ATOM_NONE);
}
m_propertiesForEffects.insert(propertyName, QList<Effect *>() << effect);
const auto atom = registerSupportProperty(propertyName);
if (atom == XCB_ATOM_NONE) {
return atom;
}
m_compositor->keepSupportProperty(atom);
m_managedProperties.insert(propertyName, atom);
registerPropertyType(atom, true);
return atom;
}
void EffectsHandlerImpl::removeSupportProperty(const QByteArray &propertyName, Effect *effect)
{
PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
if (it == m_propertiesForEffects.end()) {
// property is not registered - nothing to do
return;
}
if (!it.value().contains(effect)) {
// property is not registered for given effect - nothing to do
return;
}
it.value().removeAll(effect);
if (!it.value().isEmpty()) {
// property still registered for another effect - nothing further to do
return;
}
const xcb_atom_t atom = m_managedProperties.take(propertyName);
registerPropertyType(atom, false);
m_propertiesForEffects.remove(propertyName);
m_compositor->removeSupportProperty(atom); // delayed removal
}
2011-01-30 14:34:42 +00:00
QByteArray EffectsHandlerImpl::readRootProperty(long atom, long type, int format) const
{
if (!kwinApp()->x11Connection()) {
return QByteArray();
}
return readWindowProperty(kwinApp()->x11RootWindow(), atom, type, format);
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::activateWindow(EffectWindow *c)
2011-01-30 14:34:42 +00:00
{
auto window = static_cast<EffectWindowImpl *>(c)->window();
if (window->isClient()) {
Workspace::self()->activateWindow(window, true);
}
2011-01-30 14:34:42 +00:00
}
EffectWindow *EffectsHandlerImpl::activeWindow() const
2011-01-30 14:34:42 +00:00
{
return Workspace::self()->activeWindow() ? Workspace::self()->activeWindow()->effectWindow() : nullptr;
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::moveWindow(EffectWindow *w, const QPoint &pos, bool snap, double snapAdjust)
2011-01-30 14:34:42 +00:00
{
auto window = static_cast<EffectWindowImpl *>(w)->window();
if (!window->isClient() || !window->isMovable()) {
return;
}
if (snap) {
window->move(Workspace::self()->adjustWindowPosition(window, pos, true, snapAdjust));
} else {
window->move(pos);
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::windowToDesktop(EffectWindow *w, int desktop)
2011-01-30 14:34:42 +00:00
{
auto window = static_cast<EffectWindowImpl *>(w)->window();
if (window->isClient() && !window->isDesktop() && !window->isDock()) {
Workspace::self()->sendWindowToDesktop(window, desktop, true);
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::windowToDesktops(EffectWindow *w, const QVector<uint> &desktopIds)
{
auto window = static_cast<EffectWindowImpl *>(w)->window();
if (!window->isClient() || window->isDesktop() || window->isDock()) {
return;
}
QVector<VirtualDesktop *> desktops;
desktops.reserve(desktopIds.count());
for (uint x11Id : desktopIds) {
if (x11Id > VirtualDesktopManager::self()->count()) {
continue;
}
VirtualDesktop *d = VirtualDesktopManager::self()->desktopForX11Id(x11Id);
Q_ASSERT(d);
if (desktops.contains(d)) {
continue;
}
desktops << d;
}
window->setDesktops(desktops);
}
void EffectsHandlerImpl::windowToScreen(EffectWindow *w, EffectScreen *screen)
2011-01-30 14:34:42 +00:00
{
auto window = static_cast<EffectWindowImpl *>(w)->window();
if (window->isClient() && !window->isDesktop() && !window->isDock()) {
EffectScreenImpl *screenImpl = static_cast<EffectScreenImpl *>(screen);
Workspace::self()->sendWindowToOutput(window, screenImpl->platformOutput());
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void EffectsHandlerImpl::setShowingDesktop(bool showing)
{
Workspace::self()->setShowingDesktop(showing);
}
QString EffectsHandlerImpl::currentActivity() const
{
#if KWIN_BUILD_ACTIVITIES
if (!Activities::self()) {
return QString();
}
return Activities::self()->current();
#else
return QString();
#endif
}
int EffectsHandlerImpl::currentDesktop() const
2011-01-30 14:34:42 +00:00
{
return VirtualDesktopManager::self()->current();
2011-01-30 14:34:42 +00:00
}
int EffectsHandlerImpl::numberOfDesktops() const
2011-01-30 14:34:42 +00:00
{
return VirtualDesktopManager::self()->count();
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void EffectsHandlerImpl::setCurrentDesktop(int desktop)
{
VirtualDesktopManager::self()->setCurrent(desktop);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void EffectsHandlerImpl::setNumberOfDesktops(int desktops)
{
VirtualDesktopManager::self()->setCount(desktops);
2011-01-30 14:34:42 +00:00
}
QSize EffectsHandlerImpl::desktopGridSize() const
2011-01-30 14:34:42 +00:00
{
return VirtualDesktopManager::self()->grid().size();
2011-01-30 14:34:42 +00:00
}
int EffectsHandlerImpl::desktopGridWidth() const
2011-01-30 14:34:42 +00:00
{
return desktopGridSize().width();
2011-01-30 14:34:42 +00:00
}
int EffectsHandlerImpl::desktopGridHeight() const
2011-01-30 14:34:42 +00:00
{
return desktopGridSize().height();
2011-01-30 14:34:42 +00:00
}
int EffectsHandlerImpl::workspaceWidth() const
2011-01-30 14:34:42 +00:00
{
return desktopGridWidth() * Screens::self()->size().width();
2011-01-30 14:34:42 +00:00
}
int EffectsHandlerImpl::workspaceHeight() const
2011-01-30 14:34:42 +00:00
{
return desktopGridHeight() * Screens::self()->size().height();
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
int EffectsHandlerImpl::desktopAtCoords(QPoint coords) const
{
if (auto vd = VirtualDesktopManager::self()->grid().at(coords)) {
return vd->x11DesktopNumber();
}
return 0;
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
QPoint EffectsHandlerImpl::desktopGridCoords(int id) const
{
return VirtualDesktopManager::self()->grid().gridCoords(id);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
QPoint EffectsHandlerImpl::desktopCoords(int id) const
{
QPoint coords = VirtualDesktopManager::self()->grid().gridCoords(id);
if (coords.x() == -1) {
return QPoint(-1, -1);
}
const QSize displaySize = Screens::self()->size();
return QPoint(coords.x() * displaySize.width(), coords.y() * displaySize.height());
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
int EffectsHandlerImpl::desktopAbove(int desktop, bool wrap) const
{
return getDesktop<DesktopAbove>(desktop, wrap);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
int EffectsHandlerImpl::desktopToRight(int desktop, bool wrap) const
{
return getDesktop<DesktopRight>(desktop, wrap);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
int EffectsHandlerImpl::desktopBelow(int desktop, bool wrap) const
{
return getDesktop<DesktopBelow>(desktop, wrap);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
int EffectsHandlerImpl::desktopToLeft(int desktop, bool wrap) const
{
return getDesktop<DesktopLeft>(desktop, wrap);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
QString EffectsHandlerImpl::desktopName(int desktop) const
{
const VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForX11Id(desktop);
return vd ? vd->name() : QString();
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::optionRollOverDesktops() const
2011-01-30 14:34:42 +00:00
{
return options->isRollOverDesktops();
2011-01-30 14:34:42 +00:00
}
double EffectsHandlerImpl::animationTimeFactor() const
2011-01-30 14:34:42 +00:00
{
return options->animationTimeFactor();
2011-01-30 14:34:42 +00:00
}
EffectWindow *EffectsHandlerImpl::findWindow(WId id) const
2011-01-30 14:34:42 +00:00
{
if (X11Window *w = Workspace::self()->findClient(Predicate::WindowMatch, id)) {
return w->effectWindow();
}
if (Unmanaged *w = Workspace::self()->findUnmanaged(id)) {
return w->effectWindow();
}
return nullptr;
2011-01-30 14:34:42 +00:00
}
EffectWindow *EffectsHandlerImpl::findWindow(KWaylandServer::SurfaceInterface *surf) const
{
if (waylandServer()) {
if (Window *w = waylandServer()->findWindow(surf)) {
return w->effectWindow();
}
}
return nullptr;
}
Add windowsystem plugin for KWin's qpa Summary: KWindowSystem provides a plugin interface to have platform specific implementations. So far KWin relied on the implementation in KWayland-integration repository. This is something I find unsuited, for the following reasons: * any test in KWin for functionality set through the plugin would fail * it's not clear what's going on where * in worst case some code could deadlock * KWin shouldn't use KWindowSystem and only a small subset is allowed to be used The last point needs some further explanation. KWin internally does not and cannot use KWindowSystem. KWindowSystem (especially KWindowInfo) is exposing information which KWin sets. It's more than weird if KWin asks KWindowSystem for the state of a window it set itself. On X11 it's just slow, on Wayland it can result in roundtrips to KWin itself which is dangerous. But due to using Plasma components we have a few areas where we use KWindowSystem. E.g. a Plasma::Dialog sets a window type, the slide in direction, blur and background contrast. This we want to support and need to support. Other API elements we do not want, like for examples the available windows. KWin internal windows either have direct access to KWin or a scripting interface exposed providing (limited) access - there is just no need to have this in KWindowSystem. To make it more clear what KWin supports as API of KWindowSystem for internal windows this change implements a stripped down version of the kwayland-integration plugin. The main difference is that it does not use KWayland at all, but a QWindow internal side channel. To support this EffectWindow provides an accessor for internalWindow and the three already mentioned effects are adjusted to read from the internal QWindow and it's dynamic properties. This change is a first step for a further refactoring. I plan to split the internal window out of ShellClient into a dedicated class. I think there are nowadays too many special cases. If it moves out there is the question whether we really want to use Wayland for the internal windows or whether this is just historic ballast (after all we used to use qwayland for that in the beginning). As the change could introduce regressions I'm targetting 5.16. Test Plan: new test case for window type, manual testing using Alt+Tab for the effects integration. Sliding popups, blur and contrast worked fine. Reviewers: #kwin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D18228
2019-01-13 16:50:32 +00:00
EffectWindow *EffectsHandlerImpl::findWindow(QWindow *w) const
{
2022-04-22 17:39:12 +00:00
if (Window *toplevel = workspace()->findInternal(w)) {
return toplevel->effectWindow();
Add windowsystem plugin for KWin's qpa Summary: KWindowSystem provides a plugin interface to have platform specific implementations. So far KWin relied on the implementation in KWayland-integration repository. This is something I find unsuited, for the following reasons: * any test in KWin for functionality set through the plugin would fail * it's not clear what's going on where * in worst case some code could deadlock * KWin shouldn't use KWindowSystem and only a small subset is allowed to be used The last point needs some further explanation. KWin internally does not and cannot use KWindowSystem. KWindowSystem (especially KWindowInfo) is exposing information which KWin sets. It's more than weird if KWin asks KWindowSystem for the state of a window it set itself. On X11 it's just slow, on Wayland it can result in roundtrips to KWin itself which is dangerous. But due to using Plasma components we have a few areas where we use KWindowSystem. E.g. a Plasma::Dialog sets a window type, the slide in direction, blur and background contrast. This we want to support and need to support. Other API elements we do not want, like for examples the available windows. KWin internal windows either have direct access to KWin or a scripting interface exposed providing (limited) access - there is just no need to have this in KWindowSystem. To make it more clear what KWin supports as API of KWindowSystem for internal windows this change implements a stripped down version of the kwayland-integration plugin. The main difference is that it does not use KWayland at all, but a QWindow internal side channel. To support this EffectWindow provides an accessor for internalWindow and the three already mentioned effects are adjusted to read from the internal QWindow and it's dynamic properties. This change is a first step for a further refactoring. I plan to split the internal window out of ShellClient into a dedicated class. I think there are nowadays too many special cases. If it moves out there is the question whether we really want to use Wayland for the internal windows or whether this is just historic ballast (after all we used to use qwayland for that in the beginning). As the change could introduce regressions I'm targetting 5.16. Test Plan: new test case for window type, manual testing using Alt+Tab for the effects integration. Sliding popups, blur and contrast worked fine. Reviewers: #kwin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D18228
2019-01-13 16:50:32 +00:00
}
return nullptr;
}
EffectWindow *EffectsHandlerImpl::findWindow(const QUuid &id) const
{
if (Window *window = workspace()->findToplevel(id)) {
return window->effectWindow();
}
return nullptr;
}
EffectWindowList EffectsHandlerImpl::stackingOrder() const
2011-01-30 14:34:42 +00:00
{
2022-04-22 17:39:12 +00:00
QList<Window *> list = Workspace::self()->xStackingOrder();
EffectWindowList ret;
2022-04-22 17:39:12 +00:00
for (Window *t : list) {
if (EffectWindow *w = effectWindow(t)) {
ret.append(w);
}
}
return ret;
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::setElevatedWindow(KWin::EffectWindow *w, bool set)
2011-01-30 14:34:42 +00:00
{
elevated_windows.removeAll(w);
if (set) {
2011-01-30 14:34:42 +00:00
elevated_windows.append(w);
}
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::setTabBoxWindow(EffectWindow *w)
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
auto window = static_cast<EffectWindowImpl *>(w)->window();
if (window->isClient()) {
TabBox::TabBox::self()->setCurrentClient(window);
}
#else
Q_UNUSED(w)
#endif
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::setTabBoxDesktop(int desktop)
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
TabBox::TabBox::self()->setCurrentDesktop(desktop);
#else
Q_UNUSED(desktop)
#endif
2011-01-30 14:34:42 +00:00
}
EffectWindowList EffectsHandlerImpl::currentTabBoxWindowList() const
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
const auto clients = TabBox::TabBox::self()->currentClientList();
EffectWindowList ret;
ret.reserve(clients.size());
std::transform(std::cbegin(clients), std::cend(clients),
std::back_inserter(ret),
[](auto client) {
return client->effectWindow();
});
return ret;
#else
return EffectWindowList();
#endif
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::refTabBox()
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
TabBox::TabBox::self()->reference();
#endif
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::unrefTabBox()
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
TabBox::TabBox::self()->unreference();
#endif
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::closeTabBox()
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
TabBox::TabBox::self()->close();
#endif
2011-01-30 14:34:42 +00:00
}
QList<int> EffectsHandlerImpl::currentTabBoxDesktopList() const
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
return TabBox::TabBox::self()->currentDesktopList();
#else
return QList<int>();
#endif
2011-01-30 14:34:42 +00:00
}
int EffectsHandlerImpl::currentTabBoxDesktop() const
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
return TabBox::TabBox::self()->currentDesktop();
#else
return -1;
#endif
2011-01-30 14:34:42 +00:00
}
EffectWindow *EffectsHandlerImpl::currentTabBoxWindow() const
2011-01-30 14:34:42 +00:00
{
#if KWIN_BUILD_TABBOX
if (auto c = TabBox::TabBox::self()->currentClient()) {
return c->effectWindow();
}
#endif
return nullptr;
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::addRepaintFull()
2011-01-30 14:34:42 +00:00
{
m_compositor->scene()->addRepaintFull();
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::addRepaint(const QRect &r)
2011-01-30 14:34:42 +00:00
{
m_compositor->scene()->addRepaint(r);
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::addRepaint(const QRegion &r)
2011-01-30 14:34:42 +00:00
{
m_compositor->scene()->addRepaint(r);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void EffectsHandlerImpl::addRepaint(int x, int y, int w, int h)
{
m_compositor->scene()->addRepaint(x, y, w, h);
2011-01-30 14:34:42 +00:00
}
EffectScreen *EffectsHandlerImpl::activeScreen() const
2011-01-30 14:34:42 +00:00
{
return EffectScreenImpl::get(workspace()->activeOutput());
2011-01-30 14:34:42 +00:00
}
static VirtualDesktop *resolveVirtualDesktop(int desktopId)
2011-01-30 14:34:42 +00:00
{
if (desktopId == 0 || desktopId == -1) {
return VirtualDesktopManager::self()->currentDesktop();
} else {
return VirtualDesktopManager::self()->desktopForX11Id(desktopId);
}
}
QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const EffectScreen *screen, int desktop) const
{
const EffectScreenImpl *screenImpl = static_cast<const EffectScreenImpl *>(screen);
return Workspace::self()->clientArea(opt, screenImpl->platformOutput(), resolveVirtualDesktop(desktop));
2011-01-30 14:34:42 +00:00
}
QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const EffectWindow *c) const
2011-01-30 14:34:42 +00:00
{
2022-04-22 17:39:12 +00:00
const Window *t = static_cast<const EffectWindowImpl *>(c)->window();
return Workspace::self()->clientArea(opt, t);
2011-01-30 14:34:42 +00:00
}
QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const QPoint &p, int desktop) const
2011-01-30 14:34:42 +00:00
{
const Output *output = kwinApp()->platform()->outputAt(p);
const VirtualDesktop *virtualDesktop = resolveVirtualDesktop(desktop);
return Workspace::self()->clientArea(opt, output, virtualDesktop);
2011-01-30 14:34:42 +00:00
}
QRect EffectsHandlerImpl::virtualScreenGeometry() const
{
return Screens::self()->geometry();
}
QSize EffectsHandlerImpl::virtualScreenSize() const
{
return Screens::self()->size();
}
void EffectsHandlerImpl::defineCursor(Qt::CursorShape shape)
2011-01-30 14:34:42 +00:00
{
input()->pointer()->setEffectsOverrideCursor(shape);
}
bool EffectsHandlerImpl::checkInputWindowEvent(QMouseEvent *e)
{
if (m_grabbedMouseEffects.isEmpty()) {
return false;
}
2021-11-03 11:55:31 +00:00
for (Effect *effect : qAsConst(m_grabbedMouseEffects)) {
effect->windowInputMouseEvent(e);
}
return true;
}
bool EffectsHandlerImpl::checkInputWindowEvent(QWheelEvent *e)
{
if (m_grabbedMouseEffects.isEmpty()) {
return false;
}
2021-11-03 11:55:31 +00:00
for (Effect *effect : qAsConst(m_grabbedMouseEffects)) {
effect->windowInputMouseEvent(e);
}
return true;
}
void EffectsHandlerImpl::connectNotify(const QMetaMethod &signal)
{
if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
if (!m_trackingCursorChanges) {
connect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
Cursors::self()->mouse()->startCursorTracking();
}
++m_trackingCursorChanges;
}
EffectsHandler::connectNotify(signal);
}
void EffectsHandlerImpl::disconnectNotify(const QMetaMethod &signal)
{
if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
Q_ASSERT(m_trackingCursorChanges > 0);
if (!--m_trackingCursorChanges) {
Cursors::self()->mouse()->stopCursorTracking();
disconnect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
}
}
EffectsHandler::disconnectNotify(signal);
}
void EffectsHandlerImpl::checkInputWindowStacking()
2011-01-30 14:34:42 +00:00
{
if (m_grabbedMouseEffects.isEmpty()) {
return;
}
doCheckInputWindowStacking();
}
void EffectsHandlerImpl::doCheckInputWindowStacking()
{
2011-01-30 14:34:42 +00:00
}
QPoint EffectsHandlerImpl::cursorPos() const
2011-01-30 14:34:42 +00:00
{
return Cursors::self()->mouse()->pos();
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::reserveElectricBorder(ElectricBorder border, Effect *effect)
2011-01-30 14:34:42 +00:00
{
ScreenEdges::self()->reserve(border, effect, "borderActivated");
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border, Effect *effect)
2011-01-30 14:34:42 +00:00
{
ScreenEdges::self()->unreserve(border, effect);
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::registerTouchBorder(ElectricBorder border, QAction *action)
{
ScreenEdges::self()->reserveTouch(border, action);
}
void EffectsHandlerImpl::registerRealtimeTouchBorder(ElectricBorder border, QAction *action, EffectsHandler::TouchBorderCallback progressCallback)
{
ScreenEdges::self()->reserveTouch(border, action, [progressCallback](ElectricBorder border, const QSizeF &deltaProgress, Output *output) {
progressCallback(border, deltaProgress, EffectScreenImpl::get(output));
});
}
void EffectsHandlerImpl::unregisterTouchBorder(ElectricBorder border, QAction *action)
{
ScreenEdges::self()->unreserveTouch(border, action);
}
QPainter *EffectsHandlerImpl::scenePainter()
{
return m_scene->scenePainter();
}
void EffectsHandlerImpl::toggleEffect(const QString &name)
2011-01-30 14:34:42 +00:00
{
if (isEffectLoaded(name)) {
2011-01-30 14:34:42 +00:00
unloadEffect(name);
} else {
2011-01-30 14:34:42 +00:00
loadEffect(name);
}
2011-01-30 14:34:42 +00:00
}
QStringList EffectsHandlerImpl::loadedEffects() const
2011-01-30 14:34:42 +00:00
{
QStringList listModules;
listModules.reserve(loaded_effects.count());
std::transform(loaded_effects.constBegin(), loaded_effects.constEnd(),
std::back_inserter(listModules),
[](const EffectPair &pair) {
return pair.first;
});
2011-01-30 14:34:42 +00:00
return listModules;
}
QStringList EffectsHandlerImpl::listOfEffects() const
2011-01-30 14:34:42 +00:00
{
return m_effectLoader->listOfKnownEffects();
}
bool EffectsHandlerImpl::loadEffect(const QString &name)
2011-01-30 14:34:42 +00:00
{
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
makeOpenGLContextCurrent();
m_compositor->scene()->addRepaintFull();
return m_effectLoader->loadEffect(name);
}
void EffectsHandlerImpl::unloadEffect(const QString &name)
2011-01-30 14:34:42 +00:00
{
auto it = std::find_if(effect_order.begin(), effect_order.end(),
[name](EffectPair &pair) {
return pair.first == name;
});
if (it == effect_order.end()) {
qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Effect not loaded :" << name;
return;
}
qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Unloading Effect :" << name;
destroyEffect((*it).second);
effect_order.erase(it);
effectsChanged();
m_compositor->scene()->addRepaintFull();
}
2011-01-30 14:34:42 +00:00
void EffectsHandlerImpl::destroyEffect(Effect *effect)
{
makeOpenGLContextCurrent();
if (fullscreen_effect == effect) {
setActiveFullScreenEffect(nullptr);
}
if (keyboard_grab_effect == effect) {
ungrabKeyboard();
}
stopMouseInterception(effect);
const QList<QByteArray> properties = m_propertiesForEffects.keys();
for (const QByteArray &property : properties) {
removeSupportProperty(property, effect);
}
delete effect;
2011-01-30 14:34:42 +00:00
}
void EffectsHandlerImpl::reconfigureEffect(const QString &name)
2011-01-30 14:34:42 +00:00
{
for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
2011-01-30 14:34:42 +00:00
if ((*it).first == name) {
kwinApp()->config()->reparseConfiguration();
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
makeOpenGLContextCurrent();
2011-01-30 14:34:42 +00:00
(*it).second->reconfigure(Effect::ReconfigureAll);
return;
2011-01-30 14:34:42 +00:00
}
}
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::isEffectLoaded(const QString &name) const
2011-01-30 14:34:42 +00:00
{
auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
[&name](const EffectPair &pair) {
return pair.first == name;
});
return it != loaded_effects.constEnd();
2011-01-30 14:34:42 +00:00
}
bool EffectsHandlerImpl::isEffectSupported(const QString &name)
{
// If the effect is loaded, it is obviously supported.
if (isEffectLoaded(name)) {
return true;
}
// next checks might require a context
makeOpenGLContextCurrent();
return m_effectLoader->isEffectSupported(name);
}
QList<bool> EffectsHandlerImpl::areEffectsSupported(const QStringList &names)
{
QList<bool> retList;
retList.reserve(names.count());
std::transform(names.constBegin(), names.constEnd(),
std::back_inserter(retList),
[this](const QString &name) {
return isEffectSupported(name);
});
return retList;
}
void EffectsHandlerImpl::reloadEffect(Effect *effect)
{
QString effectName;
for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if ((*it).second == effect) {
effectName = (*it).first;
break;
}
}
if (!effectName.isNull()) {
unloadEffect(effectName);
m_effectLoader->loadEffect(effectName);
}
}
void EffectsHandlerImpl::effectsChanged()
2011-01-30 14:34:42 +00:00
{
loaded_effects.clear();
m_activeEffects.clear(); // it's possible to have a reconfigure and a quad rebuild between two paint cycles - bug #308201
loaded_effects.reserve(effect_order.count());
std::copy(effect_order.constBegin(), effect_order.constEnd(),
std::back_inserter(loaded_effects));
m_activeEffects.reserve(loaded_effects.count());
2011-01-30 14:34:42 +00:00
}
QStringList EffectsHandlerImpl::activeEffects() const
{
QStringList ret;
for (QVector<KWin::EffectPair>::const_iterator it = loaded_effects.constBegin(),
end = loaded_effects.constEnd();
it != end; ++it) {
if (it->second->isActive()) {
ret << it->first;
}
}
return ret;
}
bool EffectsHandlerImpl::blocksDirectScanout() const
{
for (QVector<KWin::EffectPair>::const_iterator it = loaded_effects.constBegin(),
end = loaded_effects.constEnd();
it != end; ++it) {
if (it->second->isActive() && it->second->blocksDirectScanout()) {
return true;
}
}
return false;
}
KWaylandServer::Display *EffectsHandlerImpl::waylandDisplay() const
{
if (waylandServer()) {
return waylandServer()->display();
}
return nullptr;
}
EffectFrame *EffectsHandlerImpl::effectFrame(EffectFrameStyle style, bool staticSize, const QPoint &position, Qt::Alignment alignment) const
2011-01-30 14:34:42 +00:00
{
return new EffectFrameImpl(style, staticSize, position, alignment);
}
QVariant EffectsHandlerImpl::kwinOption(KWinOption kwopt)
{
2011-08-09 21:53:46 +00:00
switch (kwopt) {
case CloseButtonCorner: {
// TODO: this could become per window and be derived from the actual position in the deco
const auto settings = Decoration::DecorationBridge::self()->settings();
return settings && settings->decorationButtonsLeft().contains(KDecoration2::DecorationButtonType::Close) ? Qt::TopLeftCorner : Qt::TopRightCorner;
}
case SwitchDesktopOnScreenEdge:
return ScreenEdges::self()->isDesktopSwitching();
case SwitchDesktopOnScreenEdgeMovingWindows:
return ScreenEdges::self()->isDesktopSwitchingMovingClients();
default:
return QVariant(); // an invalid one
}
}
QString EffectsHandlerImpl::supportInformation(const QString &name) const
{
auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
[name](const EffectPair &pair) {
return pair.first == name;
});
if (it == loaded_effects.constEnd()) {
return QString();
}
QString support((*it).first + QLatin1String(":\n"));
const QMetaObject *metaOptions = (*it).second->metaObject();
for (int i = 0; i < metaOptions->propertyCount(); ++i) {
const QMetaProperty property = metaOptions->property(i);
if (qstrcmp(property.name(), "objectName") == 0) {
continue;
}
support += QString::fromUtf8(property.name()) + QLatin1String(": ") + (*it).second->property(property.name()).toString() + QLatin1Char('\n');
}
return support;
}
bool EffectsHandlerImpl::isScreenLocked() const
{
#if KWIN_BUILD_SCREENLOCKER
return ScreenLockerWatcher::self()->isLocked();
#else
return false;
#endif
}
QString EffectsHandlerImpl::debug(const QString &name, const QString &parameter) const
{
QString internalName = name.toLower();
for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
if ((*it).first == internalName) {
return it->second->debug(parameter);
}
}
return QString();
}
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
bool EffectsHandlerImpl::makeOpenGLContextCurrent()
{
return m_scene->makeOpenGLContextCurrent();
}
void EffectsHandlerImpl::doneOpenGLContextCurrent()
{
m_scene->doneOpenGLContextCurrent();
}
bool EffectsHandlerImpl::animationsSupported() const
{
static const QByteArray forceEnvVar = qgetenv("KWIN_EFFECTS_FORCE_ANIMATIONS");
if (!forceEnvVar.isEmpty()) {
static const int forceValue = forceEnvVar.toInt();
return forceValue == 1;
}
return m_scene->animationsSupported();
}
void EffectsHandlerImpl::highlightWindows(const QVector<EffectWindow *> &windows)
{
Effect *e = provides(Effect::HighlightWindows);
if (!e) {
return;
}
e->perform(Effect::HighlightWindows, QVariantList{QVariant::fromValue(windows)});
}
PlatformCursorImage EffectsHandlerImpl::cursorImage() const
{
return kwinApp()->platform()->cursorImage();
}
void EffectsHandlerImpl::hideCursor()
{
2021-12-25 17:12:12 +00:00
Cursors::self()->hideCursor();
}
void EffectsHandlerImpl::showCursor()
{
2021-12-25 17:12:12 +00:00
Cursors::self()->showCursor();
}
void EffectsHandlerImpl::startInteractiveWindowSelection(std::function<void(KWin::EffectWindow *)> callback)
{
2022-04-22 17:39:12 +00:00
kwinApp()->platform()->startInteractiveWindowSelection([callback](KWin::Window *t) {
if (t && t->effectWindow()) {
callback(t->effectWindow());
} else {
callback(nullptr);
}
});
}
void EffectsHandlerImpl::startInteractivePositionSelection(std::function<void(const QPoint &)> callback)
{
kwinApp()->platform()->startInteractivePositionSelection(callback);
}
void EffectsHandlerImpl::showOnScreenMessage(const QString &message, const QString &iconName)
{
OSD::show(message, iconName);
}
void EffectsHandlerImpl::hideOnScreenMessage(OnScreenMessageHideFlags flags)
{
OSD::HideFlags osdFlags;
if (flags.testFlag(OnScreenMessageHideFlag::SkipsCloseAnimation)) {
osdFlags |= OSD::HideFlag::SkipCloseAnimation;
}
OSD::hide(osdFlags);
}
KSharedConfigPtr EffectsHandlerImpl::config() const
{
return kwinApp()->config();
}
KSharedConfigPtr EffectsHandlerImpl::inputConfig() const
{
return InputConfig::self()->inputConfig();
}
Effect *EffectsHandlerImpl::findEffect(const QString &name) const
{
auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(), [name](const EffectPair &pair) {
return pair.first == name;
});
if (it == loaded_effects.constEnd()) {
return nullptr;
}
return (*it).second;
}
void EffectsHandlerImpl::renderOffscreenQuickView(OffscreenQuickView *w) const
[libkwineffects] Introduce API to easily show a QtQuick scene in an effect Summary: EffectQuickView/Scene is a convenient class to render a QtQuick scenegraph into an effect. Current methods (such as present windows) involve creating an underlying platform window which is expensive, causes a headache to filter out again in the rest of the code, and only works as an overlay. The new class exposes things more natively to an effect where we don't mess with real windows, we can perform the painting anywhere in the view and we don't have issues with hiding/closing. QtQuick has both software and hardware accelerated modes, and kwin also has 3 render backends. Every combination is supported. * When used in OpenGL mode for both, we render into an FBO export the texture ID then it's up to the effect to render that into a scene. * When using software QtQuick rendering we blit into an image, upload that into a KWinGLTexture which serves as an abstraction layer and render that into the scene. * When using GL for QtQuick and XRender/QPainter in kwin everything is rendered into the internal FBO, blit and exported as an image. * When using software rendering for both an image gets passed directly. Mouse and keyboard events can be forwarded, only if the effect intercepts them. The class is meant to be generic enough that we can remove all the QtQuick code from Aurorae. The intention is also to replace EffectFrameImpl using this backend and we can kill all of the EffectFrame code throughout the scenes. The close button in present windows will also be ported to this, simplifiying that code base. Classes that handle the rendering and handling QML are intentionally split so that in the future we can have a declarative effects API create overlays from within the same context. Similar to how one can instantiate windows from a typical QML scene. Notes: I don't like how I pass the kwin GL context from the backends into the effect, but I need something that works with the library separation. It also currently has wayland problem if I create a QOpenGLContext before the QPA is set up with a scene - but I don't have anything better? I know for the EffectFrame we need an API to push things through the effects stack to handle blur/invert etc. Will deal with that when we port the EffectFrame. Test Plan: Used in an effect Reviewers: #kwin, zzag Reviewed By: #kwin, zzag Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D24215
2019-09-27 15:06:37 +00:00
{
if (!w->isVisible()) {
return;
}
scene()->paintOffscreenQuickView(w);
[libkwineffects] Introduce API to easily show a QtQuick scene in an effect Summary: EffectQuickView/Scene is a convenient class to render a QtQuick scenegraph into an effect. Current methods (such as present windows) involve creating an underlying platform window which is expensive, causes a headache to filter out again in the rest of the code, and only works as an overlay. The new class exposes things more natively to an effect where we don't mess with real windows, we can perform the painting anywhere in the view and we don't have issues with hiding/closing. QtQuick has both software and hardware accelerated modes, and kwin also has 3 render backends. Every combination is supported. * When used in OpenGL mode for both, we render into an FBO export the texture ID then it's up to the effect to render that into a scene. * When using software QtQuick rendering we blit into an image, upload that into a KWinGLTexture which serves as an abstraction layer and render that into the scene. * When using GL for QtQuick and XRender/QPainter in kwin everything is rendered into the internal FBO, blit and exported as an image. * When using software rendering for both an image gets passed directly. Mouse and keyboard events can be forwarded, only if the effect intercepts them. The class is meant to be generic enough that we can remove all the QtQuick code from Aurorae. The intention is also to replace EffectFrameImpl using this backend and we can kill all of the EffectFrame code throughout the scenes. The close button in present windows will also be ported to this, simplifiying that code base. Classes that handle the rendering and handling QML are intentionally split so that in the future we can have a declarative effects API create overlays from within the same context. Similar to how one can instantiate windows from a typical QML scene. Notes: I don't like how I pass the kwin GL context from the backends into the effect, but I need something that works with the library separation. It also currently has wayland problem if I create a QOpenGLContext before the QPA is set up with a scene - but I don't have anything better? I know for the EffectFrame we need an API to push things through the effects stack to handle blur/invert etc. Will deal with that when we port the EffectFrame. Test Plan: Used in an effect Reviewers: #kwin, zzag Reviewed By: #kwin, zzag Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D24215
2019-09-27 15:06:37 +00:00
}
SessionState EffectsHandlerImpl::sessionState() const
{
return Workspace::self()->sessionManager()->state();
}
QList<EffectScreen *> EffectsHandlerImpl::screens() const
{
return m_effectScreens;
}
EffectScreen *EffectsHandlerImpl::screenAt(const QPoint &point) const
{
return EffectScreenImpl::get(kwinApp()->platform()->outputAt(point));
}
EffectScreen *EffectsHandlerImpl::findScreen(const QString &name) const
{
for (EffectScreen *screen : qAsConst(m_effectScreens)) {
if (screen->name() == name) {
return screen;
}
}
return nullptr;
}
EffectScreen *EffectsHandlerImpl::findScreen(int screenId) const
{
return m_effectScreens.value(screenId);
}
void EffectsHandlerImpl::slotOutputEnabled(Output *output)
{
EffectScreen *screen = new EffectScreenImpl(output, this);
m_effectScreens.append(screen);
Q_EMIT screenAdded(screen);
}
void EffectsHandlerImpl::slotOutputDisabled(Output *output)
{
EffectScreen *screen = EffectScreenImpl::get(output);
m_effectScreens.removeOne(screen);
Q_EMIT screenRemoved(screen);
delete screen;
}
void EffectsHandlerImpl::renderScreen(EffectScreen *screen)
{
RenderTarget renderTarget(GLFramebuffer::currentFramebuffer());
renderTarget.setDevicePixelRatio(screen->devicePixelRatio());
auto output = static_cast<EffectScreenImpl *>(screen)->platformOutput();
m_scene->prePaint(output);
m_scene->paint(&renderTarget, output->geometry());
m_scene->postPaint();
}
bool EffectsHandlerImpl::isCursorHidden() const
{
return Cursors::self()->isCursorHidden();
}
QRect EffectsHandlerImpl::renderTargetRect() const
{
return m_scene->renderTargetRect();
}
qreal EffectsHandlerImpl::renderTargetScale() const
{
return m_scene->renderTargetScale();
}
KWin::EffectWindow *EffectsHandlerImpl::inputPanel() const
{
if (!InputMethod::self() || !InputMethod::self()->isEnabled()) {
return nullptr;
}
auto panel = InputMethod::self()->panel();
if (panel) {
return panel->effectWindow();
}
return nullptr;
}
bool EffectsHandlerImpl::isInputPanelOverlay() const
{
if (!InputMethod::self() || !InputMethod::self()->isEnabled()) {
return true;
}
auto panel = InputMethod::self()->panel();
if (panel) {
return panel->mode() == InputPanelV1Window::Overlay;
}
return true;
}
//****************************************
// EffectScreenImpl
//****************************************
EffectScreenImpl::EffectScreenImpl(Output *output, QObject *parent)
: EffectScreen(parent)
, m_platformOutput(output)
{
m_platformOutput->m_effectScreen = this;
connect(output, &Output::aboutToChange, this, &EffectScreen::aboutToChange);
connect(output, &Output::changed, this, &EffectScreen::changed);
connect(output, &Output::wakeUp, this, &EffectScreen::wakeUp);
connect(output, &Output::aboutToTurnOff, this, &EffectScreen::aboutToTurnOff);
connect(output, &Output::scaleChanged, this, &EffectScreen::devicePixelRatioChanged);
connect(output, &Output::geometryChanged, this, &EffectScreen::geometryChanged);
}
EffectScreenImpl::~EffectScreenImpl()
{
if (m_platformOutput) {
m_platformOutput->m_effectScreen = nullptr;
}
}
EffectScreenImpl *EffectScreenImpl::get(Output *output)
{
return output->m_effectScreen;
}
Output *EffectScreenImpl::platformOutput() const
{
return m_platformOutput;
}
QString EffectScreenImpl::name() const
{
return m_platformOutput->name();
}
qreal EffectScreenImpl::devicePixelRatio() const
{
return m_platformOutput->scale();
}
QRect EffectScreenImpl::geometry() const
{
return m_platformOutput->geometry();
}
int EffectScreenImpl::refreshRate() const
{
return m_platformOutput->refreshRate();
}
EffectScreen::Transform EffectScreenImpl::transform() const
{
return EffectScreen::Transform(m_platformOutput->transform());
}
//****************************************
// EffectWindowImpl
//****************************************
2022-04-22 17:39:12 +00:00
EffectWindowImpl::EffectWindowImpl(Window *toplevel)
2011-12-29 09:31:37 +00:00
: EffectWindow(toplevel)
, toplevel(toplevel)
, sw(nullptr)
2011-01-30 14:34:42 +00:00
{
// Deleted windows are not managed. So, when windowClosed signal is
// emitted, effects can't distinguish managed windows from unmanaged
// windows(e.g. combo box popups, popup menus, etc). Save value of the
// managed property during construction of EffectWindow. At that time,
// parent can be Client, XdgShellClient, or Unmanaged. So, later on, when
// an instance of Deleted becomes parent of the EffectWindow, effects
// can still figure out whether it is/was a managed window.
managed = toplevel->isClient();
waylandClient = qobject_cast<KWin::WaylandWindow *>(toplevel) != nullptr;
x11Client = qobject_cast<KWin::X11Window *>(toplevel) != nullptr || qobject_cast<KWin::Unmanaged *>(toplevel) != nullptr;
2011-01-30 14:34:42 +00:00
}
EffectWindowImpl::~EffectWindowImpl()
2011-01-30 14:34:42 +00:00
{
QVariant cachedTextureVariant = data(LanczosCacheRole);
if (cachedTextureVariant.isValid()) {
GLTexture *cachedTexture = static_cast<GLTexture *>(cachedTextureVariant.value<void *>());
delete cachedTexture;
}
2011-01-30 14:34:42 +00:00
}
bool EffectWindowImpl::isPaintingEnabled()
2011-01-30 14:34:42 +00:00
{
return sceneWindow()->isPaintingEnabled();
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void EffectWindowImpl::enablePainting(int reason)
{
sceneWindow()->enablePainting(reason);
}
2011-01-30 14:34:42 +00:00
void EffectWindowImpl::disablePainting(int reason)
{
sceneWindow()->disablePainting(reason);
}
void EffectWindowImpl::addRepaint(const QRect &r)
{
toplevel->addRepaint(r);
}
void EffectWindowImpl::addRepaint(int x, int y, int w, int h)
{
toplevel->addRepaint(x, y, w, h);
}
void EffectWindowImpl::addRepaintFull()
{
toplevel->addRepaintFull();
}
void EffectWindowImpl::addLayerRepaint(const QRect &r)
{
toplevel->addLayerRepaint(r);
}
void EffectWindowImpl::addLayerRepaint(int x, int y, int w, int h)
{
toplevel->addLayerRepaint(x, y, w, h);
}
const EffectWindowGroup *EffectWindowImpl::group() const
2011-01-30 14:34:42 +00:00
{
if (auto c = qobject_cast<X11Window *>(toplevel)) {
return c->group()->effectGroup();
}
return nullptr; // TODO
2011-01-30 14:34:42 +00:00
}
void EffectWindowImpl::refWindow()
2011-01-30 14:34:42 +00:00
{
if (auto d = static_cast<Deleted *>(toplevel->isDeleted() ? toplevel : nullptr)) {
return d->refWindow();
}
Q_UNREACHABLE(); // TODO
2011-01-30 14:34:42 +00:00
}
void EffectWindowImpl::unrefWindow()
2011-01-30 14:34:42 +00:00
{
if (auto d = static_cast<Deleted *>(toplevel->isDeleted() ? toplevel : nullptr)) {
return d->unrefWindow(); // delays deletion in case
}
Q_UNREACHABLE(); // TODO
2011-01-30 14:34:42 +00:00
}
EffectScreen *EffectWindowImpl::screen() const
{
return EffectScreenImpl::get(toplevel->output());
}
#define TOPLEVEL_HELPER(rettype, prototype, toplevelPrototype) \
rettype EffectWindowImpl::prototype() const \
{ \
return toplevel->toplevelPrototype(); \
}
TOPLEVEL_HELPER(double, opacity, opacity)
TOPLEVEL_HELPER(bool, hasAlpha, hasAlpha)
TOPLEVEL_HELPER(int, x, x)
TOPLEVEL_HELPER(int, y, y)
TOPLEVEL_HELPER(int, width, width)
TOPLEVEL_HELPER(int, height, height)
TOPLEVEL_HELPER(QPoint, pos, pos)
TOPLEVEL_HELPER(QSize, size, size)
TOPLEVEL_HELPER(QRect, geometry, frameGeometry)
TOPLEVEL_HELPER(QRect, frameGeometry, frameGeometry)
TOPLEVEL_HELPER(QRect, bufferGeometry, bufferGeometry)
TOPLEVEL_HELPER(QRect, clientGeometry, clientGeometry)
TOPLEVEL_HELPER(QRect, expandedGeometry, visibleGeometry)
TOPLEVEL_HELPER(QRect, rect, rect)
TOPLEVEL_HELPER(int, desktop, desktop)
TOPLEVEL_HELPER(bool, isDesktop, isDesktop)
TOPLEVEL_HELPER(bool, isDock, isDock)
TOPLEVEL_HELPER(bool, isToolbar, isToolbar)
TOPLEVEL_HELPER(bool, isMenu, isMenu)
TOPLEVEL_HELPER(bool, isNormalWindow, isNormalWindow)
TOPLEVEL_HELPER(bool, isDialog, isDialog)
TOPLEVEL_HELPER(bool, isSplash, isSplash)
TOPLEVEL_HELPER(bool, isUtility, isUtility)
TOPLEVEL_HELPER(bool, isDropdownMenu, isDropdownMenu)
TOPLEVEL_HELPER(bool, isPopupMenu, isPopupMenu)
TOPLEVEL_HELPER(bool, isTooltip, isTooltip)
TOPLEVEL_HELPER(bool, isNotification, isNotification)
TOPLEVEL_HELPER(bool, isCriticalNotification, isCriticalNotification)
TOPLEVEL_HELPER(bool, isOnScreenDisplay, isOnScreenDisplay)
TOPLEVEL_HELPER(bool, isComboBox, isComboBox)
TOPLEVEL_HELPER(bool, isDNDIcon, isDNDIcon)
TOPLEVEL_HELPER(bool, isDeleted, isDeleted)
TOPLEVEL_HELPER(QString, windowRole, windowRole)
TOPLEVEL_HELPER(QStringList, activities, activities)
TOPLEVEL_HELPER(bool, skipsCloseAnimation, skipsCloseAnimation)
TOPLEVEL_HELPER(KWaylandServer::SurfaceInterface *, surface, surface)
TOPLEVEL_HELPER(bool, isPopupWindow, isPopupWindow)
TOPLEVEL_HELPER(bool, isOutline, isOutline)
TOPLEVEL_HELPER(bool, isLockScreen, isLockScreen)
TOPLEVEL_HELPER(pid_t, pid, pid)
TOPLEVEL_HELPER(qlonglong, windowId, window)
2021-11-22 15:58:04 +00:00
TOPLEVEL_HELPER(QUuid, internalId, internalId)
#undef TOPLEVEL_HELPER
#define CLIENT_HELPER(rettype, prototype, propertyname, defaultValue) \
rettype EffectWindowImpl::prototype() const \
{ \
2022-04-22 17:39:12 +00:00
auto client = static_cast<Window *>(toplevel->isClient() ? toplevel : nullptr); \
if (client) { \
return client->propertyname(); \
} \
auto deleted = static_cast<Deleted *>(toplevel->isDeleted() ? toplevel : nullptr); \
if (deleted) { \
return deleted->propertyname(); \
} \
return defaultValue; \
}
CLIENT_HELPER(bool, isMinimized, isMinimized, false)
CLIENT_HELPER(bool, isModal, isModal, false)
CLIENT_HELPER(bool, isFullScreen, isFullScreen, false)
CLIENT_HELPER(bool, keepAbove, keepAbove, false)
CLIENT_HELPER(bool, keepBelow, keepBelow, false)
CLIENT_HELPER(QString, caption, caption, QString());
CLIENT_HELPER(QVector<uint>, desktops, x11DesktopIds, QVector<uint>());
CLIENT_HELPER(bool, isMovable, isMovable, false)
CLIENT_HELPER(bool, isMovableAcrossScreens, isMovableAcrossScreens, false)
CLIENT_HELPER(bool, isUserMove, isInteractiveMove, false)
CLIENT_HELPER(bool, isUserResize, isInteractiveResize, false)
CLIENT_HELPER(QRect, iconGeometry, iconGeometry, QRect())
CLIENT_HELPER(bool, isSpecialWindow, isSpecialWindow, true)
CLIENT_HELPER(bool, acceptsFocus, wantsInput, true) // We don't actually know...
CLIENT_HELPER(QIcon, icon, icon, QIcon())
CLIENT_HELPER(bool, isSkipSwitcher, skipSwitcher, false)
CLIENT_HELPER(bool, decorationHasAlpha, decorationHasAlpha, false)
CLIENT_HELPER(bool, isUnresponsive, unresponsive, false)
#undef CLIENT_HELPER
// legacy from tab groups, can be removed when no effects use this any more.
bool EffectWindowImpl::isCurrentTab() const
{
return true;
}
QString EffectWindowImpl::windowClass() const
{
return toplevel->resourceName() + QLatin1Char(' ') + toplevel->resourceClass();
}
QRect EffectWindowImpl::contentsRect() const
{
return QRect(toplevel->clientPos(), toplevel->clientSize());
}
NET::WindowType EffectWindowImpl::windowType() const
{
return toplevel->windowType();
}
QSize EffectWindowImpl::basicUnit() const
{
if (auto client = qobject_cast<X11Window *>(toplevel)) {
return client->basicUnit();
}
return QSize(1, 1);
}
2022-04-22 17:39:12 +00:00
void EffectWindowImpl::setWindow(Window *w)
2011-01-30 14:34:42 +00:00
{
toplevel = w;
2011-12-29 09:31:37 +00:00
setParent(w);
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
void EffectWindowImpl::setSceneWindow(SceneWindow *w)
2011-01-30 14:34:42 +00:00
{
sw = w;
2011-01-30 14:34:42 +00:00
}
QRect EffectWindowImpl::decorationInnerRect() const
2011-01-30 14:34:42 +00:00
{
return toplevel->rect() - toplevel->frameMargins();
2011-01-30 14:34:42 +00:00
}
KDecoration2::Decoration *EffectWindowImpl::decoration() const
{
return toplevel->decoration();
}
2011-01-30 14:34:42 +00:00
QByteArray EffectWindowImpl::readProperty(long atom, long type, int format) const
{
if (!kwinApp()->x11Connection()) {
return QByteArray();
}
2011-01-30 14:34:42 +00:00
return readWindowProperty(window()->window(), atom, type, format);
}
void EffectWindowImpl::deleteProperty(long int atom) const
2011-01-30 14:34:42 +00:00
{
if (kwinApp()->x11Connection()) {
deleteWindowProperty(window()->window(), atom);
}
2011-01-30 14:34:42 +00:00
}
EffectWindow *EffectWindowImpl::findModal()
2011-01-30 14:34:42 +00:00
{
2022-04-22 17:39:12 +00:00
Window *modal = toplevel->findModal();
if (modal) {
return modal->effectWindow();
}
return nullptr;
2011-01-30 14:34:42 +00:00
}
EffectWindow *EffectWindowImpl::transientFor()
{
2022-04-22 17:39:12 +00:00
Window *transientFor = toplevel->transientFor();
if (transientFor) {
return transientFor->effectWindow();
}
return nullptr;
}
Add windowsystem plugin for KWin's qpa Summary: KWindowSystem provides a plugin interface to have platform specific implementations. So far KWin relied on the implementation in KWayland-integration repository. This is something I find unsuited, for the following reasons: * any test in KWin for functionality set through the plugin would fail * it's not clear what's going on where * in worst case some code could deadlock * KWin shouldn't use KWindowSystem and only a small subset is allowed to be used The last point needs some further explanation. KWin internally does not and cannot use KWindowSystem. KWindowSystem (especially KWindowInfo) is exposing information which KWin sets. It's more than weird if KWin asks KWindowSystem for the state of a window it set itself. On X11 it's just slow, on Wayland it can result in roundtrips to KWin itself which is dangerous. But due to using Plasma components we have a few areas where we use KWindowSystem. E.g. a Plasma::Dialog sets a window type, the slide in direction, blur and background contrast. This we want to support and need to support. Other API elements we do not want, like for examples the available windows. KWin internal windows either have direct access to KWin or a scripting interface exposed providing (limited) access - there is just no need to have this in KWindowSystem. To make it more clear what KWin supports as API of KWindowSystem for internal windows this change implements a stripped down version of the kwayland-integration plugin. The main difference is that it does not use KWayland at all, but a QWindow internal side channel. To support this EffectWindow provides an accessor for internalWindow and the three already mentioned effects are adjusted to read from the internal QWindow and it's dynamic properties. This change is a first step for a further refactoring. I plan to split the internal window out of ShellClient into a dedicated class. I think there are nowadays too many special cases. If it moves out there is the question whether we really want to use Wayland for the internal windows or whether this is just historic ballast (after all we used to use qwayland for that in the beginning). As the change could introduce regressions I'm targetting 5.16. Test Plan: new test case for window type, manual testing using Alt+Tab for the effects integration. Sliding popups, blur and contrast worked fine. Reviewers: #kwin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D18228
2019-01-13 16:50:32 +00:00
QWindow *EffectWindowImpl::internalWindow() const
{
auto client = qobject_cast<InternalWindow *>(toplevel);
Add windowsystem plugin for KWin's qpa Summary: KWindowSystem provides a plugin interface to have platform specific implementations. So far KWin relied on the implementation in KWayland-integration repository. This is something I find unsuited, for the following reasons: * any test in KWin for functionality set through the plugin would fail * it's not clear what's going on where * in worst case some code could deadlock * KWin shouldn't use KWindowSystem and only a small subset is allowed to be used The last point needs some further explanation. KWin internally does not and cannot use KWindowSystem. KWindowSystem (especially KWindowInfo) is exposing information which KWin sets. It's more than weird if KWin asks KWindowSystem for the state of a window it set itself. On X11 it's just slow, on Wayland it can result in roundtrips to KWin itself which is dangerous. But due to using Plasma components we have a few areas where we use KWindowSystem. E.g. a Plasma::Dialog sets a window type, the slide in direction, blur and background contrast. This we want to support and need to support. Other API elements we do not want, like for examples the available windows. KWin internal windows either have direct access to KWin or a scripting interface exposed providing (limited) access - there is just no need to have this in KWindowSystem. To make it more clear what KWin supports as API of KWindowSystem for internal windows this change implements a stripped down version of the kwayland-integration plugin. The main difference is that it does not use KWayland at all, but a QWindow internal side channel. To support this EffectWindow provides an accessor for internalWindow and the three already mentioned effects are adjusted to read from the internal QWindow and it's dynamic properties. This change is a first step for a further refactoring. I plan to split the internal window out of ShellClient into a dedicated class. I think there are nowadays too many special cases. If it moves out there is the question whether we really want to use Wayland for the internal windows or whether this is just historic ballast (after all we used to use qwayland for that in the beginning). As the change could introduce regressions I'm targetting 5.16. Test Plan: new test case for window type, manual testing using Alt+Tab for the effects integration. Sliding popups, blur and contrast worked fine. Reviewers: #kwin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D18228
2019-01-13 16:50:32 +00:00
if (!client) {
return nullptr;
}
return client->internalWindow();
}
template<typename T>
EffectWindowList getMainWindows(T *c)
2011-01-30 14:34:42 +00:00
{
const auto mainwindows = c->mainWindows();
EffectWindowList ret;
ret.reserve(mainwindows.size());
std::transform(std::cbegin(mainwindows), std::cend(mainwindows),
std::back_inserter(ret),
[](auto window) {
return window->effectWindow();
});
return ret;
}
EffectWindowList EffectWindowImpl::mainWindows() const
{
2022-04-22 17:39:12 +00:00
if (auto client = static_cast<Window *>(toplevel->isClient() ? toplevel : nullptr)) {
return getMainWindows(client);
}
if (auto deleted = static_cast<Deleted *>(toplevel->isDeleted() ? toplevel : nullptr)) {
return getMainWindows(deleted);
}
return {};
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void EffectWindowImpl::setData(int role, const QVariant &data)
{
if (!data.isNull()) {
dataMap[role] = data;
} else {
2011-01-30 14:34:42 +00:00
dataMap.remove(role);
}
Q_EMIT effects->windowDataChanged(this, role);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
QVariant EffectWindowImpl::data(int role) const
{
return dataMap.value(role);
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
EffectWindow *effectWindow(Window *w)
2011-01-30 14:34:42 +00:00
{
EffectWindowImpl *ret = w->effectWindow();
return ret;
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
EffectWindow *effectWindow(SceneWindow *w)
2011-01-30 14:34:42 +00:00
{
EffectWindowImpl *ret = w->window()->effectWindow();
2011-01-30 14:34:42 +00:00
ret->setSceneWindow(w);
return ret;
2011-01-30 14:34:42 +00:00
}
void EffectWindowImpl::elevate(bool elevate)
{
effects->setElevatedWindow(this, elevate);
}
void EffectWindowImpl::minimize()
{
if (toplevel->isClient()) {
toplevel->minimize();
}
}
void EffectWindowImpl::unminimize()
{
if (toplevel->isClient()) {
toplevel->unminimize();
}
}
void EffectWindowImpl::closeWindow()
{
if (toplevel->isClient()) {
toplevel->closeWindow();
}
}
void EffectWindowImpl::referencePreviousWindowPixmap()
{
if (sw) {
sw->referencePreviousPixmap();
}
}
void EffectWindowImpl::unreferencePreviousWindowPixmap()
{
if (sw) {
sw->unreferencePreviousPixmap();
}
}
bool EffectWindowImpl::isManaged() const
{
return managed;
}
bool EffectWindowImpl::isWaylandClient() const
{
return waylandClient;
}
bool EffectWindowImpl::isX11Client() const
{
return x11Client;
}
//****************************************
// EffectWindowGroupImpl
//****************************************
EffectWindowList EffectWindowGroupImpl::members() const
2011-01-30 14:34:42 +00:00
{
const auto memberList = group->members();
EffectWindowList ret;
ret.reserve(memberList.size());
std::transform(std::cbegin(memberList), std::cend(memberList),
std::back_inserter(ret),
[](auto toplevel) {
return toplevel->effectWindow();
});
return ret;
2011-01-30 14:34:42 +00:00
}
//****************************************
// EffectFrameImpl
//****************************************
2011-01-30 14:34:42 +00:00
EffectFrameImpl::EffectFrameImpl(EffectFrameStyle style, bool staticSize, QPoint position, Qt::Alignment alignment)
: QObject(nullptr)
, EffectFrame()
2011-01-30 14:34:42 +00:00
, m_style(style)
, m_static(staticSize)
, m_point(position)
, m_alignment(alignment)
, m_shader(nullptr)
, m_theme(new Plasma::Theme(this))
2011-01-30 14:34:42 +00:00
{
if (m_style == EffectFrameStyled) {
m_frame.setImagePath(QStringLiteral("widgets/background"));
2011-01-30 14:34:42 +00:00
m_frame.setCacheAllRenderedFrames(true);
connect(m_theme, &Plasma::Theme::themeChanged, this, &EffectFrameImpl::plasmaThemeChanged);
2011-01-30 14:34:42 +00:00
}
m_selection.setImagePath(QStringLiteral("widgets/viewitem"));
m_selection.setElementPrefix(QStringLiteral("hover"));
m_selection.setCacheAllRenderedFrames(true);
m_selection.setEnabledBorders(Plasma::FrameSvg::AllBorders);
m_sceneFrame = Compositor::self()->scene()->createEffectFrame(this);
2011-01-30 14:34:42 +00:00
}
EffectFrameImpl::~EffectFrameImpl()
2011-01-30 14:34:42 +00:00
{
delete m_sceneFrame;
2011-01-30 14:34:42 +00:00
}
const QFont &EffectFrameImpl::font() const
2011-01-30 14:34:42 +00:00
{
return m_font;
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::setFont(const QFont &font)
2011-01-30 14:34:42 +00:00
{
if (m_font == font) {
return;
2011-01-30 14:34:42 +00:00
}
m_font = font;
QRect oldGeom = m_geometry;
2011-01-30 14:34:42 +00:00
if (!m_text.isEmpty()) {
autoResize();
2011-01-30 14:34:42 +00:00
}
if (oldGeom == m_geometry) {
// Wasn't updated in autoResize()
m_sceneFrame->freeTextFrame();
}
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::free()
2011-01-30 14:34:42 +00:00
{
m_sceneFrame->free();
2011-01-30 14:34:42 +00:00
}
const QRect &EffectFrameImpl::geometry() const
2011-01-30 14:34:42 +00:00
{
return m_geometry;
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::setGeometry(const QRect &geometry, bool force)
2011-01-30 14:34:42 +00:00
{
QRect oldGeom = m_geometry;
m_geometry = geometry;
2011-01-30 14:34:42 +00:00
if (m_geometry == oldGeom && !force) {
return;
2011-01-30 14:34:42 +00:00
}
effects->addRepaint(oldGeom);
effects->addRepaint(m_geometry);
if (m_geometry.size() == oldGeom.size() && !force) {
return;
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
if (m_style == EffectFrameStyled) {
qreal left, top, right, bottom;
m_frame.getMargins(left, top, right, bottom); // m_geometry is the inner geometry
2011-01-30 14:34:42 +00:00
m_frame.resizeFrame(m_geometry.adjusted(-left, -top, right, bottom).size());
}
free();
2011-01-30 14:34:42 +00:00
}
const QIcon &EffectFrameImpl::icon() const
2011-01-30 14:34:42 +00:00
{
return m_icon;
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::setIcon(const QIcon &icon)
2011-01-30 14:34:42 +00:00
{
m_icon = icon;
2011-01-30 14:34:42 +00:00
if (isCrossFade()) {
m_sceneFrame->crossFadeIcon();
}
if (m_iconSize.isEmpty() && !m_icon.availableSizes().isEmpty()) { // Set a size if we don't already have one
setIconSize(m_icon.availableSizes().constFirst());
2011-01-30 14:34:42 +00:00
}
m_sceneFrame->freeIconFrame();
}
const QSize &EffectFrameImpl::iconSize() const
2011-01-30 14:34:42 +00:00
{
return m_iconSize;
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::setIconSize(const QSize &size)
2011-01-30 14:34:42 +00:00
{
if (m_iconSize == size) {
return;
2011-01-30 14:34:42 +00:00
}
m_iconSize = size;
autoResize();
m_sceneFrame->freeIconFrame();
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::plasmaThemeChanged()
2011-01-30 14:34:42 +00:00
{
free();
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::render(const QRegion &region, double opacity, double frameOpacity)
2011-01-30 14:34:42 +00:00
{
if (m_geometry.isEmpty()) {
return; // Nothing to display
}
m_shader = nullptr;
setScreenProjectionMatrix(static_cast<EffectsHandlerImpl *>(effects)->scene()->screenProjectionMatrix());
2011-01-30 14:34:42 +00:00
effects->paintEffectFrame(this, region, opacity, frameOpacity);
}
2011-01-30 14:34:42 +00:00
void EffectFrameImpl::finalRender(QRegion region, double opacity, double frameOpacity) const
{
region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL
2011-01-30 14:34:42 +00:00
m_sceneFrame->render(region, opacity, frameOpacity);
}
Qt::Alignment EffectFrameImpl::alignment() const
2011-01-30 14:34:42 +00:00
{
return m_alignment;
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::align(QRect &geometry)
{
if (m_alignment & Qt::AlignLeft) {
2011-01-30 14:34:42 +00:00
geometry.moveLeft(m_point.x());
} else if (m_alignment & Qt::AlignRight) {
2011-01-30 14:34:42 +00:00
geometry.moveLeft(m_point.x() - geometry.width());
} else {
2011-01-30 14:34:42 +00:00
geometry.moveLeft(m_point.x() - geometry.width() / 2);
}
if (m_alignment & Qt::AlignTop) {
2011-01-30 14:34:42 +00:00
geometry.moveTop(m_point.y());
} else if (m_alignment & Qt::AlignBottom) {
2011-01-30 14:34:42 +00:00
geometry.moveTop(m_point.y() - geometry.height());
} else {
2011-01-30 14:34:42 +00:00
geometry.moveTop(m_point.y() - geometry.height() / 2);
}
}
2011-01-30 14:34:42 +00:00
void EffectFrameImpl::setAlignment(Qt::Alignment alignment)
{
m_alignment = alignment;
2011-01-30 14:34:42 +00:00
align(m_geometry);
setGeometry(m_geometry);
}
void EffectFrameImpl::setPosition(const QPoint &point)
2011-01-30 14:34:42 +00:00
{
m_point = point;
QRect geometry = m_geometry; // this is important, setGeometry need call repaint for old & new geometry
2011-01-30 14:34:42 +00:00
align(geometry);
setGeometry(geometry);
}
const QString &EffectFrameImpl::text() const
2011-01-30 14:34:42 +00:00
{
return m_text;
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::setText(const QString &text)
2011-01-30 14:34:42 +00:00
{
if (m_text == text) {
return;
2011-01-30 14:34:42 +00:00
}
if (isCrossFade()) {
m_sceneFrame->crossFadeText();
2011-01-30 14:34:42 +00:00
}
m_text = text;
QRect oldGeom = m_geometry;
autoResize();
2011-01-30 14:34:42 +00:00
if (oldGeom == m_geometry) {
// Wasn't updated in autoResize()
m_sceneFrame->freeTextFrame();
}
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::setSelection(const QRect &selection)
2011-01-30 14:34:42 +00:00
{
if (selection == m_selectionGeometry) {
return;
2011-01-30 14:34:42 +00:00
}
m_selectionGeometry = selection;
2011-01-30 14:34:42 +00:00
if (m_selectionGeometry.size() != m_selection.frameSize().toSize()) {
m_selection.resizeFrame(m_selectionGeometry.size());
}
// TODO; optimize to only recreate when resizing
m_sceneFrame->freeSelection();
2011-01-30 14:34:42 +00:00
}
void EffectFrameImpl::autoResize()
2011-01-30 14:34:42 +00:00
{
if (m_static) {
return; // Not automatically resizing
}
QRect geometry;
// Set size
2011-01-30 14:34:42 +00:00
if (!m_text.isEmpty()) {
QFontMetrics metrics(m_font);
geometry.setSize(metrics.size(0, m_text));
}
if (!m_icon.isNull() && !m_iconSize.isEmpty()) {
geometry.setLeft(-m_iconSize.width());
if (m_iconSize.height() > geometry.height()) {
2011-01-30 14:34:42 +00:00
geometry.setHeight(m_iconSize.height());
}
}
2011-01-30 14:34:42 +00:00
align(geometry);
setGeometry(geometry);
}
QColor EffectFrameImpl::styledTextColor()
{
return m_theme->color(Plasma::Theme::TextColor);
}
} // namespace