/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "main.h" #include "dbus.h" #include "kwin_interface.h" #include "kwinglobals.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(KWinCompositingConfigFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KWinCompositingConfigFactory("kcmkwincompositing")) namespace KWin { ConfirmDialog::ConfirmDialog() : KTimerDialog(10000, KTimerDialog::CountDown, 0, i18n("Confirm Desktop Effects Change"), KTimerDialog::Ok | KTimerDialog::Cancel, KTimerDialog::Cancel) { setObjectName(QLatin1String("mainKTimerDialog")); setButtonGuiItem(KDialog::Ok, KGuiItem(i18n("&Accept Configuration"), "dialog-ok")); setButtonGuiItem(KDialog::Cancel, KGuiItem(i18n("&Return to Previous Configuration"), "dialog-cancel")); QLabel *label = new QLabel(i18n("Desktop effects settings have changed.\n" "Do you want to keep the new settings?\n" "They will be automatically reverted in 10 seconds."), this); label->setWordWrap(true); setMainWidget(label); setWindowIcon(KIcon("preferences-desktop-effect")); } KWinCompositingConfig::KWinCompositingConfig(QWidget *parent, const QVariantList &args) : KCModule(KWinCompositingConfigFactory::componentData(), parent) , mKWinConfig(KSharedConfig::openConfig("kwinrc")) , m_showConfirmDialog(false) , m_showDetailedErrors(new QAction(i18nc("Action to open a dialog showing detailed information why an effect could not be loaded", "Details"), this)) , m_dontShowAgain(new QAction(i18nc("Prevent warning from bein displayed again", "Don't show again!"), this)) { QDBusConnection::sessionBus().registerService("org.kde.kwinCompositingDialog"); QDBusConnection::sessionBus().registerObject("/CompositorSettings", this); new MainAdaptor(this); KGlobal::locale()->insertCatalog("kwin_effects"); KGlobal::locale()->insertCatalog("kwin_scripting"); ui.setupUi(this); layout()->setMargin(0); layout()->activate(); ui.tabWidget->setCurrentIndex(0); ui.statusTitleWidget->hide(); ui.rearmGlSupport->hide(); ui.messageBox->setMessageType(KMessageWidget::Warning); ui.messageBox->addAction(m_dontShowAgain); foreach (QWidget *w, m_dontShowAgain->associatedWidgets()) w->setVisible(false); ui.messageBox->addAction(m_showDetailedErrors); bool showMessage = false; QString message, details, dontAgainKey; if (args.count() > 1) { for (int i = 0; i < args.count() - 1; ++i) { if (args.at(i).toString() == "warn") { showMessage = true; message = QString::fromLocal8Bit(QByteArray::fromBase64(args.at(i+1).toByteArray())); } else if (args.at(i).toString() == "details") { showMessage = true; details = QString::fromLocal8Bit(QByteArray::fromBase64(args.at(i+1).toByteArray())); } else if (args.at(i).toString() == "dontagain") { showMessage = true; dontAgainKey = args.at(i+1).toString(); } } } if (showMessage) { ui.messageBox->setVisible(showMessage); // first show, animation is broken on init warn(message, details, dontAgainKey); } else ui.messageBox->setVisible(false); ui.ghns->setIcon(KIcon("get-hot-new-stuff")); // For future use (void) I18N_NOOP("Use GLSL shaders"); #define OPENGL31_INDEX 0 #define OPENGL20_INDEX 1 #define OPENGL12_INDEX 2 #define XRENDER_INDEX 3 #ifndef KWIN_HAVE_XRENDER_COMPOSITING ui.compositingType->removeItem(XRENDER_INDEX); #define XRENDER_INDEX -1 #endif ui.glSwapStrategy->addItem(i18n("None"), "n"); ui.glSwapStrategy->setItemData(0, i18n("The painting is not synchronized with the screen."), Qt::ToolTipRole); ui.glSwapStrategy->addItem(i18n("Automatic"), "a"); ui.glSwapStrategy->setItemData(1, i18n("Tries to re-use older buffers and if that is not possible,\npicks a strategy matching your hardware."), Qt::ToolTipRole); ui.glSwapStrategy->addItem(i18n("Only when Cheap"), "e"); ui.glSwapStrategy->setItemData(2, i18n("When major regions of the screen are updated,\nthe entire screen will be repainted.\nCan cause tearing with small updates."), Qt::ToolTipRole); ui.glSwapStrategy->addItem(i18n("Full scene repaints"), "p"); ui.glSwapStrategy->setItemData(3, i18n("The complete screen is repainted for every frame.\nCan be slow with large blurred areas."), Qt::ToolTipRole); ui.glSwapStrategy->addItem(i18n("Re-use screen content"), "c"); ui.glSwapStrategy->setItemData(4, i18n("WARNING:\nThis strategy is usually slow with Open Source drivers.\nUndamaged pixels will be copied from GL_FRONT to GL_BACK"), Qt::ToolTipRole); connect(ui.tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); connect(ui.rearmGlSupportButton, SIGNAL(clicked()), this, SLOT(rearmGlSupport())); connect(ui.useCompositing, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.useCompositing, SIGNAL(clicked(bool)), this, SLOT(suggestGraphicsSystem())); connect(ui.effectWinManagement, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.effectAnimations, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.effectSelector, SIGNAL(changed(bool)), this, SLOT(changed())); connect(ui.effectSelector, SIGNAL(configCommitted(QByteArray)), this, SLOT(reparseConfiguration(QByteArray))); connect(ui.desktopSwitchingCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.animationSpeedCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.compositingType, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.compositingType, SIGNAL(currentIndexChanged(int)), this, SLOT(toogleSmoothScaleUi(int))); connect(ui.compositingType, SIGNAL(activated(int)), this, SLOT(suggestGraphicsSystem())); connect(ui.graphicsSystem, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.windowThumbnails, SIGNAL(activated(int)), this, SLOT(changed())); connect(ui.unredirectFullscreen , SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.glScaleFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.xrScaleFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.glSwapStrategy, SIGNAL(currentIndexChanged(int)), this, SLOT(glSwapStrategyChanged(int))); connect(ui.glSwapStrategy, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.glColorCorrection, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(m_showDetailedErrors, SIGNAL(triggered(bool)), SLOT(showDetailedEffectLoadingInformation())); connect(m_dontShowAgain, SIGNAL(triggered(bool)), SLOT(blockFutureWarnings())); connect(ui.ghns, SIGNAL(clicked(bool)), SLOT(slotGHNS())); // Open the temporary config file // Temporary conf file is used to synchronize effect checkboxes with effect // selector by loading/saving effects from/to temp config when active tab // changes. mTmpConfigFile.open(); mTmpConfig = KSharedConfig::openConfig(mTmpConfigFile.fileName()); // toggle effects shortcut button stuff - /HAS/ to happen before load! m_actionCollection = new KActionCollection( this, KComponentData("kwin") ); m_actionCollection->setConfigGroup("Suspend Compositing"); m_actionCollection->setConfigGlobal(true); KAction* a = static_cast(m_actionCollection->addAction( "Suspend Compositing" )); a->setProperty("isConfigurationAction", true); a->setGlobalShortcut( KShortcut( Qt::ALT + Qt::SHIFT + Qt::Key_F12 )); connect(ui.toggleEffectsShortcut, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(toggleEffectShortcutChanged(QKeySequence))); // Initialize the user interface with the config loaded from kwinrc. load(); KAboutData *about = new KAboutData(I18N_NOOP("kcmkwincompositing"), 0, ki18n("KWin Desktop Effects Configuration Module"), 0, KLocalizedString(), KAboutData::License_GPL, ki18n("(c) 2007 Rivo Laks")); about->addAuthor(ki18n("Rivo Laks"), KLocalizedString(), "rivolaks@hot.ee"); setAboutData(about); // search the effect names KServiceTypeTrader* trader = KServiceTypeTrader::self(); KService::List services; QString slide, cube, fadedesktop; // desktop switcher services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_slide'"); if (!services.isEmpty()) slide = services.first()->name(); services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_cubeslide'"); if (!services.isEmpty()) cube = services.first()->name(); services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_fadedesktop'"); if (!services.isEmpty()) fadedesktop = services.first()->name(); ui.desktopSwitchingCombo->addItem(i18n("No effect")); ui.desktopSwitchingCombo->addItem(slide); ui.desktopSwitchingCombo->addItem(cube); ui.desktopSwitchingCombo->addItem(fadedesktop); } KWinCompositingConfig::~KWinCompositingConfig() { } void KWinCompositingConfig::reparseConfiguration(const QByteArray& conf) { KSettings::Dispatcher::reparseConfiguration(conf); } void KWinCompositingConfig::showConfirmDialog(bool reinitCompositing) { bool revert = false; // Feel free to extend this to support several kwin instances (multihead) if you // think it makes sense. OrgKdeKWinInterface kwin("org.kde.KWin", "/KWin", QDBusConnection::sessionBus()); if (reinitCompositing ? !kwin.compositingActive().value() : !kwin.waitForCompositingSetup().value()) { KMessageBox::sorry(this, i18n( "Failed to activate desktop effects using the given " "configuration options. Settings will be reverted to their previous values.\n\n" "Check your X configuration. You may also consider changing advanced options, " "especially changing the compositing type.")); revert = true; } else { ConfirmDialog confirm; if (!confirm.exec()) revert = true; else { // compositing is enabled now checkLoadedEffects(); } } if (revert) { // Revert settings KConfigGroup config(mKWinConfig, "Compositing"); config.deleteGroup(); QMap::const_iterator it = mPreviousConfig.constBegin(); for (; it != mPreviousConfig.constEnd(); ++it) { if (it.value().isEmpty()) continue; config.writeEntry(it.key(), it.value()); } // Sync with KWin and reload configChanged(reinitCompositing); load(); } } void KWinCompositingConfig::initEffectSelector() { // Find all .desktop files of the effects KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect"); QList effectinfos = KPluginInfo::fromServices(offers); // Add them to the plugin selector ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Appearance"), "Appearance", mTmpConfig); ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Accessibility"), "Accessibility", mTmpConfig); ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Focus"), "Focus", mTmpConfig); ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Window Management"), "Window Management", mTmpConfig); ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Candy"), "Candy", mTmpConfig); ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Demos"), "Demos", mTmpConfig); ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Tests"), "Tests", mTmpConfig); ui.effectSelector->addPlugins(effectinfos, KPluginSelector::ReadConfigFile, i18n("Tools"), "Tools", mTmpConfig); } void KWinCompositingConfig::currentTabChanged(int tab) { // block signals to don't emit the changed()-signal by just switching the current tab blockSignals(true); // write possible changes done to synchronize effect checkboxes and selector // TODO: This segment is prone to fail when the UI is changed; // you'll most likely not think of the hard coded numbers here when just changing the order of the tabs. if (tab == 0) { // General tab was activated saveEffectsTab(); loadGeneralTab(); } else if (tab == 1) { // Effects tab was activated saveGeneralTab(); loadEffectsTab(); } blockSignals(false); } void KWinCompositingConfig::loadGeneralTab() { KConfigGroup config(mKWinConfig, "Compositing"); bool enabled = config.readEntry("Enabled", true); ui.useCompositing->setChecked(enabled); // this works by global shortcut magics - it will pick the current sc // but the constructor line that adds the default alt+shift+f12 gsc is IMPORTANT! if (KAction *a = qobject_cast(m_actionCollection->action("Suspend Compositing"))) ui.toggleEffectsShortcut->setKeySequence(a->globalShortcut().primary()); ui.animationSpeedCombo->setCurrentIndex(config.readEntry("AnimationSpeed", 3)); // Load effect settings KConfigGroup effectconfig(mTmpConfig, "Plugins"); #define LOAD_EFFECT_CONFIG(effectname) effectconfig.readEntry("kwin4_effect_" effectname "Enabled", true) int winManagementEnabled = LOAD_EFFECT_CONFIG("presentwindows") + LOAD_EFFECT_CONFIG("desktopgrid") + LOAD_EFFECT_CONFIG("dialogparent"); if (winManagementEnabled > 0 && winManagementEnabled < 3) { ui.effectWinManagement->setTristate(true); ui.effectWinManagement->setCheckState(Qt::PartiallyChecked); } else ui.effectWinManagement->setChecked(winManagementEnabled); ui.effectAnimations->setChecked(LOAD_EFFECT_CONFIG("minimizeanimation")); #undef LOAD_EFFECT_CONFIG // desktop switching // Set current option to "none" if no plugin is activated. ui.desktopSwitchingCombo->setCurrentIndex(0); if (effectEnabled("slide", effectconfig)) ui.desktopSwitchingCombo->setCurrentIndex(1); if (effectEnabled("cubeslide", effectconfig)) ui.desktopSwitchingCombo->setCurrentIndex(2); if (effectEnabled("fadedesktop", effectconfig)) ui.desktopSwitchingCombo->setCurrentIndex(3); } void KWinCompositingConfig::glSwapStrategyChanged(int idx) { ui.glSwapStrategy->setToolTip(ui.glSwapStrategy->itemData(idx, Qt::ToolTipRole).toString()); } void KWinCompositingConfig::rearmGlSupport() { // rearm config KConfigGroup gl_workaround_config = KConfigGroup(mKWinConfig, "Compositing"); gl_workaround_config.writeEntry("OpenGLIsUnsafe", false); gl_workaround_config.sync(); // save last changes save(); // Initialize the user interface with the config loaded from kwinrc. load(); } void KWinCompositingConfig::suggestGraphicsSystem() { if (!ui.useCompositing->isChecked() || ui.compositingType->currentIndex() == XRENDER_INDEX) ui.graphicsSystem->setCurrentIndex(0); } void KWinCompositingConfig::toogleSmoothScaleUi(int compositingType) { ui.glScaleFilter->setVisible(compositingType != XRENDER_INDEX); ui.xrScaleFilter->setVisible(compositingType == XRENDER_INDEX); ui.scaleMethodLabel->setBuddy(compositingType == XRENDER_INDEX ? ui.xrScaleFilter : ui.glScaleFilter); ui.glGroup->setEnabled(compositingType != XRENDER_INDEX); } void KWinCompositingConfig::toggleEffectShortcutChanged(const QKeySequence &seq) { if (KAction *a = qobject_cast(m_actionCollection->action("Suspend Compositing"))) a->setGlobalShortcut(KShortcut(seq), KAction::ActiveShortcut, KAction::NoAutoloading); m_actionCollection->writeSettings(); } bool KWinCompositingConfig::effectEnabled(const QString& effect, const KConfigGroup& cfg) const { KService::List services = KServiceTypeTrader::self()->query( "KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_" + effect + '\''); if (services.isEmpty()) return false; QVariant v = services.first()->property("X-KDE-PluginInfo-EnabledByDefault"); return cfg.readEntry("kwin4_effect_" + effect + "Enabled", v.toBool()); } void KWinCompositingConfig::loadEffectsTab() { ui.effectSelector->load(); } void KWinCompositingConfig::loadAdvancedTab() { KConfigGroup config(mKWinConfig, "Compositing"); QString backend = config.readEntry("Backend", "OpenGL"); if (backend == "OpenGL") { int index = OPENGL20_INDEX; if (config.readEntry("GLLegacy", false)) index = OPENGL12_INDEX; else if (config.readEntry("GLCore", false)) index = OPENGL31_INDEX; ui.compositingType->setCurrentIndex(index); } else if (backend == "XRender") { ui.compositingType->setCurrentIndex(XRENDER_INDEX); } originalGraphicsSystem = config.readEntry("GraphicsSystem", QString()); if (originalGraphicsSystem.isEmpty()) { // detect system default QPixmap pix(1,1); QPainter p(&pix); originalGraphicsSystem = (p.paintEngine()->type() == QPaintEngine::X11) ? "native" : "raster"; p.end(); } ui.graphicsSystem->setCurrentIndex((originalGraphicsSystem == "native") ? 0 : 1); // 4 - off, 5 - shown, 6 - always, other are old values int hps = config.readEntry("HiddenPreviews", 5); if (hps == 6) // always ui.windowThumbnails->setCurrentIndex(0); else if (hps == 4) // never ui.windowThumbnails->setCurrentIndex(2); else // shown, or default ui.windowThumbnails->setCurrentIndex(1); ui.unredirectFullscreen->setChecked(config.readEntry("UnredirectFullscreen", false)); ui.xrScaleFilter->setCurrentIndex((int)config.readEntry("XRenderSmoothScale", false)); ui.glScaleFilter->setCurrentIndex(config.readEntry("GLTextureFilter", 2)); int swapStrategy = ui.glSwapStrategy->findData(config.readEntry("GLPreferBufferSwap", "a")); if (swapStrategy < 0) swapStrategy = ui.glSwapStrategy->findData("n"); ui.glSwapStrategy->setCurrentIndex(swapStrategy); ui.glColorCorrection->setChecked(config.readEntry("GLColorCorrection", false)); toogleSmoothScaleUi(ui.compositingType->currentIndex()); } void KWinCompositingConfig::updateStatusUI(bool compositingIsPossible) { if (compositingIsPossible) { ui.compositingOptionsContainer->show(); ui.statusTitleWidget->hide(); ui.rearmGlSupport->hide(); } else { OrgKdeKWinInterface kwin("org.kde.KWin", "/KWin", QDBusConnection::sessionBus()); ui.compositingOptionsContainer->hide(); QString text = i18n("Desktop effects are not available on this system due to the following technical issues:"); text += "
"; text += kwin.isValid() ? kwin.compositingNotPossibleReason() : i18nc("Reason shown when trying to activate desktop effects and KWin (most likely) crashes", "Window Manager seems not to be running"); ui.statusTitleWidget->setText(text); ui.statusTitleWidget->setPixmap(KTitleWidget::InfoMessage, KTitleWidget::ImageLeft); ui.statusTitleWidget->show(); ui.rearmGlSupport->setVisible(kwin.isValid() ? kwin.openGLIsBroken() : true); } } void KWinCompositingConfig::load() { initEffectSelector(); mKWinConfig->reparseConfiguration(); QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.KWin", "/KWin", "org.kde.KWin", "compositingPossible"); QDBusConnection::sessionBus().callWithCallback(msg, this, SLOT(updateStatusUI(bool))); // Copy Plugins group to temp config file QMap entries = mKWinConfig->entryMap("Plugins"); QMap::const_iterator it = entries.constBegin(); KConfigGroup tmpconfig(mTmpConfig, "Plugins"); tmpconfig.deleteGroup(); for (; it != entries.constEnd(); ++it) tmpconfig.writeEntry(it.key(), it.value()); loadGeneralTab(); loadEffectsTab(); loadAdvancedTab(); emit changed(false); } void KWinCompositingConfig::saveGeneralTab() { KConfigGroup config(mKWinConfig, "Compositing"); // Check if any critical settings that need confirmation have changed config.writeEntry("Enabled", ui.useCompositing->isChecked()); config.writeEntry("AnimationSpeed", ui.animationSpeedCombo->currentIndex()); // Save effects KConfigGroup effectconfig(mTmpConfig, "Plugins"); #define WRITE_EFFECT_CONFIG(effectname, widget) effectconfig.writeEntry("kwin4_effect_" effectname "Enabled", widget->isChecked()) if (ui.effectWinManagement->checkState() != Qt::PartiallyChecked) { WRITE_EFFECT_CONFIG("presentwindows", ui.effectWinManagement); WRITE_EFFECT_CONFIG("desktopgrid", ui.effectWinManagement); WRITE_EFFECT_CONFIG("dialogparent", ui.effectWinManagement); } // TODO: maybe also do some effect-specific configuration here, e.g. // enable/disable desktopgrid's animation according to this setting WRITE_EFFECT_CONFIG("minimizeanimation", ui.effectAnimations); #undef WRITE_EFFECT_CONFIG int desktopSwitcher = ui.desktopSwitchingCombo->currentIndex(); switch(desktopSwitcher) { case 0: // no effect effectconfig.writeEntry("kwin4_effect_slideEnabled", false); effectconfig.writeEntry("kwin4_effect_cubeslideEnabled", false); effectconfig.writeEntry("kwin4_effect_fadedesktopEnabled", false); break; case 1: // slide effectconfig.writeEntry("kwin4_effect_slideEnabled", true); effectconfig.writeEntry("kwin4_effect_cubeslideEnabled", false); effectconfig.writeEntry("kwin4_effect_fadedesktopEnabled", false); break; case 2: // cube effectconfig.writeEntry("kwin4_effect_slideEnabled", false); effectconfig.writeEntry("kwin4_effect_cubeslideEnabled", true); effectconfig.writeEntry("kwin4_effect_fadedesktopEnabled", false); break; case 3: // fadedesktop effectconfig.writeEntry("kwin4_effect_slideEnabled", false); effectconfig.writeEntry("kwin4_effect_cubeslideEnabled", false); effectconfig.writeEntry("kwin4_effect_fadedesktopEnabled", true); break; } } void KWinCompositingConfig::saveEffectsTab() { ui.effectSelector->save(); } bool KWinCompositingConfig::saveAdvancedTab() { bool advancedChanged = false; static const int hps[] = { 6 /*always*/, 5 /*shown*/, 4 /*never*/ }; KConfigGroup config(mKWinConfig, "Compositing"); QString graphicsSystem = (ui.graphicsSystem->currentIndex() == 0) ? "native" : "raster"; QString backend; bool glLegacy; bool glCore; switch (ui.compositingType->currentIndex()) { case OPENGL12_INDEX: backend = "OpenGL"; glLegacy = true; glCore = false; break; case OPENGL20_INDEX: backend = "OpenGL"; glLegacy = false; glCore = false; break; case OPENGL31_INDEX: backend = "OpenGL"; glLegacy = false; glCore = true; break; case XRENDER_INDEX: backend = "XRender"; glLegacy = false; glCore = false; break; } if (config.readEntry("Backend", "OpenGL") != backend || config.readEntry("GLLegacy", false) != glLegacy || config.readEntry("GLCore", false) != glCore || ((config.readEntry("GLPreferBufferSwap", "a") == "n") xor (ui.glSwapStrategy->itemData(ui.glSwapStrategy->currentIndex()) == "n"))) { m_showConfirmDialog = true; advancedChanged = true; } else if (config.readEntry("HiddenPreviews", 5) != hps[ ui.windowThumbnails->currentIndex()] || (int)config.readEntry("XRenderSmoothScale", false) != ui.xrScaleFilter->currentIndex() || config.readEntry("GLTextureFilter", 2) != ui.glScaleFilter->currentIndex()) { advancedChanged = true; } else if (originalGraphicsSystem != graphicsSystem) { advancedChanged = true; } config.writeEntry("Backend", backend); if (backend == "OpenGL") { config.writeEntry("GLLegacy", glLegacy); config.writeEntry("GLCore", glCore); } config.writeEntry("GraphicsSystem", graphicsSystem); config.writeEntry("HiddenPreviews", hps[ ui.windowThumbnails->currentIndex()]); config.writeEntry("UnredirectFullscreen", ui.unredirectFullscreen->isChecked()); config.writeEntry("XRenderSmoothScale", ui.xrScaleFilter->currentIndex() == 1); config.writeEntry("GLTextureFilter", ui.glScaleFilter->currentIndex()); config.writeEntry("GLPreferBufferSwap", ui.glSwapStrategy->itemData(ui.glSwapStrategy->currentIndex()).toString()); config.writeEntry("GLColorCorrection", ui.glColorCorrection->isChecked()); return advancedChanged; } void KWinCompositingConfig::save() { OrgKdeKWinInterface kwin("org.kde.KWin", "/KWin", QDBusConnection::sessionBus()); if (ui.compositingType->currentIndex() != XRENDER_INDEX && kwin.openGLIsBroken() && !ui.rearmGlSupport->isVisible()) { KConfigGroup config(mKWinConfig, "Compositing"); QString oldBackend = config.readEntry("Backend", "OpenGL"); config.writeEntry("Backend", "OpenGL"); config.sync(); updateStatusUI(false); config.writeEntry("Backend", oldBackend); config.sync(); ui.tabWidget->setCurrentIndex(0); return; } // Save current config. We'll use this for restoring in case something goes wrong. KConfigGroup config(mKWinConfig, "Compositing"); mPreviousConfig = config.entryMap(); // bah; tab content being dependent on the other is really bad; and // deprecated in the HIG for a reason. It is confusing! // Make sure we only call save on each tab once; as they are stateful due to the revert concept if (ui.tabWidget->currentIndex() == 0) { // "General" tab was active saveGeneralTab(); loadEffectsTab(); saveEffectsTab(); } else { saveEffectsTab(); loadGeneralTab(); saveGeneralTab(); } bool advancedChanged = saveAdvancedTab(); // Copy Plugins group from temp config to real config QMap entries = mTmpConfig->entryMap("Plugins"); QMap::const_iterator it = entries.constBegin(); KConfigGroup realconfig(mKWinConfig, "Plugins"); realconfig.deleteGroup(); for (; it != entries.constEnd(); ++it) realconfig.writeEntry(it.key(), it.value()); emit changed(false); configChanged(advancedChanged); // This assumes that this KCM is running with the same environment variables as KWin // TODO: Detect KWIN_COMPOSE=N as well if (!qgetenv("KDE_FAILSAFE").isNull() && ui.useCompositing->isChecked()) { KMessageBox::sorry(this, i18n( "Your settings have been saved but as KDE is currently running in failsafe " "mode desktop effects cannot be enabled at this time.\n\n" "Please exit failsafe mode to enable desktop effects.")); m_showConfirmDialog = false; // Dangerous but there is no way to test if failsafe mode } if (m_showConfirmDialog) { m_showConfirmDialog = false; if (advancedChanged) QTimer::singleShot(1000, this, SLOT(confirmReInit())); else showConfirmDialog(false); } } void KWinCompositingConfig::checkLoadedEffects() { // check for effects not supported by Backend or hardware // such effects are enabled but not returned by DBus method loadedEffects OrgKdeKWinInterface kwin("org.kde.KWin", "/KWin", QDBusConnection::sessionBus()); KConfigGroup effectConfig = KConfigGroup(mKWinConfig, "Compositing"); bool enabledAfter = effectConfig.readEntry("Enabled", true); QDBusPendingReply< QStringList > reply = kwin.loadedEffects(); if (!reply.isError() && enabledAfter && !getenv("KDE_FAILSAFE")) { effectConfig = KConfigGroup(mKWinConfig, "Plugins"); QStringList loadedEffects = reply.value(); QStringList effects = effectConfig.keyList(); QStringList disabledEffects = QStringList(); foreach (QString effect, effects) { // krazy:exclude=foreach QString temp = effect.mid(13, effect.length() - 13 - 7); effect.truncate(effect.length() - 7); if (effectEnabled(temp, effectConfig) && !loadedEffects.contains(effect)) { disabledEffects << effect; } } if (!disabledEffects.isEmpty()) { m_showDetailedErrors->setData(disabledEffects); foreach (QWidget *w, m_showDetailedErrors->associatedWidgets()) w->setVisible(true); ui.messageBox->setText(i18ncp("Error Message shown when a desktop effect could not be loaded", "One desktop effect could not be loaded.", "%1 desktop effects could not be loaded.", disabledEffects.count())); ui.messageBox->animatedShow(); } else { foreach (QWidget *w, m_showDetailedErrors->associatedWidgets()) w->setVisible(false); } } } void KWinCompositingConfig::showDetailedEffectLoadingInformation() { QStringList disabledEffects = m_showDetailedErrors->data().toStringList(); OrgKdeKWinInterface kwin("org.kde.KWin", "/KWin", QDBusConnection::sessionBus()); QDBusPendingReply< QString > pendingCompositingType = kwin.compositingType(); QString compositingType = pendingCompositingType.isError() ? "none" : pendingCompositingType.value(); KServiceTypeTrader* trader = KServiceTypeTrader::self(); KService::List services; const KLocalizedString unknownReason = ki18nc("Effect with given name could not be activated due to unknown reason", "%1 effect failed to load due to unknown reason."); const KLocalizedString requiresShaders = ki18nc("Effect with given name could not be activated as it requires hardware shaders", "%1 effect requires hardware support."); const KLocalizedString requiresOpenGL = ki18nc("Effect with given name could not be activated as it requires OpenGL", "%1 effect requires OpenGL."); const KLocalizedString requiresOpenGL2 = ki18nc("Effect with given name could not be activated as it requires OpenGL 2", "%1 effect requires OpenGL 2."); KDialog *dialog = new KDialog(this); dialog->setWindowTitle(i18nc("Window title", "List of effects which could not be loaded")); dialog->setButtons(KDialog::Ok); QWidget *mainWidget = new QWidget(dialog); dialog->setMainWidget(mainWidget); QVBoxLayout *vboxLayout = new QVBoxLayout(mainWidget); mainWidget->setLayout(vboxLayout); KTitleWidget *titleWidget = new KTitleWidget(mainWidget); titleWidget->setText(i18n("For technical reasons it is not possible to determine all possible error causes."), KTitleWidget::InfoMessage); QLabel *label = new QLabel(mainWidget); label->setOpenExternalLinks(true); vboxLayout->addWidget(titleWidget); vboxLayout->addWidget(label); if (!m_externErrorDetails.isNull()) { label->setText(m_externErrorDetails); } else if (compositingType != "none") { QString text; if (disabledEffects.count() > 1) { text = "
    "; } foreach (const QString & effect, disabledEffects) { QString message; services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == '" + effect + '\''); if (!services.isEmpty()) { KService::Ptr service = services.first(); if (compositingType == "xrender") { // XRender compositing QVariant openGL = service->property("X-KWin-Requires-OpenGL"); QVariant openGL2 = service->property("X-KWin-Requires-OpenGL2"); if ((openGL.isValid() && openGL.toBool()) || (openGL2.isValid() && openGL2.toBool())) { // effect requires OpenGL message = requiresOpenGL.subs(service->name()).toString(); } else { // effect does not require OpenGL, unknown reason message = unknownReason.subs(service->name()).toString(); } } else if (compositingType == "gl1") { // OpenGL 1 compositing QVariant openGL2 = service->property("X-KWin-Requires-OpenGL2"); QVariant shaders = service->property("X-KWin-Requires-Shaders"); if (openGL2.isValid() && openGL2.toBool()) { // effect requires OpenGL 2 message = requiresOpenGL2.subs(service->name()).toString(); } else if (shaders.isValid() && shaders.toBool()) { // effect requires hardware shaders message = requiresShaders.subs(service->name()).toString(); } else { // unknown reason message = unknownReason.subs(service->name()).toString(); } } else { // OpenGL 2 compositing - unknown reason message = unknownReason.subs(service->name()).toString(); } } else { message = unknownReason.subs(effect).toString(); } if (disabledEffects.count() > 1) { text.append("
  • "); text.append(message); text.append("
  • "); } else { text = message; } } if (disabledEffects.count() > 1) { text.append("
"); } label->setText(text); } else { // compositing is not active - no effect can be active label->setText(i18nc("Error Message shown when compositing is not active after tried activation", "Desktop effect system is not running.")); } dialog->show(); } void KWinCompositingConfig::warn(QString message, QString details, QString dontAgainKey) { ui.messageBox->setText(message); m_dontShowAgain->setData(dontAgainKey); foreach (QWidget *w, m_dontShowAgain->associatedWidgets()) w->setVisible(!dontAgainKey.isEmpty()); m_externErrorDetails = details.isNull() ? "" : details; foreach (QWidget *w, m_showDetailedErrors->associatedWidgets()) w->setVisible(!m_externErrorDetails.isEmpty()); ui.messageBox->animatedShow(); } void KWinCompositingConfig::blockFutureWarnings() { QString key; if (QAction *act = qobject_cast(sender())) key = act->data().toString(); if (key.isEmpty()) return; QStringList l = key.split(':', QString::SkipEmptyParts); KConfig cfg(l.count() > 1 ? l.at(0) : "kwin_dialogsrc"); KConfigGroup(&cfg, "Notification Messages").writeEntry(l.last(), false); cfg.sync(); ui.messageBox->animatedHide(); } void KWinCompositingConfig::configChanged(bool reinitCompositing) { // Send signal to kwin mKWinConfig->sync(); // Send signal to all kwin instances QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", reinitCompositing ? "reinitCompositing" : "reloadConfig"); QDBusConnection::sessionBus().send(message); // maybe it's ok now? if (reinitCompositing && !ui.compositingOptionsContainer->isVisible()) load(); // HACK: We can't just do this here, due to the asynchronous nature of signals. // We also can't change reinitCompositing into a message (which would allow // callWithCallbac() to do this neater) due to multiple kwin instances. if (!m_showConfirmDialog) QTimer::singleShot(1000, this, SLOT(checkLoadedEffects())); } void KWinCompositingConfig::defaults() { ui.tabWidget->setCurrentIndex(0); ui.useCompositing->setChecked(true); ui.effectWinManagement->setChecked(true); ui.effectAnimations->setChecked(true); ui.desktopSwitchingCombo->setCurrentIndex(1); ui.animationSpeedCombo->setCurrentIndex(3); ui.effectSelector->defaults(); ui.compositingType->setCurrentIndex(OPENGL20_INDEX); ui.windowThumbnails->setCurrentIndex(1); ui.unredirectFullscreen->setChecked(false); ui.xrScaleFilter->setCurrentIndex(0); ui.glScaleFilter->setCurrentIndex(2); ui.glSwapStrategy->setCurrentIndex(ui.glSwapStrategy->findData("a")); ui.glColorCorrection->setChecked(false); } QString KWinCompositingConfig::quickHelp() const { return i18n("

Desktop Effects

"); } void KWinCompositingConfig::slotGHNS() { QPointer downloadDialog = new KNS3::DownloadDialog("kwineffect.knsrc", this); if (downloadDialog->exec() == KDialog::Accepted) { if (!downloadDialog->changedEntries().isEmpty()) { initEffectSelector(); } } delete downloadDialog; } } // namespace #include "dbus.moc" #include "main.moc"