File indexing completed on 2024-11-10 04:56:59

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2007 Rivo Laks <rivolaks@hot.ee>
0006     SPDX-FileCopyrightText: 2008 Lucas Murray <lmurray@undefinedfire.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "invert.h"
0012 
0013 #include "effect/effecthandler.h"
0014 #include "opengl/glplatform.h"
0015 #include "opengl/glutils.h"
0016 #include <KGlobalAccel>
0017 #include <KLocalizedString>
0018 #include <QAction>
0019 #include <QFile>
0020 #include <QStandardPaths>
0021 
0022 #include <QMatrix4x4>
0023 
0024 Q_LOGGING_CATEGORY(KWIN_INVERT, "kwin_effect_invert", QtWarningMsg)
0025 
0026 static void ensureResources()
0027 {
0028     // Must initialize resources manually because the effect is a static lib.
0029     Q_INIT_RESOURCE(invert);
0030 }
0031 
0032 namespace KWin
0033 {
0034 
0035 InvertEffect::InvertEffect()
0036     : m_inited(false)
0037     , m_valid(true)
0038     , m_shader(nullptr)
0039     , m_allWindows(false)
0040 {
0041     QAction *a = new QAction(this);
0042     a->setObjectName(QStringLiteral("Invert"));
0043     a->setText(i18n("Toggle Invert Effect"));
0044     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::CTRL | Qt::META | Qt::Key_I));
0045     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::CTRL | Qt::META | Qt::Key_I));
0046     connect(a, &QAction::triggered, this, &InvertEffect::toggleScreenInversion);
0047 
0048     QAction *b = new QAction(this);
0049     b->setObjectName(QStringLiteral("InvertWindow"));
0050     b->setText(i18n("Toggle Invert Effect on Window"));
0051     KGlobalAccel::self()->setDefaultShortcut(b, QList<QKeySequence>() << (Qt::CTRL | Qt::META | Qt::Key_U));
0052     KGlobalAccel::self()->setShortcut(b, QList<QKeySequence>() << (Qt::CTRL | Qt::META | Qt::Key_U));
0053     connect(b, &QAction::triggered, this, &InvertEffect::toggleWindow);
0054 
0055     QAction *c = new QAction(this);
0056     c->setObjectName(QStringLiteral("Invert Screen Colors"));
0057     c->setText(i18n("Invert Screen Colors"));
0058     KGlobalAccel::self()->setDefaultShortcut(c, QList<QKeySequence>());
0059     KGlobalAccel::self()->setShortcut(c, QList<QKeySequence>());
0060     connect(c, &QAction::triggered, this, &InvertEffect::toggleScreenInversion);
0061 
0062     connect(effects, &EffectsHandler::windowAdded, this, &InvertEffect::slotWindowAdded);
0063     connect(effects, &EffectsHandler::windowClosed, this, &InvertEffect::slotWindowClosed);
0064 }
0065 
0066 InvertEffect::~InvertEffect() = default;
0067 
0068 bool InvertEffect::supported()
0069 {
0070     return effects->compositingType() == OpenGLCompositing;
0071 }
0072 
0073 bool InvertEffect::isInvertable(EffectWindow *window) const
0074 {
0075     return m_allWindows != m_windows.contains(window);
0076 }
0077 
0078 void InvertEffect::invert(EffectWindow *window)
0079 {
0080     if (m_valid && !m_inited) {
0081         m_valid = loadData();
0082     }
0083 
0084     redirect(window);
0085     setShader(window, m_shader.get());
0086 }
0087 
0088 void InvertEffect::uninvert(EffectWindow *window)
0089 {
0090     unredirect(window);
0091 }
0092 
0093 bool InvertEffect::loadData()
0094 {
0095     ensureResources();
0096     m_inited = true;
0097 
0098     m_shader = ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture, QString(), QStringLiteral(":/effects/invert/shaders/invert.frag"));
0099     if (!m_shader->isValid()) {
0100         qCCritical(KWIN_INVERT) << "The shader failed to load!";
0101         return false;
0102     }
0103 
0104     return true;
0105 }
0106 
0107 void InvertEffect::slotWindowAdded(KWin::EffectWindow *w)
0108 {
0109     if (isInvertable(w)) {
0110         invert(w);
0111     }
0112 }
0113 
0114 void InvertEffect::slotWindowClosed(EffectWindow *w)
0115 {
0116     m_windows.removeOne(w);
0117 }
0118 
0119 void InvertEffect::toggleScreenInversion()
0120 {
0121     m_allWindows = !m_allWindows;
0122 
0123     const auto windows = effects->stackingOrder();
0124     for (EffectWindow *window : windows) {
0125         if (isInvertable(window)) {
0126             invert(window);
0127         } else {
0128             uninvert(window);
0129         }
0130     }
0131 
0132     effects->addRepaintFull();
0133 }
0134 
0135 void InvertEffect::toggleWindow()
0136 {
0137     if (!effects->activeWindow()) {
0138         return;
0139     }
0140     if (!m_windows.contains(effects->activeWindow())) {
0141         m_windows.append(effects->activeWindow());
0142     } else {
0143         m_windows.removeOne(effects->activeWindow());
0144     }
0145     if (isInvertable(effects->activeWindow())) {
0146         invert(effects->activeWindow());
0147     } else {
0148         uninvert(effects->activeWindow());
0149     }
0150     effects->activeWindow()->addRepaintFull();
0151 }
0152 
0153 bool InvertEffect::isActive() const
0154 {
0155     return m_valid && (m_allWindows || !m_windows.isEmpty());
0156 }
0157 
0158 bool InvertEffect::provides(Feature f)
0159 {
0160     return f == ScreenInversion;
0161 }
0162 
0163 } // namespace
0164 
0165 #include "moc_invert.cpp"