diff --git a/kcmkwin/kwincompositing/main.cpp b/kcmkwin/kwincompositing/main.cpp index f29cf0f67..d87480e1e 100644 --- a/kcmkwin/kwincompositing/main.cpp +++ b/kcmkwin/kwincompositing/main.cpp @@ -1,773 +1,786 @@ /******************************************************************** 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 "kwin_interface.h" #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 &) : KCModule( KWinCompositingConfigFactory::componentData(), parent ) , mKWinConfig(KSharedConfig::openConfig( "kwinrc" )) , m_showConfirmDialog( false ) , kwinInterface( NULL ) { KGlobal::locale()->insertCatalog( "kwin_effects" ); ui.setupUi(this); layout()->setMargin(0); ui.verticalSpacer->changeSize(20, KDialog::groupSpacingHint()); ui.verticalSpacer_2->changeSize(20, KDialog::groupSpacingHint()); ui.tabWidget->setCurrentIndex(0); ui.statusTitleWidget->hide(); #define OPENGL_INDEX 0 #define XRENDER_INDEX 1 #ifndef KWIN_HAVE_OPENGL_COMPOSITING ui.compositingType->removeItem( OPENGL_INDEX ); ui.glGroup->setEnabled( false ); #define OPENGL_INDEX -1 #define XRENDER_INDEX 0 #endif #ifndef KWIN_HAVE_XRENDER_COMPOSITING ui.compositingType->removeItem( XRENDER_INDEX ); ui.xrenderGroup->setEnabled( false ); #define XRENDER_INDEX -1 #endif kwinInterface = new OrgKdeKWinInterface( "org.kde.kwin", "/KWin", QDBusConnection::sessionBus() ); connect(ui.useCompositing, SIGNAL(toggled(bool)), this, SLOT(compositingEnabled(bool))); connect(ui.tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); connect(ui.useCompositing, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.effectWinManagement, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.effectShadows, 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(const QByteArray&)), this, SLOT(reparseConfiguration(const QByteArray&))); connect(ui.windowSwitchingCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); 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.windowThumbnails, SIGNAL(activated(int)), this, SLOT(changed())); connect(ui.disableChecks, SIGNAL(toggled(bool)), 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.glMode, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); - connect(ui.glTextureFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); connect(ui.glDirect, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.glVSync, SIGNAL(toggled(bool)), this, SLOT(changed())); - connect(ui.xrenderSmoothScale, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(ui.compositingStateButton, SIGNAL(clicked(bool)), kwinInterface, SLOT(toggleCompositing())); connect(kwinInterface, SIGNAL(compositingToggled(bool)), this, SLOT(setupCompositingState(bool))); // 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()); // NOTICE: this is intended to workaround broken GL implementations that successfully segfault on glXQuery :-( KConfigGroup unsafeConfig(mKWinConfig, "Compositing"); const bool glUnsafe = unsafeConfig.readEntry( "OpenGLIsUnsafe", false ); if( !glUnsafe && CompositingPrefs::compositingPossible() ) { unsafeConfig.writeEntry( "OpenGLIsUnsafe", true ); unsafeConfig.sync(); // Driver-specific config detection mDefaultPrefs.detect(); initEffectSelector(); // Initialize the user interface with the config loaded from kwinrc. load(); unsafeConfig.writeEntry( "OpenGLIsUnsafe", false ); unsafeConfig.sync(); } else { // TODO: Add a "force recheck" button that removes the "OpenGLInUnsafe" flag ui.useCompositing->setEnabled(false); ui.useCompositing->setChecked(false); compositingEnabled(false); QString text = i18n("Desktop effects are not available on this system due to the following technical issues:"); text += "
"; text += CompositingPrefs::compositingNotPossibleReason(); ui.statusTitleWidget->setText(text); ui.statusTitleWidget->setPixmap(KTitleWidget::InfoMessage, KTitleWidget::ImageLeft); ui.statusTitleWidget->show(); setupCompositingState( false, false ); } 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 boxswitch, presentwindows, coverswitch, flipswitch, slide, cube, fadedesktop; // window switcher services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_boxswitch'"); if( !services.isEmpty() ) boxswitch = services.first()->name(); services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_presentwindows'"); if( !services.isEmpty() ) presentwindows = services.first()->name(); services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_coverswitch'"); if( !services.isEmpty() ) coverswitch = services.first()->name(); services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_flipswitch'"); if( !services.isEmpty() ) flipswitch = services.first()->name(); // 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(); // init the combo boxes ui.windowSwitchingCombo->addItem(i18n("No Effect")); ui.windowSwitchingCombo->addItem(boxswitch); ui.windowSwitchingCombo->addItem(presentwindows); ui.windowSwitchingCombo->addItem(coverswitch); ui.windowSwitchingCombo->addItem(flipswitch); 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::compositingEnabled( bool enabled ) { // Enable the other configuration tabs only when compositing is enabled. ui.compositingOptionsContainer->setEnabled(enabled); ui.tabWidget->setTabEnabled(1, enabled); ui.tabWidget->setTabEnabled(2, enabled); } 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 setupCompositingState( kwinInterface->compositingActive() ); 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", mDefaultPrefs.recommendCompositing()); ui.useCompositing->setChecked( enabled ); 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.effectShadows->setChecked(LOAD_EFFECT_CONFIG("shadow")); ui.effectAnimations->setChecked(LOAD_EFFECT_CONFIG("minimizeanimation")); #undef LOAD_EFFECT_CONFIG // window switching // Set current option to "none" if no plugin is activated. ui.windowSwitchingCombo->setCurrentIndex( 0 ); KConfigGroup boxswitchconfig(mKWinConfig, "Effect-BoxSwitch"); if( effectEnabled( "boxswitch", effectconfig ) && boxswitchconfig.readEntry("TabBox", true)) ui.windowSwitchingCombo->setCurrentIndex( 1 ); KConfigGroup coverswitchconfig(mKWinConfig, "Effect-CoverSwitch"); if( effectEnabled( "coverswitch", effectconfig ) && coverswitchconfig.readEntry("TabBox", false)) ui.windowSwitchingCombo->setCurrentIndex( 3 ); KConfigGroup flipswitchconfig(mKWinConfig, "Effect-FlipSwitch"); if( effectEnabled( "flipswitch", effectconfig ) && flipswitchconfig.readEntry("TabBox", false)) ui.windowSwitchingCombo->setCurrentIndex( 4 ); KConfigGroup presentwindowsconfig(mKWinConfig, "Effect-PresentWindows"); if( effectEnabled( "presentwindows", effectconfig ) && presentwindowsconfig.readEntry("TabBox", false) ) ui.windowSwitchingCombo->setCurrentIndex( 2 ); // 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 ); if( enabled ) setupCompositingState( kwinInterface->compositingActive() ); else setupCompositingState( false, false ); } void KWinCompositingConfig::setupCompositingState( bool active, bool enabled ) { if( !qgetenv( "KDE_FAILSAFE" ).isNull() ) enabled = false; // compositing state QString stateIcon; QString stateText; QString stateButtonText; if( enabled ) { // check if compositing is active or suspended if( active ) { stateIcon = QString( "dialog-ok-apply" ); stateText = i18n( "Desktop effects are active" ); stateButtonText = i18n( "Suspend Desktop Effects" ); } else { stateIcon = QString( "dialog-cancel" ); stateText = i18n( "Desktop effects are temporarily disabled" ); stateButtonText = i18n( "Resume Desktop Effects" ); } } else { // compositing is disabled stateIcon = QString( "dialog-cancel" ); stateText = i18n( "Desktop effects are disabled" ); stateButtonText = i18n( "Resume Desktop Effects" ); } const int iconSize = (QApplication::fontMetrics().height() > 24) ? 32 : 22; ui.compositingStateIcon->setPixmap( KIcon( stateIcon ).pixmap( iconSize, iconSize ) ); ui.compositingStateLabel->setText( stateText ); ui.compositingStateButton->setText( stateButtonText ); ui.compositingStateIcon->setEnabled( enabled ); ui.compositingStateLabel->setEnabled( enabled ); ui.compositingStateButton->setEnabled( enabled ); } +void KWinCompositingConfig::toogleSmoothScaleUi( int compositingType ) +{ + ui.glScaleFilter->setVisible( compositingType == OPENGL_INDEX ); + ui.xrScaleFilter->setVisible( compositingType == XRENDER_INDEX ); + ui.scaleMethodLabel->setBuddy( compositingType == XRENDER_INDEX ? ui.xrScaleFilter : ui.glScaleFilter ); +} + 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"); ui.compositingType->setCurrentIndex((backend == "XRender") ? XRENDER_INDEX : 0); // 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.disableChecks->setChecked( config.readEntry( "DisableChecks", false )); ui.unredirectFullscreen->setChecked( config.readEntry( "UnredirectFullscreen", true )); + ui.xrScaleFilter->setCurrentIndex((int)config.readEntry("XRenderSmoothScale", false)); + ui.glScaleFilter->setCurrentIndex(config.readEntry("GLTextureFilter", 2)); + QString glMode = config.readEntry("GLMode", "TFP"); ui.glMode->setCurrentIndex((glMode == "TFP") ? 0 : ((glMode == "SHM") ? 1 : 2)); - ui.glTextureFilter->setCurrentIndex(config.readEntry("GLTextureFilter", 1)); ui.glDirect->setChecked(config.readEntry("GLDirect", mDefaultPrefs.enableDirectRendering())); ui.glVSync->setChecked(config.readEntry("GLVSync", mDefaultPrefs.enableVSync())); - ui.xrenderSmoothScale->setChecked(config.readEntry("XRenderSmoothScale", false)); + toogleSmoothScaleUi( ui.compositingType->currentIndex() ); } void KWinCompositingConfig::load() { mKWinConfig->reparseConfiguration(); // 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 if(ui.useCompositing->isChecked() && ui.useCompositing->isChecked() != config.readEntry("Enabled", mDefaultPrefs.recommendCompositing())) m_showConfirmDialog = true; config.writeEntry("Enabled", ui.useCompositing->isChecked()); config.writeEntry("AnimationSpeed", ui.animationSpeedCombo->currentIndex()); // disable the compositing state if compositing was turned off if( !ui.useCompositing->isChecked() ) setupCompositingState( false, false ); // 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); } WRITE_EFFECT_CONFIG("shadow", ui.effectShadows); // 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 windowSwitcher = ui.windowSwitchingCombo->currentIndex(); bool boxSwitch = false; bool presentWindowSwitching = false; bool coverSwitch = false; bool flipSwitch = false; switch( windowSwitcher ) { case 1: boxSwitch = true; break; case 2: presentWindowSwitching = true; break; case 3: coverSwitch = true; break; case 4: flipSwitch = true; break; default: break; // nothing } // activate effects if not active if( boxSwitch ) effectconfig.writeEntry("kwin4_effect_boxswitchEnabled", true); if( presentWindowSwitching ) effectconfig.writeEntry("kwin4_effect_presentwindowsEnabled", true); if( coverSwitch ) effectconfig.writeEntry("kwin4_effect_coverswitchEnabled", true); if( flipSwitch ) effectconfig.writeEntry("kwin4_effect_flipswitchEnabled", true); KConfigGroup boxswitchconfig( mKWinConfig, "Effect-BoxSwitch" ); boxswitchconfig.writeEntry( "TabBox", boxSwitch ); boxswitchconfig.sync(); KConfigGroup presentwindowsconfig( mKWinConfig, "Effect-PresentWindows" ); presentwindowsconfig.writeEntry( "TabBox", presentWindowSwitching ); presentwindowsconfig.sync(); KConfigGroup coverswitchconfig( mKWinConfig, "Effect-CoverSwitch" ); coverswitchconfig.writeEntry( "TabBox", coverSwitch ); coverswitchconfig.sync(); KConfigGroup flipswitchconfig( mKWinConfig, "Effect-FlipSwitch" ); flipswitchconfig.writeEntry( "TabBox", flipSwitch ); flipswitchconfig.sync(); 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 glModes[] = { "TFP", "SHM", "Fallback" }; if( config.readEntry("Backend", "OpenGL") != ((ui.compositingType->currentIndex() == 0) ? "OpenGL" : "XRender") || config.readEntry("GLMode", "TFP") != glModes[ui.glMode->currentIndex()] || config.readEntry("GLDirect", mDefaultPrefs.enableDirectRendering()) != ui.glDirect->isChecked() || config.readEntry("GLVSync", mDefaultPrefs.enableVSync()) != ui.glVSync->isChecked() || config.readEntry("DisableChecks", false ) != ui.disableChecks->isChecked()) { m_showConfirmDialog = true; advancedChanged = true; } else if( config.readEntry("HiddenPreviews", 5) != hps[ ui.windowThumbnails->currentIndex() ] - || config.readEntry("XRenderSmoothScale", false ) != ui.xrenderSmoothScale->isChecked() ) + || (int)config.readEntry("XRenderSmoothScale", false ) != ui.xrScaleFilter->currentIndex() + || config.readEntry("GLTextureFilter", 2 ) != ui.glScaleFilter->currentIndex()) advancedChanged = true; config.writeEntry("Backend", (ui.compositingType->currentIndex() == OPENGL_INDEX) ? "OpenGL" : "XRender"); config.writeEntry("HiddenPreviews", hps[ ui.windowThumbnails->currentIndex() ] ); config.writeEntry("DisableChecks", ui.disableChecks->isChecked()); config.writeEntry( "UnredirectFullscreen", ui.unredirectFullscreen->isChecked() ); + config.writeEntry("XRenderSmoothScale", ui.xrScaleFilter->currentIndex() == 1); + config.writeEntry("GLTextureFilter", ui.glScaleFilter->currentIndex()); + config.writeEntry("GLMode", glModes[ui.glMode->currentIndex()]); - config.writeEntry("GLTextureFilter", ui.glTextureFilter->currentIndex()); config.writeEntry("GLDirect", ui.glDirect->isChecked()); config.writeEntry("GLVSync", ui.glVSync->isChecked()); - config.writeEntry("XRenderSmoothScale", ui.xrenderSmoothScale->isChecked()); return advancedChanged; } void KWinCompositingConfig::save() { // 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; showConfirmDialog(advancedChanged); } } void KWinCompositingConfig::checkLoadedEffects() { // check for effects not supported by Backend or hardware // such effects are enabled but not returned by DBus method loadedEffects QDBusMessage message = QDBusMessage::createMethodCall( "org.kde.kwin", "/KWin", "org.kde.KWin", "loadedEffects" ); QDBusMessage reply = QDBusConnection::sessionBus().call( message ); KConfigGroup effectConfig = KConfigGroup( mKWinConfig, "Compositing" ); bool enabledAfter = effectConfig.readEntry( "Enabled", mDefaultPrefs.recommendCompositing() ); if( reply.type() == QDBusMessage::ReplyMessage && enabledAfter && !getenv( "KDE_FAILSAFE" )) { effectConfig = KConfigGroup( mKWinConfig, "Plugins" ); QStringList loadedEffects = reply.arguments()[0].toStringList(); 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() ) { KServiceTypeTrader* trader = KServiceTypeTrader::self(); KService::List services; QString message = i18n( "The following desktop effects could not be activated:" ); message.append( "
    " ); foreach( const QString &effect, disabledEffects ) { services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == '" + effect + '\''); message.append( "
  • " ); if( !services.isEmpty() ) message.append( services.first()->name() ); else message.append( effect); message.append( "
  • " ); } message.append( "
" ); KNotification::event( "effectsnotsupported", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) ); } } } 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); // 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(mDefaultPrefs.recommendCompositing()); ui.effectWinManagement->setChecked(true); ui.effectShadows->setChecked(true); ui.effectAnimations->setChecked(true); ui.windowSwitchingCombo->setCurrentIndex( 1 ); ui.desktopSwitchingCombo->setCurrentIndex( 1 ); ui.animationSpeedCombo->setCurrentIndex( 3 ); ui.effectSelector->defaults(); ui.compositingType->setCurrentIndex( 0 ); ui.windowThumbnails->setCurrentIndex( 1 ); ui.disableChecks->setChecked( false ); ui.unredirectFullscreen->setChecked( true ); + ui.xrScaleFilter->setCurrentIndex( 0 ); + ui.glScaleFilter->setCurrentIndex( 2 ); ui.glMode->setCurrentIndex( 0 ); - ui.glTextureFilter->setCurrentIndex( 1 ); ui.glDirect->setChecked( mDefaultPrefs.enableDirectRendering() ); ui.glVSync->setChecked( mDefaultPrefs.enableVSync() ); - ui.xrenderSmoothScale->setChecked( false ); } QString KWinCompositingConfig::quickHelp() const { return i18n("

Desktop Effects

"); } } // namespace #include "main.moc" diff --git a/kcmkwin/kwincompositing/main.h b/kcmkwin/kwincompositing/main.h index 7536fb20e..b48499b8f 100644 --- a/kcmkwin/kwincompositing/main.h +++ b/kcmkwin/kwincompositing/main.h @@ -1,96 +1,99 @@ /******************************************************************** 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 . *********************************************************************/ #ifndef __MAIN_H__ #define __MAIN_H__ #include #include #include #include "kwin_interface.h" #include "ui_main.h" #include "compositingprefs.h" #include "ktimerdialog.h" class KPluginSelector; class QLabel; namespace KWin { class ConfirmDialog : public KTimerDialog { Q_OBJECT public: ConfirmDialog(); }; class KWinCompositingConfig : public KCModule { Q_OBJECT public: KWinCompositingConfig(QWidget *parent, const QVariantList &args); virtual ~KWinCompositingConfig(); virtual QString quickHelp() const; public slots: virtual void compositingEnabled(bool enabled); virtual void showConfirmDialog(bool reinitCompositing); void currentTabChanged(int tab); virtual void load(); virtual void save(); virtual void defaults(); void reparseConfiguration(const QByteArray& conf); void loadGeneralTab(); void loadEffectsTab(); void loadAdvancedTab(); void saveGeneralTab(); void saveEffectsTab(); bool saveAdvancedTab(); void checkLoadedEffects(); void configChanged(bool reinitCompositing); void initEffectSelector(); void setupCompositingState( bool active, bool enabled = true ); + + private slots: + void toogleSmoothScaleUi( int compositingType ); private: bool effectEnabled( const QString& effect, const KConfigGroup& cfg ) const; KSharedConfigPtr mKWinConfig; Ui::KWinCompositingConfig ui; CompositingPrefs mDefaultPrefs; QMap mPreviousConfig; KTemporaryFile mTmpConfigFile; KSharedConfigPtr mTmpConfig; bool m_showConfirmDialog; OrgKdeKWinInterface* kwinInterface; }; } // namespace #endif diff --git a/kcmkwin/kwincompositing/main.ui b/kcmkwin/kwincompositing/main.ui index 91267e045..5bb71f5dd 100644 --- a/kcmkwin/kwincompositing/main.ui +++ b/kcmkwin/kwincompositing/main.ui @@ -1,627 +1,659 @@ KWinCompositingConfig 0 0 - 728 - 554 + 595 + 483 0 General Enable desktop effects true 0 Common Settings Improved window management true Shadows true Various animations true Qt::Vertical QSizePolicy::Fixed 20 20 You can find more effects, as well as effect-specific settings, in the "All Effects" tab above. true Effect for window switching: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter windowSwitchingCombo 0 0 - - - - Effect for desktop switching: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - desktopSwitchingCombo - - - 0 0 Qt::Vertical QSizePolicy::Fixed 20 20 0 0 3 Instant Very Fast Fast Normal Slow Very Slow Extremely Slow Animation speed: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter animationSpeedCombo Compositing State Qt::Horizontal 40 20 Qt::Vertical 20 0 All Effects Hint: To find out or configure how to activate an effect, look at the effect's settings. true 0 0 Advanced - - - - - - 0 - 0 - - - - Compositing type: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - compositingType - - - - - - - - 1 - 0 - - - - - OpenGL - - - - - XRender - - - - - - - - - 0 - 0 - - - - Keep window thumbnails: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - windowThumbnails - - - - - - - - 1 - 0 - - - - - Always (Breaks minimization) - + + + + + + + + 1 + 0 + + + + Compositing type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + compositingType + + - - - Only for Shown Windows - + + + + + 3 + 0 + + + + + OpenGL + + + + + XRender + + + - - - Never - + + + + Disable functionality checks + + - + - - - - Enabling this option allows compositing to be activated even if some of the internal checks fail. Doing so may make the whole desktop unusable and its use is not recommened. Use only if KWin refuses to activate compositing on a system that should be capable of compositing. - - - - Disable functionality checks - - - - - + + - OpenGL Options + General Options - + + true + + - + - 0 + 1 0 - OpenGL mode: + Keep window thumbnails: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - glMode + windowThumbnails - + - 1 + 3 0 - Texture From Pixmap + Always (Breaks minimization) - Shared Memory + Only for Shown Windows - Fallback + Never - + - 0 + 1 0 - Texture filter: + Scale method: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - glTextureFilter + xrScaleFilter - + + + 0 + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Segoe'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Crisp:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">XRenderSetPictureFilter(&quot;fast&quot;)</span> - Pretty fast on all GPUs but looks bricky</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Smooth:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">XRenderSetPictureFilter(&quot;good&quot;) </span>- linear blending.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Fast enough on newer nvidia GPUs and maybe others but also can be <span style=" text-decoration: underline;">very</span> slow, you'll have to try.</p></body></html> + + + 0 + + + + Crisp + + + + + Smooth (slower) + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Segoe'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Crisp:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">GL_NEAREST</span> - (very) fast on all GPUs but looks bricky</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Smooth:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">GL_LINEAR</span> - fast on most GPUs but a little blurry</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Accurate:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Lanczos filter, requires shader support (glsl or arb).</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Might be slow on weaker GPUs and even cause various troubles with broken drivers. (From overbrightning to segfaults)</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Fall back to &quot;Smooth&quot; if you've problems</p></body></html> + + + 1 + + + + Crisp + + + + + Smooth + + + + + Accurate + + + + + + + + + + Suspend desktop effects for fullscreen windows + + + + + + + + + + OpenGL Options + + + true + + + + - + 1 0 + + OpenGL mode: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + glMode + + + + + + + + 3 + 0 + + - Nearest (fastest) + Texture From Pixmap - Bilinear + Shared Memory - Trilinear (best quality) + Fallback - + Enable direct rendering false - + Use VSync false - - - - XRender Options - - - - - - Smooth scaling (slower) - - - - - - - - + + Qt::Vertical 20 - 0 + 40 - - - - Improves performance of fullscreen applications like games and video players. This option can cause flickering when another window is opened above the fullscreen window. - - - Suspend desktop effects for fullscreen windows - - - KTitleWidget QWidget
ktitlewidget.h
KComboBox QComboBox
kcombobox.h
KPluginSelector QWidget
kpluginselector.h
1
tabWidget useCompositing effectWinManagement effectShadows effectAnimations windowSwitchingCombo desktopSwitchingCombo animationSpeedCombo useCompositing toggled(bool) groupBox setEnabled(bool) 86 66 102 156
diff --git a/lanczosfilter.cpp b/lanczosfilter.cpp index ac665aa73..9cc3215ca 100644 --- a/lanczosfilter.cpp +++ b/lanczosfilter.cpp @@ -1,639 +1,645 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 by Fredrik Höglund Copyright (C) 2010 Martin Gräßlin 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 "lanczosfilter.h" #include "effects.h" #ifdef KWIN_HAVE_OPENGL_COMPOSITING #include #endif #include #include #include #include namespace KWin { LanczosFilter::LanczosFilter( QObject* parent ) : QObject( parent ) #ifdef KWIN_HAVE_OPENGL_COMPOSITING , m_offscreenTex( 0 ) , m_offscreenTarget( 0 ) - , m_shader( new LanczosShader( this ) ) + , m_shader( 0 ) #endif , m_inited( false) { } LanczosFilter::~LanczosFilter() { #ifdef KWIN_HAVE_OPENGL_COMPOSITING delete m_offscreenTarget; delete m_offscreenTex; #endif } void LanczosFilter::init() { if (m_inited) return; m_inited = true; #ifdef KWIN_HAVE_OPENGL_COMPOSITING - // check the blacklist + KSharedConfigPtr config = KSharedConfig::openConfig( "kwinrc" ); + + if ( config->group( "Compositing" ).readEntry( "GLTextureFilter", 2 ) != 2 ) + return; // disabled by config + + // check the blacklist KConfigGroup blacklist = config->group( "Blacklist" ).group( "Lanczos" ); if( effects->checkDriverBlacklist( blacklist ) ) { kDebug() << "Lanczos Filter disabled by driver blacklist"; return; } + m_shader = new LanczosShader( this ); if( !m_shader->init() ) { delete m_shader; m_shader = 0; } #endif } void LanczosFilter::updateOffscreenSurfaces() { #ifdef KWIN_HAVE_OPENGL_COMPOSITING int w = displayWidth(); int h = displayHeight(); if ( !GLTexture::NPOTTextureSupported() ) { w = nearestPowerOfTwo( w ); h = nearestPowerOfTwo( h ); } if ( !m_offscreenTex || m_offscreenTex->width() != w || m_offscreenTex->height() != h ) { if ( m_offscreenTex ) { delete m_offscreenTex; delete m_offscreenTarget; } m_offscreenTex = new GLTexture( w, h ); m_offscreenTex->setFilter( GL_LINEAR ); m_offscreenTex->setWrapMode( GL_CLAMP_TO_EDGE ); m_offscreenTarget = new GLRenderTarget( m_offscreenTex ); } #endif } static float sinc( float x ) { return std::sin( x * M_PI ) / ( x * M_PI ); } static float lanczos( float x, float a ) { if ( qFuzzyCompare( x + 1.0, 1.0 ) ) return 1.0; if ( qAbs( x ) >= a ) return 0.0; return sinc( x ) * sinc( x / a ); } #ifdef KWIN_HAVE_OPENGL_COMPOSITING void LanczosShader::createKernel( float delta, int *size ) { const float a = 2.0; // The two outermost samples always fall at points where the lanczos // function returns 0, so we'll skip them. const int sampleCount = qBound( 3, qCeil(delta * a) * 2 + 1 - 2, 49 ); const int center = sampleCount / 2; const int kernelSize = center + 1; const float factor = 1.0 / delta; QVector values( kernelSize ); float sum = 0; for ( int i = 0; i < kernelSize; i++ ) { const float val = lanczos( i * factor, a ); sum += i > 0 ? val * 2 : val; values[i] = val; } memset(m_kernel, 0, 25 * sizeof(QVector4D)); // Normalize the kernel for ( int i = 0; i < kernelSize; i++ ) { const float val = values[i] / sum; m_kernel[i] = QVector4D( val, val, val, val ); } *size = kernelSize; } void LanczosShader::createOffsets( int count, float width, Qt::Orientation direction ) { memset(m_offsets, 0, 25 * sizeof(QVector2D)); for ( int i = 0; i < count; i++ ) { m_offsets[i] = ( direction == Qt::Horizontal ) ? QVector2D( i / width, 0 ) : QVector2D( 0, i / width ); } } #endif void LanczosFilter::performPaint( EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data ) { #ifdef KWIN_HAVE_OPENGL_COMPOSITING if( effects->compositingType() == KWin::OpenGLCompositing && KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects ) { if (!m_inited) init(); const QRect screenRect = Workspace::self()->clientArea( ScreenArea, w->screen(), w->desktop() ); // window geometry may not be bigger than screen geometry to fit into the FBO if ( m_shader && w->width() <= screenRect.width() && w->height() <= screenRect.height() ) { double left = 0; double top = 0; double right = w->width(); double bottom = w->height(); foreach( const WindowQuad& quad, data.quads ) { // we need this loop to include the decoration padding left = qMin(left, quad.left()); top = qMin(top, quad.top()); right = qMax(right, quad.right()); bottom = qMax(bottom, quad.bottom()); } double width = right - left; double height = bottom - top; if( width > screenRect.width() || height > screenRect.height() ) { // window with padding does not fit into the framebuffer // so cut of the shadow left = 0; top = 0; width = w->width(); height = w->height(); } int tx = data.xTranslate + w->x() + left*data.xScale; int ty = data.yTranslate + w->y() + top*data.yScale; int tw = width*data.xScale; int th = height*data.yScale; const QRect textureRect(tx, ty, tw, th); int sw = width; int sh = height; GLTexture *cachedTexture = static_cast< GLTexture*>(w->data( LanczosCacheRole ).value()); if( cachedTexture ) { if( cachedTexture->width() == tw && cachedTexture->height() == th ) { cachedTexture->bind(); data.opacity *= 0.99; prepareRenderStates( cachedTexture, data.opacity, data.brightness, data.saturation ); cachedTexture->render( textureRect, textureRect ); restoreRenderStates( cachedTexture, data.opacity, data.brightness, data.saturation ); cachedTexture->unbind(); m_timer.start( 5000, this ); return; } else { // offscreen texture not matching - delete delete cachedTexture; cachedTexture = 0; w->setData( LanczosCacheRole, QVariant() ); } } WindowPaintData thumbData = data; thumbData.xScale = 1.0; thumbData.yScale = 1.0; thumbData.xTranslate = -w->x() - left; thumbData.yTranslate = -w->y() - top; thumbData.brightness = 1.0; thumbData.opacity = 1.0; thumbData.saturation = 1.0; // Bind the offscreen FBO and draw the window on it unscaled updateOffscreenSurfaces(); effects->pushRenderTarget( m_offscreenTarget ); glClear( GL_COLOR_BUFFER_BIT ); w->sceneWindow()->performPaint( mask, QRegion(0, 0, sw, sh), thumbData ); // Create a scratch texture and copy the rendered window into it GLTexture tex( sw, sh ); tex.setFilter( GL_LINEAR ); tex.setWrapMode( GL_CLAMP_TO_EDGE ); tex.bind(); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - sh, sw, sh ); // Set up the shader for horizontal scaling float dx = sw / float(tw); int kernelSize; m_shader->createKernel( dx, &kernelSize ); m_shader->createOffsets( kernelSize, sw, Qt::Horizontal ); m_shader->bind(); m_shader->setUniforms(); // Draw the window back into the FBO, this time scaled horizontally glClear( GL_COLOR_BUFFER_BIT ); glBegin( GL_QUADS ); glTexCoord2f( 0, 0 ); glVertex2i( 0, 0 ); // Top left glTexCoord2f( 1, 0 ); glVertex2i( tw, 0 ); // Top right glTexCoord2f( 1, 1 ); glVertex2i( tw, sh ); // Bottom right glTexCoord2f( 0, 1 ); glVertex2i( 0, sh ); // Bottom left glEnd(); // At this point we don't need the scratch texture anymore tex.unbind(); tex.discard(); // create scratch texture for second rendering pass GLTexture tex2( tw, sh ); tex2.setFilter( GL_LINEAR ); tex2.setWrapMode( GL_CLAMP_TO_EDGE ); tex2.bind(); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - sh, tw, sh ); // Set up the shader for vertical scaling float dy = sh / float(th); m_shader->createKernel( dy, &kernelSize ); m_shader->createOffsets( kernelSize, m_offscreenTex->height(), Qt::Vertical ); m_shader->setUniforms(); // Now draw the horizontally scaled window in the FBO at the right // coordinates on the screen, while scaling it vertically and blending it. glClear( GL_COLOR_BUFFER_BIT ); glBegin( GL_QUADS ); glTexCoord2f( 0, 0 ); glVertex2i( 0, 0 ); // Top left glTexCoord2f( 1, 0 ); glVertex2i( tw, 0 ); // Top right glTexCoord2f( 1, 1 ); glVertex2i( tw, th ); // Bottom right glTexCoord2f( 0, 1 ); glVertex2i( 0, th ); // Bottom left glEnd(); tex2.unbind(); tex2.discard(); m_shader->unbind(); // create cache texture GLTexture *cache = new GLTexture( tw, th ); cache->setFilter( GL_LINEAR ); cache->setWrapMode( GL_CLAMP_TO_EDGE ); cache->bind(); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - th, tw, th ); effects->popRenderTarget(); prepareRenderStates( cache, data.opacity, data.brightness, data.saturation ); cache->render( textureRect, textureRect ); restoreRenderStates( cache, data.opacity, data.brightness, data.saturation ); cache->unbind(); w->setData( LanczosCacheRole, QVariant::fromValue( static_cast( cache ))); // Delete the offscreen surface after 5 seconds m_timer.start( 5000, this ); return; } } // if ( effects->compositingType() == KWin::OpenGLCompositing ) #endif w->sceneWindow()->performPaint( mask, region, data ); } // End of function void LanczosFilter::timerEvent( QTimerEvent *event ) { #ifdef KWIN_HAVE_OPENGL_COMPOSITING if (event->timerId() == m_timer.timerId()) { m_timer.stop(); delete m_offscreenTarget; delete m_offscreenTex; m_offscreenTarget = 0; m_offscreenTex = 0; foreach( EffectWindow* w, effects->stackingOrder() ) { QVariant cachedTextureVariant = w->data( LanczosCacheRole ); if( cachedTextureVariant.isValid() ) { GLTexture *cachedTexture = static_cast< GLTexture*>(cachedTextureVariant.value()); delete cachedTexture; cachedTexture = 0; w->setData( LanczosCacheRole, QVariant() ); } } } #endif } void LanczosFilter::prepareRenderStates( GLTexture* tex, double opacity, double brightness, double saturation ) { #ifdef KWIN_HAVE_OPENGL_COMPOSITING const bool alpha = false; // true; WORKAROUND - "true" causes issues with at least nvidia chips and translucent windows "overbrightning" // setup blending of transparent windows glPushAttrib( GL_ENABLE_BIT ); glEnable( GL_BLEND ); glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); if( saturation != 1.0 && tex->saturationSupported()) { // First we need to get the color from [0; 1] range to [0.5; 1] range glActiveTexture( GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5}; glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant ); tex->bind(); // Then we take dot product of the result of previous pass and // saturation_constant. This gives us completely unsaturated // (greyscale) image // Note that both operands have to be in range [0.5; 1] since opengl // automatically substracts 0.5 from them glActiveTexture( GL_TEXTURE1 ); float saturation_constant[] = { 0.5 + 0.5*0.30, 0.5 + 0.5*0.59, 0.5 + 0.5*0.11, saturation }; glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant ); tex->bind(); // Finally we need to interpolate between the original image and the // greyscale image to get wanted level of saturation glActiveTexture( GL_TEXTURE2 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant ); // Also replace alpha by primary color's alpha here glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); // And make primary color contain the wanted opacity glColor4f( opacity, opacity, opacity, opacity ); tex->bind(); if( alpha || brightness != 1.0f ) { glActiveTexture( GL_TEXTURE3 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); // The color has to be multiplied by both opacity and brightness float opacityByBrightness = opacity * brightness; glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity ); if( alpha ) { // Multiply original texture's alpha by our opacity glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA ); } else { // Alpha will be taken from previous stage glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); } tex->bind(); } glActiveTexture(GL_TEXTURE0 ); } else if( opacity != 1.0 || brightness != 1.0 ) { // the window is additionally configured to have its opacity adjusted, // do it float opacityByBrightness = opacity * brightness; if( alpha) { glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity); } else { // Multiply color by brightness and replace alpha by opacity float constant[] = { opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity }; glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant ); } } #endif } void LanczosFilter::restoreRenderStates( GLTexture* tex, double opacity, double brightness, double saturation ) { #ifdef KWIN_HAVE_OPENGL_COMPOSITING if( opacity != 1.0 || saturation != 1.0 || brightness != 1.0f ) { if( saturation != 1.0 && tex->saturationSupported()) { glActiveTexture(GL_TEXTURE3); glDisable( tex->target()); glActiveTexture(GL_TEXTURE2); glDisable( tex->target()); glActiveTexture(GL_TEXTURE1); glDisable( tex->target()); glActiveTexture(GL_TEXTURE0); } } glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glColor4f( 0, 0, 0, 0 ); glPopAttrib(); // ENABLE_BIT #endif } /************************************************ * LanczosShader ************************************************/ #ifdef KWIN_HAVE_OPENGL_COMPOSITING LanczosShader::LanczosShader( QObject* parent ) : QObject( parent ) , m_shader( 0 ) , m_arbProgram( 0 ) { } LanczosShader::~LanczosShader() { delete m_shader; if( m_arbProgram ) { glDeleteProgramsARB(1, &m_arbProgram); m_arbProgram = 0; } } void LanczosShader::bind() { if( m_shader ) m_shader->bind(); else { glEnable(GL_FRAGMENT_PROGRAM_ARB); glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_arbProgram); } } void LanczosShader::unbind() { if( m_shader ) m_shader->unbind(); else { int boundObject; glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &boundObject); if( boundObject == m_arbProgram ) { glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); glDisable(GL_FRAGMENT_PROGRAM_ARB); } } } void LanczosShader::setUniforms() { if( m_shader ) { glUniform1i( m_uTexUnit, 0 ); glUniform2fv( m_uOffsets, 25, (const GLfloat*)m_offsets ); glUniform4fv( m_uKernel, 25, (const GLfloat*)m_kernel ); } else { for( int i=0; i<25; ++i ) { glProgramLocalParameter4fARB( GL_FRAGMENT_PROGRAM_ARB, i, m_offsets[i].x(), m_offsets[i].y(), 0, 0 ); } for( int i=0; i<25; ++i ) { glProgramLocalParameter4fARB( GL_FRAGMENT_PROGRAM_ARB, i+25, m_kernel[i].x(), m_kernel[i].y(), m_kernel[i].z(), m_kernel[i].w() ); } } } bool LanczosShader::init() { if ( GLShader::fragmentShaderSupported() && GLShader::vertexShaderSupported() && GLRenderTarget::supported() ) { m_shader = new GLShader(":/resources/lanczos-vertex.glsl", ":/resources/lanczos-fragment.glsl"); if (m_shader->isValid()) { m_shader->bind(); m_uTexUnit = m_shader->uniformLocation("texUnit"); m_uKernel = m_shader->uniformLocation("kernel"); m_uOffsets = m_shader->uniformLocation("offsets"); m_shader->unbind(); return true; } else { kDebug(1212) << "Shader is not valid"; m_shader = 0; // try ARB shader } } // try to create an ARB Shader if( !hasGLExtension("GL_ARB_fragment_program") ) return false; QByteArray text; QTextStream stream(&text); stream << "!!ARBfp1.0\n"; stream << "TEMP coord;\n"; // temporary variable to store texcoord stream << "TEMP color;\n"; // temporary variable to store fetched texture colors stream << "TEMP sum;\n"; // variable to render the final result stream << "TEX sum, fragment.texcoord, texture[0], 2D;\n"; // sum = texture2D(texUnit, gl_TexCoord[0].st) stream << "MUL sum, sum, program.local[25];\n"; // sum = sum * kernel[0] for( int i=1; i<25; ++i ) { stream << "ADD coord, fragment.texcoord, program.local[" << i << "];\n"; // coord = gl_TexCoord[0] + offset[i] stream << "TEX color, coord, texture[0], 2D;\n"; // color = texture2D(texUnit, coord) stream << "MAD sum, color, program.local[" << (25+i) << "], sum;\n"; // sum += color * kernel[i] stream << "SUB coord, fragment.texcoord, program.local[" << i << "];\n"; // coord = gl_TexCoord[0] - offset[i] stream << "TEX color, coord, texture[0], 2D;\n"; // color = texture2D(texUnit, coord) stream << "MAD sum, color, program.local[" << (25+i) << "], sum;\n"; // sum += color * kernel[i] } stream << "MOV result.color, sum;\n"; // gl_FragColor = sum stream << "END\n"; stream.flush(); glEnable(GL_FRAGMENT_PROGRAM_ARB); glGenProgramsARB(1, &m_arbProgram); glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_arbProgram); glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, text.length(), text.constData()); if( glGetError() ) { const char *error = (const char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB); kError() << "Failed to compile fragment program:" << error; return false; } glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); glDisable(GL_FRAGMENT_PROGRAM_ARB); kDebug( 1212 ) << "ARB Shader compiled, id: " << m_arbProgram; return true; } #endif } // namespace diff --git a/options.cpp b/options.cpp index e4edd4ea4..4ec5e9316 100644 --- a/options.cpp +++ b/options.cpp @@ -1,462 +1,462 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak 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 "options.h" #ifndef KCMRULES #include #include #include #include #include #include #include #include "client.h" #include "compositingprefs.h" #include #endif namespace KWin { #ifndef KCMRULES Options::Options() : electric_borders( 0 ) , electric_border_delay( 0 ) { updateSettings(); } Options::~Options() { } unsigned long Options::updateSettings() { KSharedConfig::Ptr _config = KGlobal::config(); unsigned long changed = 0; changed |= KDecorationOptions::updateSettings( _config.data() ); // read decoration settings KConfigGroup config( _config, "Windows" ); moveMode = stringToMoveResizeMode( config.readEntry("MoveMode", "Opaque" )); resizeMode = stringToMoveResizeMode( config.readEntry("ResizeMode", "Opaque" )); show_geometry_tip = config.readEntry("GeometryTip", false); QString val; val = config.readEntry ("FocusPolicy", "ClickToFocus"); focusPolicy = ClickToFocus; // what a default :-) if ( val == "FocusFollowsMouse" ) focusPolicy = FocusFollowsMouse; else if ( val == "FocusUnderMouse" ) focusPolicy = FocusUnderMouse; else if ( val == "FocusStrictlyUnderMouse" ) focusPolicy = FocusStrictlyUnderMouse; val = config.readEntry ("AltTabStyle", "KDE"); altTabStyle = KDE; // what a default :-) if ( val == "CDE" ) altTabStyle = CDE; separateScreenFocus = config.readEntry( "SeparateScreenFocus", false ); activeMouseScreen = config.readEntry( "ActiveMouseScreen", focusPolicy != ClickToFocus ); rollOverDesktops = config.readEntry("RollOverDesktops", true); legacyFullscreenSupport = config.readEntry( "LegacyFullscreenSupport", false ); // focusStealingPreventionLevel = config.readEntry( "FocusStealingPreventionLevel", 2 ); // TODO use low level for now focusStealingPreventionLevel = config.readEntry( "FocusStealingPreventionLevel", 1 ); focusStealingPreventionLevel = qMax( 0, qMin( 4, focusStealingPreventionLevel )); if( !focusPolicyIsReasonable()) // #48786, comments #7 and later focusStealingPreventionLevel = 0; xineramaEnabled = config.readEntry ("XineramaEnabled", true); xineramaPlacementEnabled = config.readEntry ("XineramaPlacementEnabled", true); xineramaMovementEnabled = config.readEntry ("XineramaMovementEnabled", true); xineramaMaximizeEnabled = config.readEntry ("XineramaMaximizeEnabled", true); xineramaFullscreenEnabled = config.readEntry ("XineramaFullscreenEnabled", true); placement = Placement::policyFromString( config.readEntry("Placement"), true ); xineramaPlacementScreen = qBound( -1, config.readEntry( "XineramaPlacementScreen", -1 ), Kephal::ScreenUtils::numScreens() - 1 ); if( focusPolicy == ClickToFocus ) { autoRaise = false; autoRaiseInterval = 0; delayFocus = false; delayFocusInterval = 0; } else { autoRaise = config.readEntry("AutoRaise", false); autoRaiseInterval = config.readEntry("AutoRaiseInterval", 0 ); delayFocus = config.readEntry("DelayFocus", false); delayFocusInterval = config.readEntry("DelayFocusInterval", 0 ); } shadeHover = config.readEntry("ShadeHover", false); shadeHoverInterval = config.readEntry("ShadeHoverInterval", 250 ); tilingOn = config.readEntry( "TilingOn", false ); tilingLayout = static_cast(config.readEntry( "TilingDefaultLayout", 0 )); tilingRaisePolicy = config.readEntry( "TilingRaisePolicy", 0 ); // important: autoRaise implies ClickRaise clickRaise = autoRaise || config.readEntry("ClickRaise", true); borderSnapZone = config.readEntry("BorderSnapZone", 10); windowSnapZone = config.readEntry("WindowSnapZone", 10); centerSnapZone = config.readEntry("CenterSnapZone", 0); snapOnlyWhenOverlapping = config.readEntry("SnapOnlyWhenOverlapping", false); // Electric borders KConfigGroup borderConfig( _config, "ElectricBorders" ); electric_border_top = electricBorderAction( borderConfig.readEntry( "Top", "None" )); electric_border_top_right = electricBorderAction( borderConfig.readEntry( "TopRight", "None" )); electric_border_right = electricBorderAction( borderConfig.readEntry( "Right", "None" )); electric_border_bottom_right = electricBorderAction( borderConfig.readEntry( "BottomRight", "None" )); electric_border_bottom = electricBorderAction( borderConfig.readEntry( "Bottom", "None" )); electric_border_bottom_left = electricBorderAction( borderConfig.readEntry( "BottomLeft", "None" )); electric_border_left = electricBorderAction( borderConfig.readEntry( "Left", "None" )); electric_border_top_left = electricBorderAction( borderConfig.readEntry( "TopLeft", "None" )); electric_borders = config.readEntry("ElectricBorders", 0); electric_border_delay = config.readEntry("ElectricBorderDelay", 150); electric_border_cooldown = config.readEntry("ElectricBorderCooldown", 350); electric_border_pushback_pixels = config.readEntry("ElectricBorderPushbackPixels", 1); electric_border_maximize = config.readEntry("ElectricBorderMaximize", true); electric_border_tiling = config.readEntry("ElectricBorderTiling", true); OpTitlebarDblClick = windowOperation( config.readEntry("TitlebarDoubleClickCommand", "Maximize"), true ); setOpMaxButtonLeftClick( windowOperation( config.readEntry("MaximizeButtonLeftClickCommand", "Maximize"), true )); setOpMaxButtonMiddleClick( windowOperation( config.readEntry("MaximizeButtonMiddleClickCommand", "Maximize (vertical only)"), true )); setOpMaxButtonRightClick( windowOperation( config.readEntry("MaximizeButtonRightClickCommand", "Maximize (horizontal only)"), true )); ignorePositionClasses = config.readEntry("IgnorePositionClasses",QStringList()); ignoreFocusStealingClasses = config.readEntry("IgnoreFocusStealingClasses",QStringList()); // Qt3.2 and older had resource class all lowercase, but Qt3.3 has it capitalized // therefore Client::resourceClass() forces lowercase, force here lowercase as well for( QStringList::Iterator it = ignorePositionClasses.begin(); it != ignorePositionClasses.end(); ++it ) (*it) = (*it).toLower(); for( QStringList::Iterator it = ignoreFocusStealingClasses.begin(); it != ignoreFocusStealingClasses.end(); ++it ) (*it) = (*it).toLower(); killPingTimeout = config.readEntry( "KillPingTimeout", 5000 ); hideUtilityWindowsForInactive = config.readEntry( "HideUtilityWindowsForInactive", true); inactiveTabsSkipTaskbar = config.readEntry( "InactiveTabsSkipTaskbar", false ); autogroupSimilarWindows = config.readEntry( "AutogroupSimilarWindows", false ); autogroupInForeground = config.readEntry( "AutogroupInForeground", true ); showDesktopIsMinimizeAll = config.readEntry( "ShowDesktopIsMinimizeAll", false ); borderless_maximized_windows = config.readEntry( "BorderlessMaximizedWindows", false ); // Mouse bindings config = KConfigGroup( _config, "MouseBindings" ); CmdActiveTitlebar1 = mouseCommand(config.readEntry("CommandActiveTitlebar1","Raise"), true ); CmdActiveTitlebar2 = mouseCommand(config.readEntry("CommandActiveTitlebar2","Start Window Tab Drag"), true ); CmdActiveTitlebar3 = mouseCommand(config.readEntry("CommandActiveTitlebar3","Operations menu"), true ); CmdInactiveTitlebar1 = mouseCommand(config.readEntry("CommandInactiveTitlebar1","Activate and raise"), true ); CmdInactiveTitlebar2 = mouseCommand(config.readEntry("CommandInactiveTitlebar2","Start Window Tab Drag"), true ); CmdInactiveTitlebar3 = mouseCommand(config.readEntry("CommandInactiveTitlebar3","Operations menu"), true ); CmdTitlebarWheel = mouseWheelCommand(config.readEntry("CommandTitlebarWheel","Switch to Window Tab to the Left/Right")); CmdWindow1 = mouseCommand(config.readEntry("CommandWindow1","Activate, raise and pass click"), false ); CmdWindow2 = mouseCommand(config.readEntry("CommandWindow2","Activate and pass click"), false ); CmdWindow3 = mouseCommand(config.readEntry("CommandWindow3","Activate and pass click"), false ); CmdWindowWheel = mouseCommand(config.readEntry("CommandWindowWheel", "Scroll"), false); CmdAllModKey = (config.readEntry("CommandAllKey","Alt") == "Meta") ? Qt::Key_Meta : Qt::Key_Alt; CmdAll1 = mouseCommand(config.readEntry("CommandAll1","Move"), false ); CmdAll2 = mouseCommand(config.readEntry("CommandAll2","Toggle raise and lower"), false ); CmdAll3 = mouseCommand(config.readEntry("CommandAll3","Resize"), false ); CmdAllWheel = mouseWheelCommand(config.readEntry("CommandAllWheel","Nothing")); config=KConfigGroup(_config,"Compositing"); refreshRate = config.readEntry( "RefreshRate", 0 ); // Read button tooltip animation effect from kdeglobals // Since we want to allow users to enable window decoration tooltips // and not kstyle tooltips and vise-versa, we don't read the // "EffectNoTooltip" setting from kdeglobals. #if 0 FIXME: we have no mac style menu implementation in kwin anymore, so this just breaks things for people! KConfig _globalConfig("kdeglobals"); KConfigGroup globalConfig(&_globalConfig, "KDE"); topmenus = globalConfig.readEntry("macStyle", false); #else topmenus = false; #endif // QToolTip::setGloballyEnabled( d->show_tooltips ); // KDE4 this probably needs to be done manually in clients // Driver-specific config detection reloadCompositingSettings(); return changed; } void Options::reloadCompositingSettings() { KSharedConfig::Ptr _config = KGlobal::config(); KConfigGroup config(_config, "Compositing"); // do not even detect compositing preferences if explicitly disabled bool environmentForce = false; if( getenv( "KWIN_COMPOSE" )) { // if compositing is enforced by the environment variable, the preferences have to be read const char c = getenv( "KWIN_COMPOSE" )[ 0 ]; if( c == 'X' || c == 'O' ) environmentForce = true; } if( config.hasKey( "Enabled" ) && !config.readEntry( "Enabled", true ) && !environmentForce ) { useCompositing = false; return; } // Compositing settings CompositingPrefs prefs; prefs.detect(); useCompositing = config.readEntry( "Enabled" , prefs.recommendCompositing()); QString compositingBackend = config.readEntry("Backend", "OpenGL"); if( compositingBackend == "XRender" ) compositingMode = XRenderCompositing; else compositingMode = OpenGLCompositing; disableCompositingChecks = config.readEntry("DisableChecks", false); QString glmode = config.readEntry("GLMode", "TFP" ).toUpper(); if( glmode == "TFP" ) glMode = GLTFP; else if( glmode == "SHM" ) glMode = GLSHM; else glMode = GLFallback; glDirect = config.readEntry("GLDirect", prefs.enableDirectRendering() ); glVSync = config.readEntry("GLVSync", prefs.enableVSync() ); - glSmoothScale = qBound( -1, config.readEntry( "GLTextureFilter", -1 ), 2 ); + glSmoothScale = qBound( -1, config.readEntry( "GLTextureFilter", 2 ), 2 ); glStrictBinding = config.readEntry( "GLStrictBinding", prefs.strictBinding()); xrenderSmoothScale = config.readEntry("XRenderSmoothScale", false ); hiddenPreviews = HiddenPreviewsShown; // 4 - off, 5 - shown, 6 - always, other are old values int hps = config.readEntry( "HiddenPreviews", 5 ); if( hps == 4 ) hiddenPreviews = HiddenPreviewsNever; else if( hps == 5 ) hiddenPreviews = HiddenPreviewsShown; else if( hps == 6 ) hiddenPreviews = HiddenPreviewsAlways; unredirectFullscreen = config.readEntry( "UnredirectFullscreen", true ); animationSpeed = qBound( 0, config.readEntry( "AnimationSpeed", 3 ), 6 ); } ElectricBorderAction Options::electricBorderAction( const QString& name ) { QString lowerName = name.toLower(); if( lowerName == "dashboard" ) return ElectricActionDashboard; else if( lowerName == "showdesktop" ) return ElectricActionShowDesktop; else if( lowerName == "lockscreen" ) return ElectricActionLockScreen; else if( lowerName == "preventscreenlocking" ) return ElectricActionPreventScreenLocking; return ElectricActionNone; } // restricted should be true for operations that the user may not be able to repeat // if the window is moved out of the workspace (e.g. if the user moves a window // by the titlebar, and moves it too high beneath Kicker at the top edge, they // may not be able to move it back, unless they know about Alt+LMB) Options::WindowOperation Options::windowOperation(const QString &name, bool restricted ) { if (name == "Move") return restricted ? MoveOp : UnrestrictedMoveOp; else if (name == "Resize") return restricted ? ResizeOp : UnrestrictedResizeOp; else if (name == "Maximize") return MaximizeOp; else if (name == "Minimize") return MinimizeOp; else if (name == "Close") return CloseOp; else if (name == "OnAllDesktops") return OnAllDesktopsOp; else if (name == "Shade") return ShadeOp; else if (name == "Operations") return OperationsOp; else if (name == "Maximize (vertical only)") return VMaximizeOp; else if (name == "Maximize (horizontal only)") return HMaximizeOp; else if (name == "Lower") return LowerOp; return NoOp; } Options::MouseCommand Options::mouseCommand(const QString &name, bool restricted ) { QString lowerName = name.toLower(); if (lowerName == "raise") return MouseRaise; if (lowerName == "lower") return MouseLower; if (lowerName == "operations menu") return MouseOperationsMenu; if (lowerName == "toggle raise and lower") return MouseToggleRaiseAndLower; if (lowerName == "activate and raise") return MouseActivateAndRaise; if (lowerName == "activate and lower") return MouseActivateAndLower; if (lowerName == "activate") return MouseActivate; if (lowerName == "activate, raise and pass click") return MouseActivateRaiseAndPassClick; if (lowerName == "activate and pass click") return MouseActivateAndPassClick; if (lowerName == "scroll") return MouseNothing; if (lowerName == "activate and scroll") return MouseActivateAndPassClick; if (lowerName == "activate, raise and scroll") return MouseActivateRaiseAndPassClick; if (lowerName == "activate, raise and move") return restricted ? MouseActivateRaiseAndMove : MouseActivateRaiseAndUnrestrictedMove; if (lowerName == "move") return restricted ? MouseMove : MouseUnrestrictedMove; if (lowerName == "resize") return restricted ? MouseResize : MouseUnrestrictedResize; if (lowerName == "shade") return MouseShade; if (lowerName == "minimize") return MouseMinimize; if (lowerName == "start window tab drag") return MouseClientGroupDrag; if (lowerName == "close") return MouseClose; if (lowerName == "nothing") return MouseNothing; return MouseNothing; } Options::MouseWheelCommand Options::mouseWheelCommand(const QString &name) { QString lowerName = name.toLower(); if (lowerName == "raise/lower") return MouseWheelRaiseLower; if (lowerName == "shade/unshade") return MouseWheelShadeUnshade; if (lowerName == "maximize/restore") return MouseWheelMaximizeRestore; if (lowerName == "above/below") return MouseWheelAboveBelow; if (lowerName == "previous/next desktop") return MouseWheelPreviousNextDesktop; if (lowerName == "change opacity") return MouseWheelChangeOpacity; if (lowerName == "switch to window tab to the left/right") return MouseWheelChangeGroupWindow; if (lowerName == "nothing") return MouseWheelNothing; return MouseWheelChangeGroupWindow; } bool Options::showGeometryTip() { return show_geometry_tip; } ElectricBorderAction Options::electricBorderAction( ElectricBorder edge ) { switch( edge ) { case ElectricTop: return electric_border_top; case ElectricTopRight: return electric_border_top_right; case ElectricRight: return electric_border_right; case ElectricBottomRight: return electric_border_bottom_right; case ElectricBottom: return electric_border_bottom; case ElectricBottomLeft: return electric_border_bottom_left; case ElectricLeft: return electric_border_left; case ElectricTopLeft: return electric_border_top_left; default: // fallthrough break; } return ElectricActionNone; } int Options::electricBorders() { return electric_borders; } int Options::electricBorderDelay() { return electric_border_delay; } int Options::electricBorderCooldown() { return electric_border_cooldown; } bool Options::checkIgnoreFocusStealing( const Client* c ) { return ignoreFocusStealingClasses.contains(QString::fromLatin1(c->resourceClass())); } Options::MouseCommand Options::wheelToMouseCommand( MouseWheelCommand com, int delta ) { switch( com ) { case MouseWheelRaiseLower: return delta > 0 ? MouseRaise : MouseLower; case MouseWheelShadeUnshade: return delta > 0 ? MouseSetShade : MouseUnsetShade; case MouseWheelMaximizeRestore: return delta > 0 ? MouseMaximize : MouseRestore; case MouseWheelAboveBelow: return delta > 0 ? MouseAbove : MouseBelow; case MouseWheelPreviousNextDesktop: return delta > 0 ? MousePreviousDesktop : MouseNextDesktop; case MouseWheelChangeOpacity: return delta > 0 ? MouseOpacityMore : MouseOpacityLess; case MouseWheelChangeGroupWindow: return delta > 0 ? MouseLeftGroupWindow : MouseRightGroupWindow; default: return MouseNothing; } } #endif Options::MoveResizeMode Options::stringToMoveResizeMode( const QString& s ) { return s == "Opaque" ? Opaque : Transparent; } const char* Options::moveResizeModeToString( MoveResizeMode mode ) { return mode == Opaque ? "Opaque" : "Transparent"; } double Options::animationTimeFactor() const { const double factors[] = { 0, 0.2, 0.5, 1, 2, 4, 20 }; return factors[ animationSpeed ]; } } // namespace diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 3a2d574df..dc0a2ed08 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -1,2366 +1,2344 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Based on glcompmgr code by Felix Bellaby. Using code from Compiz and Beryl. 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 . *********************************************************************/ /* This is the OpenGL-based compositing code. It is the primary and most powerful compositing backend. Sources and other compositing managers: ======================================= - http://opengl.org - documentation - OpenGL Redbook (http://opengl.org/documentation/red_book/ - note it's only version 1.1) - GLX docs (http://opengl.org/documentation/specs/glx/glx1.4.pdf) - extensions docs (http://www.opengl.org/registry/) - glcompmgr - http://lists.freedesktop.org/archives/xorg/2006-July/017006.html , - http://www.mail-archive.com/compiz%40lists.freedesktop.org/msg00023.html - simple and easy to understand - works even without texture_from_pixmap extension - claims to support several different gfx cards - compile with something like "gcc -Wall glcompmgr-0.5.c `pkg-config --cflags --libs glib-2.0` -lGL -lXcomposite -lXdamage -L/usr/X11R6/lib" - compiz - git clone git://anongit.freedesktop.org/git/xorg/app/compiz - the ultimate - glxcompmgr - git clone git://anongit.freedesktop.org/git/xorg/app/glxcompgr - a rather old version of compiz, but also simpler and as such simpler to understand - beryl - a fork of Compiz - http://beryl-project.org - git clone git://anongit.beryl-project.org/beryl/beryl-core (or beryl-plugins etc. , the full list should be at git://anongit.beryl-project.org/beryl/) - libcm (metacity) - cvs -d :pserver:anonymous@anoncvs.gnome.org:/cvs/gnome co libcm - not much idea about it, the model differs a lot from KWin/Compiz/Beryl - does not seem to be very powerful or with that much development going on */ #include "scene_opengl.h" #include #include "utils.h" #include "client.h" #include "deleted.h" #include "effects.h" #include #include #include // turns on checks for opengl errors in various places (for easier finding of them) // normally only few of them are enabled //#define CHECK_GL_ERROR #ifdef KWIN_HAVE_OPENGL_COMPOSITING #include #include namespace KWin { //**************************************** // SceneOpenGL //**************************************** // the configs used for the destination GLXFBConfig SceneOpenGL::fbcbuffer_db; GLXFBConfig SceneOpenGL::fbcbuffer_nondb; // the configs used for windows SceneOpenGL::FBConfigInfo SceneOpenGL::fbcdrawableinfo[ 32 + 1 ]; // GLX content GLXContext SceneOpenGL::ctxbuffer; GLXContext SceneOpenGL::ctxdrawable; // the destination drawable where the compositing is done GLXDrawable SceneOpenGL::glxbuffer = None; GLXDrawable SceneOpenGL::last_pixmap = None; bool SceneOpenGL::tfp_mode; // using glXBindTexImageEXT (texture_from_pixmap) bool SceneOpenGL::db; // destination drawable is double-buffered bool SceneOpenGL::shm_mode; #ifdef HAVE_XSHM XShmSegmentInfo SceneOpenGL::shm; #endif SceneOpenGL::SceneOpenGL( Workspace* ws ) : Scene( ws ) , init_ok( false ) , selfCheckDone( false ) { if( !Extensions::glxAvailable()) { kDebug( 1212 ) << "No glx extensions available"; return; // error } initGLX(); // check for FBConfig support if( !hasGLExtension( "GLX_SGIX_fbconfig" ) || !glXGetFBConfigAttrib || !glXGetFBConfigs || !glXGetVisualFromFBConfig || !glXCreatePixmap || !glXDestroyPixmap || !glXCreateWindow || !glXDestroyWindow ) { kError( 1212 ) << "GLX_SGIX_fbconfig or required GLX functions missing"; return; // error } if( !selectMode()) return; // error if( !initBuffer()) // create destination buffer return; // error if( !initRenderingContext()) return; // error // Initialize OpenGL initGL(); if( QString((const char*)glGetString( GL_RENDERER )) == "Software Rasterizer" ) { kError( 1212 ) << "OpenGL Software Rasterizer detected. Falling back to XRender."; QTimer::singleShot( 0, Workspace::self(), SLOT( fallbackToXRenderCompositing())); return; } if( !hasGLExtension( "GL_ARB_texture_non_power_of_two" ) && !hasGLExtension( "GL_ARB_texture_rectangle" )) { kError( 1212 ) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing"; return; // error } if( db ) glDrawBuffer( GL_BACK ); // Check whether certain features are supported has_waitSync = false; if( glXGetVideoSync && glXIsDirect( display(), ctxbuffer ) && options->glVSync ) { unsigned int sync; if( glXGetVideoSync( &sync ) == 0 ) { if( glXWaitVideoSync( 1, 0, &sync ) == 0 ) has_waitSync = true; } } // OpenGL scene setup glMatrixMode( GL_PROJECTION ); glLoadIdentity(); float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * tan( fovy * M_PI / 360.0f ); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; // swap top and bottom to have OpenGL coordinate system match X system glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); float scaleFactor = 1.1 * tan( fovy * M_PI / 360.0f )/ymax; glTranslatef( xmin*scaleFactor, ymax*scaleFactor, -1.1 ); glScalef( (xmax-xmin)*scaleFactor/displayWidth(), -(ymax-ymin)*scaleFactor/displayHeight(), 0.001 ); if( checkGLError( "Init" )) { kError( 1212 ) << "OpenGL compositing setup failed"; return; // error } // selfcheck is broken (see Bug 253357) #if 0 // Do self-check immediatelly during compositing setup only when it's not KWin startup // at the same time (in other words, only when activating compositing using the kcm). // Currently selfcheck causes bad flicker (due to X mapping the overlay window // for too long?) which looks bad during KDE startup. if( !initting ) { if( !selfCheck()) return; selfCheckDone = true; } #endif kDebug( 1212 ) << "DB:" << db << ", TFP:" << tfp_mode << ", SHM:" << shm_mode << ", Direct:" << bool( glXIsDirect( display(), ctxbuffer )) << endl; init_ok = true; } SceneOpenGL::~SceneOpenGL() { if( !init_ok ) { // TODO this probably needs to clean up whatever has been created until the failure wspace->destroyOverlay(); return; } foreach( Window* w, windows ) delete w; // do cleanup after initBuffer() glXMakeCurrent( display(), None, NULL ); glXDestroyContext( display(), ctxbuffer ); if( wspace->overlayWindow()) { if( hasGLXVersion( 1, 3 )) glXDestroyWindow( display(), glxbuffer ); XDestroyWindow( display(), buffer ); wspace->destroyOverlay(); } else { glXDestroyPixmap( display(), glxbuffer ); XFreeGC( display(), gcroot ); XFreePixmap( display(), buffer ); } if( shm_mode ) cleanupShm(); if( !tfp_mode && !shm_mode ) { if( last_pixmap != None ) glXDestroyPixmap( display(), last_pixmap ); glXDestroyContext( display(), ctxdrawable ); } SceneOpenGL::EffectFrame::cleanup(); checkGLError( "Cleanup" ); } bool SceneOpenGL::initFailed() const { return !init_ok; } bool SceneOpenGL::selectMode() { // select mode - try TFP first, then SHM, otherwise fallback mode shm_mode = false; tfp_mode = false; if( options->glMode == Options::GLTFP ) { if( initTfp()) tfp_mode = true; else if( initShm()) shm_mode = true; } else if( options->glMode == Options::GLSHM ) { if( initShm()) shm_mode = true; else if( initTfp()) tfp_mode = true; } if( !initDrawableConfigs()) return false; return true; } bool SceneOpenGL::initTfp() { if( glXBindTexImageEXT == NULL || glXReleaseTexImageEXT == NULL ) return false; return true; } bool SceneOpenGL::initShm() { #ifdef HAVE_XSHM int major, minor; Bool pixmaps; if( !XShmQueryVersion( display(), &major, &minor, &pixmaps ) || !pixmaps ) return false; if( XShmPixmapFormat( display()) != ZPixmap ) return false; const int MAXSIZE = 4096 * 2048 * 4; // TODO check there are not larger windows // TODO check that bytes_per_line doesn't involve padding? shm.readOnly = False; shm.shmid = shmget( IPC_PRIVATE, MAXSIZE, IPC_CREAT | 0600 ); if( shm.shmid < 0 ) return false; shm.shmaddr = ( char* ) shmat( shm.shmid, NULL, 0 ); if( shm.shmaddr == ( void * ) -1 ) { shmctl( shm.shmid, IPC_RMID, 0 ); return false; } #ifdef __linux__ // mark as deleted to automatically free the memory in case // of a crash (but this doesn't work e.g. on Solaris ... oh well) shmctl( shm.shmid, IPC_RMID, 0 ); #endif KXErrorHandler errs; XShmAttach( display(), &shm ); if( errs.error( true )) { #ifndef __linux__ shmctl( shm.shmid, IPC_RMID, 0 ); #endif shmdt( shm.shmaddr ); return false; } return true; #else return false; #endif } void SceneOpenGL::cleanupShm() { #ifdef HAVE_XSHM shmdt( shm.shmaddr ); #ifndef __linux__ shmctl( shm.shmid, IPC_RMID, 0 ); #endif #endif } bool SceneOpenGL::initRenderingContext() { bool direct_rendering = options->glDirect; if( !tfp_mode && !shm_mode ) direct_rendering = false; // fallback doesn't seem to work with direct rendering KXErrorHandler errs1; ctxbuffer = glXCreateNewContext( display(), fbcbuffer, GLX_RGBA_TYPE, NULL, direct_rendering ? GL_TRUE : GL_FALSE ); bool failed = ( ctxbuffer == NULL || !glXMakeCurrent( display(), glxbuffer, ctxbuffer )); if( errs1.error( true )) // always check for error( having it all in one if() could skip failed = true; // it due to evaluation short-circuiting if( failed ) { if( !direct_rendering ) { kDebug( 1212 ).nospace() << "Couldn't initialize rendering context (" << KXErrorHandler::errorMessage( errs1.errorEvent()) << ")"; return false; } glXMakeCurrent( display(), None, NULL ); if( ctxbuffer != NULL ) glXDestroyContext( display(), ctxbuffer ); direct_rendering = false; // try again KXErrorHandler errs2; ctxbuffer = glXCreateNewContext( display(), fbcbuffer, GLX_RGBA_TYPE, NULL, GL_FALSE ); bool failed = ( ctxbuffer == NULL || !glXMakeCurrent( display(), glxbuffer, ctxbuffer )); if( errs2.error( true )) failed = true; if( failed ) { kDebug( 1212 ).nospace() << "Couldn't initialize rendering context (" << KXErrorHandler::errorMessage( errs2.errorEvent()) << ")"; return false; } } if( !tfp_mode && !shm_mode ) { ctxdrawable = glXCreateNewContext( display(), fbcdrawableinfo[ QX11Info::appDepth() ].fbconfig, GLX_RGBA_TYPE, ctxbuffer, direct_rendering ? GL_TRUE : GL_FALSE ); } return true; } // create destination buffer bool SceneOpenGL::initBuffer() { if( !initBufferConfigs()) return false; if( fbcbuffer_db != NULL && wspace->createOverlay()) { // we have overlay, try to create double-buffered window in it fbcbuffer = fbcbuffer_db; XVisualInfo* visual = glXGetVisualFromFBConfig( display(), fbcbuffer ); XSetWindowAttributes attrs; attrs.colormap = XCreateColormap( display(), rootWindow(), visual->visual, AllocNone ); buffer = XCreateWindow( display(), wspace->overlayWindow(), 0, 0, displayWidth(), displayHeight(), 0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs ); if( hasGLXVersion( 1, 3 )) glxbuffer = glXCreateWindow( display(), fbcbuffer, buffer, NULL ); else glxbuffer = buffer; wspace->setupOverlay( buffer ); db = true; XFree( visual ); } else if( fbcbuffer_nondb != NULL ) { // cannot get any double-buffered drawable, will double-buffer using a pixmap fbcbuffer = fbcbuffer_nondb; XVisualInfo* visual = glXGetVisualFromFBConfig( display(), fbcbuffer ); XGCValues gcattr; gcattr.subwindow_mode = IncludeInferiors; gcroot = XCreateGC( display(), rootWindow(), GCSubwindowMode, &gcattr ); buffer = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), visual->depth ); glxbuffer = glXCreatePixmap( display(), fbcbuffer, buffer, NULL ); db = false; XFree( visual ); } else { kError( 1212 ) << "Couldn't create output buffer (failed to create overlay window?) !"; return false; // error } int vis_buffer; glXGetFBConfigAttrib( display(), fbcbuffer, GLX_VISUAL_ID, &vis_buffer ); XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig( display(), fbcbuffer ); kDebug( 1212 ) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number( vis_buffer, 16 ); XFree( visinfo_buffer ); return true; } // choose the best configs for the destination buffer bool SceneOpenGL::initBufferConfigs() { int cnt; GLXFBConfig *fbconfigs = glXGetFBConfigs( display(), DefaultScreen( display() ), &cnt ); fbcbuffer_db = NULL; fbcbuffer_nondb = NULL; for( int i = 0; i < 2; i++ ) { int back, stencil, depth, caveat, alpha; back = i > 0 ? INT_MAX : 1; stencil = INT_MAX; depth = INT_MAX; caveat = INT_MAX; alpha = 0; for( int j = 0; j < cnt; j++ ) { XVisualInfo *vi; int visual_depth; vi = glXGetVisualFromFBConfig( display(), fbconfigs[ j ] ); if( vi == NULL ) continue; visual_depth = vi->depth; XFree( vi ); if( visual_depth != DefaultDepth( display(), DefaultScreen( display()))) continue; int value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_ALPHA_SIZE, &alpha ); glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_BUFFER_SIZE, &value ); if( value != visual_depth && ( value - alpha ) != visual_depth ) continue; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_RENDER_TYPE, &value ); if( !( value & GLX_RGBA_BIT )) continue; int back_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_DOUBLEBUFFER, &back_value ); if( i > 0 ) { if( back_value > back ) continue; } else { if( back_value < back ) continue; } int stencil_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_STENCIL_SIZE, &stencil_value ); if( stencil_value > stencil ) continue; int depth_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_DEPTH_SIZE, &depth_value ); if( depth_value > depth ) continue; int caveat_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_CONFIG_CAVEAT, &caveat_value ); if( caveat_value > caveat ) continue; back = back_value; stencil = stencil_value; depth = depth_value; caveat = caveat_value; if( i > 0 ) fbcbuffer_nondb = fbconfigs[ j ]; else fbcbuffer_db = fbconfigs[ j ]; } } if( cnt ) XFree( fbconfigs ); if( fbcbuffer_db == NULL && fbcbuffer_nondb == NULL ) { kError( 1212 ) << "Couldn't find framebuffer configuration for buffer!"; return false; } for( int i = 0; i <= 32; i++ ) { if( fbcdrawableinfo[ i ].fbconfig == NULL ) continue; int vis_drawable = 0; glXGetFBConfigAttrib( display(), fbcdrawableinfo[ i ].fbconfig, GLX_VISUAL_ID, &vis_drawable ); kDebug( 1212 ) << "Drawable visual (depth " << i << "): 0x" << QString::number( vis_drawable, 16 ); } return true; } // make a list of the best configs for windows by depth bool SceneOpenGL::initDrawableConfigs() { int cnt; GLXFBConfig *fbconfigs = glXGetFBConfigs( display(), DefaultScreen( display() ), &cnt ); for( int i = 0; i <= 32; i++ ) { int back, stencil, depth, caveat, alpha, mipmap, rgba; back = INT_MAX; stencil = INT_MAX; depth = INT_MAX; caveat = INT_MAX; mipmap = 0; rgba = 0; fbcdrawableinfo[ i ].fbconfig = NULL; fbcdrawableinfo[ i ].bind_texture_format = 0; fbcdrawableinfo[ i ].texture_targets = 0; fbcdrawableinfo[ i ].y_inverted = 0; fbcdrawableinfo[ i ].mipmap = 0; for( int j = 0; j < cnt; j++ ) { XVisualInfo *vi; int visual_depth; vi = glXGetVisualFromFBConfig( display(), fbconfigs[ j ] ); if( vi == NULL ) continue; visual_depth = vi->depth; XFree( vi ); if( visual_depth != i ) continue; int value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_ALPHA_SIZE, &alpha ); glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_BUFFER_SIZE, &value ); if( value != i && ( value - alpha ) != i ) continue; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_RENDER_TYPE, &value ); if( !( value & GLX_RGBA_BIT )) continue; if( tfp_mode ) { value = 0; if( i == 32 ) { glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value ); if( value ) { // TODO I think this should be set only after the config passes all tests rgba = 1; fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGBA_EXT; } } if( !value ) { if( rgba ) continue; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_BIND_TO_TEXTURE_RGB_EXT, &value ); if( !value ) continue; fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGB_EXT; } } int back_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_DOUBLEBUFFER, &back_value ); if( back_value > back ) continue; int stencil_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_STENCIL_SIZE, &stencil_value ); if( stencil_value > stencil ) continue; int depth_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_DEPTH_SIZE, &depth_value ); if( depth_value > depth ) continue; int mipmap_value = -1; if( tfp_mode && GLTexture::framebufferObjectSupported()) { glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_BIND_TO_MIPMAP_TEXTURE_EXT, &mipmap_value ); if( mipmap_value < mipmap ) continue; } int caveat_value; glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_CONFIG_CAVEAT, &caveat_value ); if( caveat_value > caveat ) continue; // ok, config passed all tests, it's the best one so far fbcdrawableinfo[ i ].fbconfig = fbconfigs[ j ]; caveat = caveat_value; back = back_value; stencil = stencil_value; depth = depth_value; mipmap = mipmap_value; if ( tfp_mode ) { glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &value ); fbcdrawableinfo[ i ].texture_targets = value; } glXGetFBConfigAttrib( display(), fbconfigs[ j ], GLX_Y_INVERTED_EXT, &value ); fbcdrawableinfo[ i ].y_inverted = value; fbcdrawableinfo[ i ].mipmap = mipmap; } } if( cnt ) XFree( fbconfigs ); if( fbcdrawableinfo[ DefaultDepth( display(), DefaultScreen( display())) ].fbconfig == NULL ) { kError( 1212 ) << "Couldn't find framebuffer configuration for default depth!"; return false; } if( fbcdrawableinfo[ 32 ].fbconfig == NULL ) { kError( 1212 ) << "Couldn't find framebuffer configuration for depth 32 (no ARGB GLX visual)!"; return false; } return true; } // Test if compositing actually _really_ works, by creating a texture from a testing // window, drawing it on the screen, reading the contents back and comparing. This // should test whether compositing really works. // This function does the whole selfcheck, it can be done also in two parts // during actual drawing (to avoid flicker, see selfCheck() call from the ctor). bool SceneOpenGL::selfCheck() { QRegion reg = selfCheckRegion(); if( wspace->overlayWindow()) { // avoid covering the whole screen too soon wspace->setOverlayShape( reg ); wspace->showOverlay(); } selfCheckSetup(); flushBuffer( PAINT_SCREEN_REGION, reg ); bool ok = selfCheckFinish(); if( wspace->overlayWindow()) wspace->hideOverlay(); return ok; } void SceneOpenGL::selfCheckSetup() { KXErrorHandler err; QImage img( selfCheckWidth(), selfCheckHeight(), QImage::Format_RGB32 ); img.setPixel( 0, 0, QColor( Qt::red ).rgb()); img.setPixel( 1, 0, QColor( Qt::green ).rgb()); img.setPixel( 2, 0, QColor( Qt::blue ).rgb()); img.setPixel( 0, 1, QColor( Qt::white ).rgb()); img.setPixel( 1, 1, QColor( Qt::black ).rgb()); img.setPixel( 2, 1, QColor( Qt::white ).rgb()); QPixmap pix = QPixmap::fromImage( img ); foreach( const QPoint& p, selfCheckPoints()) { XSetWindowAttributes wa; wa.override_redirect = True; ::Window window = XCreateWindow( display(), rootWindow(), 0, 0, selfCheckWidth(), selfCheckHeight(), 0, QX11Info::appDepth(), CopyFromParent, CopyFromParent, CWOverrideRedirect, &wa ); XSetWindowBackgroundPixmap( display(), window, pix.handle()); XClearWindow( display(), window ); XMapWindow( display(), window ); // move the window one down to where the result will be rendered too, just in case // the render would fail completely and eventual check would try to read this window's contents XMoveWindow( display(), window, p.x() + 1, p.y()); XCompositeRedirectWindow( display(), window, CompositeRedirectAutomatic ); Pixmap wpix = XCompositeNameWindowPixmap( display(), window ); glXWaitX(); Texture texture; texture.load( wpix, QSize( selfCheckWidth(), selfCheckHeight()), QX11Info::appDepth()); texture.bind(); QRect rect( p.x(), p.y(), selfCheckWidth(), selfCheckHeight()); texture.render( infiniteRegion(), rect ); texture.unbind(); glXWaitGL(); XFreePixmap( display(), wpix ); XDestroyWindow( display(), window ); } err.error( true ); // just sync and discard } bool SceneOpenGL::selfCheckFinish() { glXWaitGL(); KXErrorHandler err; bool ok = true; foreach( const QPoint& p, selfCheckPoints()) { QPixmap pix = QPixmap::grabWindow( rootWindow(), p.x(), p.y(), selfCheckWidth(), selfCheckHeight()); QImage img = pix.toImage(); // kDebug(1212) << "P:" << QColor( img.pixel( 0, 0 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 1, 0 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 2, 0 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 0, 1 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 1, 1 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 2, 1 )).name(); if( img.pixel( 0, 0 ) != QColor( Qt::red ).rgb() || img.pixel( 1, 0 ) != QColor( Qt::green ).rgb() || img.pixel( 2, 0 ) != QColor( Qt::blue ).rgb() || img.pixel( 0, 1 ) != QColor( Qt::white ).rgb() || img.pixel( 1, 1 ) != QColor( Qt::black ).rgb() || img.pixel( 2, 1 ) != QColor( Qt::white ).rgb()) { kError( 1212 ) << "OpenGL compositing self-check failed, disabling compositing."; ok = false; break; } } if( err.error( true )) ok = false; if( ok ) kDebug( 1212 ) << "OpenGL compositing self-check passed."; if( !ok && options->disableCompositingChecks ) { kWarning( 1212 ) << "Compositing checks disabled, proceeding regardless of self-check failure."; return true; } return ok; } // the entry function for painting void SceneOpenGL::paint( QRegion damage, ToplevelList toplevels ) { foreach( Toplevel* c, toplevels ) { assert( windows.contains( c )); stacking_order.append( windows[ c ] ); } grabXServer(); glXWaitX(); glPushMatrix(); int mask = 0; #ifdef CHECK_GL_ERROR checkGLError( "Paint1" ); #endif paintScreen( &mask, &damage ); // call generic implementation #ifdef CHECK_GL_ERROR checkGLError( "Paint2" ); #endif glPopMatrix(); ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync if( wspace->overlayWindow()) // show the window only after the first pass, since wspace->showOverlay(); // that pass may take long // selfcheck is broken (see Bug 253357) #if 0 if( !selfCheckDone ) { selfCheckSetup(); damage |= selfCheckRegion(); } #endif flushBuffer( mask, damage ); #if 0 if( !selfCheckDone ) { if( !selfCheckFinish()) QTimer::singleShot( 0, Workspace::self(), SLOT( finishCompositing())); selfCheckDone = true; } #endif // do cleanup stacking_order.clear(); checkGLError( "PostPaint" ); } // wait for vblank signal before painting void SceneOpenGL::waitSync() { // NOTE that vsync has no effect with indirect rendering if( waitSyncAvailable()) { unsigned int sync; glFlush(); glXGetVideoSync( &sync ); glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync ); } } // actually paint to the screen (double-buffer swap or copy from pixmap buffer) void SceneOpenGL::flushBuffer( int mask, QRegion damage ) { if( db ) { if( mask & PAINT_SCREEN_REGION ) { waitSync(); if( glXCopySubBuffer ) { foreach( const QRect &r, damage.rects()) { // convert to OpenGL coordinates int y = displayHeight() - r.y() - r.height(); glXCopySubBuffer( display(), glxbuffer, r.x(), y, r.width(), r.height()); } } else { // no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt glEnable( GL_SCISSOR_TEST ); glDrawBuffer( GL_FRONT ); int xpos = 0; int ypos = 0; foreach( const QRect &r, damage.rects()) { // convert to OpenGL coordinates int y = displayHeight() - r.y() - r.height(); // Move raster position relatively using glBitmap() rather // than using glRasterPos2f() - the latter causes drawing // artefacts at the bottom screen edge with some gfx cards // glRasterPos2f( r.x(), r.y() + r.height()); glBitmap( 0, 0, 0, 0, r.x() - xpos, y - ypos, NULL ); xpos = r.x(); ypos = y; glScissor( r.x(), y, r.width(), r.height()); glCopyPixels( r.x(), y, r.width(), r.height(), GL_COLOR ); } glBitmap( 0, 0, 0, 0, -xpos, -ypos, NULL ); // move position back to 0,0 glDrawBuffer( GL_BACK ); glDisable( GL_SCISSOR_TEST ); } } else { waitSync(); glXSwapBuffers( display(), glxbuffer ); } glXWaitGL(); XFlush( display()); } else { glFlush(); glXWaitGL(); waitSync(); if( mask & PAINT_SCREEN_REGION ) foreach( const QRect &r, damage.rects()) XCopyArea( display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y()); else XCopyArea( display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0 ); XFlush( display()); } } void SceneOpenGL::paintGenericScreen( int mask, ScreenPaintData data ) { if( mask & PAINT_SCREEN_TRANSFORMED ) { // apply screen transformations glPushMatrix(); glTranslatef( data.xTranslate, data.yTranslate, data.zTranslate ); if( data.rotation ) { // translate to rotation point, rotate, translate back glTranslatef( data.rotation->xRotationPoint, data.rotation->yRotationPoint, data.rotation->zRotationPoint ); float xAxis = 0.0; float yAxis = 0.0; float zAxis = 0.0; switch( data.rotation->axis ) { case RotationData::XAxis: xAxis = 1.0; break; case RotationData::YAxis: yAxis = 1.0; break; case RotationData::ZAxis: zAxis = 1.0; break; } glRotatef( data.rotation->angle, xAxis, yAxis, zAxis ); glTranslatef( -data.rotation->xRotationPoint, -data.rotation->yRotationPoint, -data.rotation->zRotationPoint ); } glScalef( data.xScale, data.yScale, data.zScale ); } Scene::paintGenericScreen( mask, data ); if( mask & PAINT_SCREEN_TRANSFORMED ) glPopMatrix(); } void SceneOpenGL::paintBackground( QRegion region ) { PaintClipper pc( region ); if( !PaintClipper::clip()) { glPushAttrib( GL_COLOR_BUFFER_BIT ); glClearColor( 0, 0, 0, 1 ); // black glClear( GL_COLOR_BUFFER_BIT ); glPopAttrib(); return; } if( pc.clip() && pc.paintArea().isEmpty()) return; // no background to paint glPushAttrib( GL_CURRENT_BIT ); glColor4f( 0, 0, 0, 1 ); // black for( PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) { glBegin( GL_QUADS ); QRect r = iterator.boundingRect(); glVertex2i( r.x(), r.y()); glVertex2i( r.x() + r.width(), r.y()); glVertex2i( r.x() + r.width(), r.y() + r.height()); glVertex2i( r.x(), r.y() + r.height()); glEnd(); } glPopAttrib(); } void SceneOpenGL::windowAdded( Toplevel* c ) { assert( !windows.contains( c )); windows[ c ] = new Window( c ); c->effectWindow()->setSceneWindow( windows[ c ]); } void SceneOpenGL::windowClosed( Toplevel* c, Deleted* deleted ) { assert( windows.contains( c )); if( deleted != NULL ) { // replace c with deleted Window* w = windows.take( c ); w->updateToplevel( deleted ); windows[ deleted ] = w; } else { delete windows.take( c ); c->effectWindow()->setSceneWindow( NULL ); } } void SceneOpenGL::windowDeleted( Deleted* c ) { assert( windows.contains( c )); delete windows.take( c ); c->effectWindow()->setSceneWindow( NULL ); } void SceneOpenGL::windowGeometryShapeChanged( Toplevel* c ) { if( !windows.contains( c )) // this is ok, shape is not valid return; // by default Window* w = windows[ c ]; w->discardShape(); w->checkTextureSize(); } void SceneOpenGL::windowOpacityChanged( Toplevel* ) { #if 0 // not really needed, windows are painted on every repaint // and opacity is used when applying texture, not when // creating it if( !windows.contains( c )) // this is ok, texture is created return; // on demand Window* w = windows[ c ]; w->discardTexture(); #endif } //**************************************** // SceneOpenGL::Texture //**************************************** SceneOpenGL::Texture::Texture() : GLTexture() { init(); } SceneOpenGL::Texture::Texture( const Pixmap& pix, const QSize& size, int depth ) : GLTexture() { init(); load( pix, size, depth ); } SceneOpenGL::Texture::~Texture() { discard(); } void SceneOpenGL::Texture::init() { damaged = true; glxpixmap = None; } void SceneOpenGL::Texture::createTexture() { glGenTextures( 1, &mTexture ); } void SceneOpenGL::Texture::discard() { if( mTexture != None ) release(); GLTexture::discard(); } void SceneOpenGL::Texture::release() { if( tfp_mode && glxpixmap != None ) { glXReleaseTexImageEXT( display(), glxpixmap, GLX_FRONT_LEFT_EXT ); glXDestroyPixmap( display(), glxpixmap ); glxpixmap = None; } } void SceneOpenGL::Texture::findTarget() { unsigned int new_target = 0; if( tfp_mode && glXQueryDrawable && glxpixmap != None ) glXQueryDrawable( display(), glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target ); // Hack for XGL - this should not be a fallback for glXQueryDrawable() but instead the case // when glXQueryDrawable is not available. However this call fails with XGL, unless KWin // is compiled statically with the libGL that Compiz is built against (without which neither // Compiz works with XGL). Falling back to doing this manually makes this work. if( new_target == 0 ) { if( NPOTTextureSupported() || ( isPowerOfTwo( mSize.width()) && isPowerOfTwo( mSize.height()))) new_target = GLX_TEXTURE_2D_EXT; else new_target = GLX_TEXTURE_RECTANGLE_EXT; } switch( new_target ) { case GLX_TEXTURE_2D_EXT: mTarget = GL_TEXTURE_2D; mScale.setWidth( 1.0f / mSize.width()); mScale.setHeight( 1.0f / mSize.height()); break; case GLX_TEXTURE_RECTANGLE_EXT: mTarget = GL_TEXTURE_RECTANGLE_ARB; mScale.setWidth( 1.0f ); mScale.setHeight( 1.0f ); break; default: abort(); } } QRegion SceneOpenGL::Texture::optimizeBindDamage( const QRegion& reg, int limit ) { if( reg.rects().count() <= 1 ) return reg; // try to reduce the number of rects, as especially with SHM mode every rect // causes X roundtrip, even for very small areas - so, when the size difference // between all the areas and the bounding rectangle is small, simply use // only the bounding rectangle int size = 0; foreach( const QRect &r, reg.rects()) size += r.width() * r.height(); if( reg.boundingRect().width() * reg.boundingRect().height() - size < limit ) return reg.boundingRect(); return reg; } bool SceneOpenGL::Texture::load( const Pixmap& pix, const QSize& size, int depth, QRegion region ) { #ifdef CHECK_GL_ERROR checkGLError( "TextureLoad1" ); #endif if( pix == None || size.isEmpty() || depth < 1 ) return false; if( tfp_mode ) { if( fbcdrawableinfo[ depth ].fbconfig == NULL ) { kDebug( 1212 ) << "No framebuffer configuration for depth " << depth << "; not binding pixmap" << endl; return false; } } mSize = size; if( mTexture == None || !region.isEmpty()) { // new texture, or texture contents changed; mipmaps now invalid setDirty(); } #ifdef CHECK_GL_ERROR checkGLError( "TextureLoad2" ); #endif if( tfp_mode ) { // tfp mode, simply bind the pixmap to texture if( mTexture == None ) createTexture(); // The GLX pixmap references the contents of the original pixmap, so it doesn't // need to be recreated when the contents change. // The texture may or may not use the same storage depending on the EXT_tfp // implementation. When options->glStrictBinding is true, the texture uses // a different storage and needs to be updated with a call to // glXBindTexImageEXT() when the contents of the pixmap has changed. if( glxpixmap != None ) glBindTexture( mTarget, mTexture ); else { int attrs[] = { GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format, GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap, None, None, None }; if ( ( fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT ) && ( GLTexture::NPOTTextureSupported() || ( isPowerOfTwo(size.width()) && isPowerOfTwo(size.height()) ))) { attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT; attrs[ 5 ] = GLX_TEXTURE_2D_EXT; } else if ( fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT ) { attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT; attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT; } glxpixmap = glXCreatePixmap( display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs ); #ifdef CHECK_GL_ERROR checkGLError( "TextureLoadTFP1" ); #endif findTarget(); y_inverted = fbcdrawableinfo[ depth ].y_inverted ? true : false; can_use_mipmaps = fbcdrawableinfo[ depth ].mipmap ? true : false; glBindTexture( mTarget, mTexture ); #ifdef CHECK_GL_ERROR checkGLError( "TextureLoadTFP2" ); #endif if( !options->glStrictBinding ) glXBindTexImageEXT( display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL ); } if( options->glStrictBinding ) // Mark the texture as damaged so it will be updated on the next call to bind() damaged = true; } else if( shm_mode ) { // copy pixmap contents to a texture via shared memory #ifdef HAVE_XSHM GLenum pixfmt, type; if( depth >= 24 ) { pixfmt = GL_BGRA; type = GL_UNSIGNED_BYTE; } else { // depth 16 pixfmt = GL_RGB; type = GL_UNSIGNED_SHORT_5_6_5; } findTarget(); #ifdef CHECK_GL_ERROR checkGLError( "TextureLoadSHM1" ); #endif if( mTexture == None ) { createTexture(); glBindTexture( mTarget, mTexture ); y_inverted = false; glTexImage2D( mTarget, 0, depth == 32 ? GL_RGBA : GL_RGB, mSize.width(), mSize.height(), 0, pixfmt, type, NULL ); } else glBindTexture( mTarget, mTexture ); if( !region.isEmpty()) { XGCValues xgcv; xgcv.graphics_exposures = False; xgcv.subwindow_mode = IncludeInferiors; GC gc = XCreateGC( display(), pix, GCGraphicsExposures | GCSubwindowMode, &xgcv ); Pixmap p = XShmCreatePixmap( display(), rootWindow(), shm.shmaddr, &shm, mSize.width(), mSize.height(), depth ); QRegion damage = optimizeBindDamage( region, 100 * 100 ); glPixelStorei( GL_UNPACK_ROW_LENGTH, mSize.width()); foreach( const QRect &r, damage.rects()) { // TODO for small areas it might be faster to not use SHM to avoid the XSync() XCopyArea( display(), pix, p, gc, r.x(), r.y(), r.width(), r.height(), 0, 0 ); glXWaitX(); glTexSubImage2D( mTarget, 0, r.x(), r.y(), r.width(), r.height(), pixfmt, type, shm.shmaddr ); glXWaitGL(); } glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); XFreePixmap( display(), p ); XFreeGC( display(), gc ); } #ifdef CHECK_GL_ERROR checkGLError( "TextureLoadSHM2" ); #endif y_inverted = true; can_use_mipmaps = true; #endif } else { // fallback, copy pixmap contents to a texture // note that if depth is not QX11Info::appDepth(), this may // not work (however, it does seem to work with nvidia) findTarget(); GLXDrawable pixmap = glXCreatePixmap( display(), fbcdrawableinfo[ QX11Info::appDepth() ].fbconfig, pix, NULL ); glXMakeCurrent( display(), pixmap, ctxdrawable ); if( last_pixmap != None ) glXDestroyPixmap( display(), last_pixmap ); // workaround for ATI - it leaks/crashes when the pixmap is destroyed immediately // here (http://lists.kde.org/?l=kwin&m=116353772208535&w=2) last_pixmap = pixmap; glReadBuffer( GL_FRONT ); glDrawBuffer( GL_FRONT ); if( mTexture == None ) { createTexture(); glBindTexture( mTarget, mTexture ); y_inverted = false; glCopyTexImage2D( mTarget, 0, depth == 32 ? GL_RGBA : GL_RGB, 0, 0, mSize.width(), mSize.height(), 0 ); } else { glBindTexture( mTarget, mTexture ); QRegion damage = optimizeBindDamage( region, 30 * 30 ); foreach( const QRect &r, damage.rects()) { // convert to OpenGL coordinates (this is mapping // the pixmap to a texture, this is not affected // by using glOrtho() for the OpenGL scene) int gly = mSize.height() - r.y() - r.height(); glCopyTexSubImage2D( mTarget, 0, r.x(), gly, r.x(), gly, r.width(), r.height()); } } glXWaitGL(); if( db ) glDrawBuffer( GL_BACK ); glXMakeCurrent( display(), glxbuffer, ctxbuffer ); glBindTexture( mTarget, mTexture ); y_inverted = false; can_use_mipmaps = true; } #ifdef CHECK_GL_ERROR checkGLError( "TextureLoad0" ); #endif return true; } bool SceneOpenGL::Texture::load( const Pixmap& pix, const QSize& size, int depth ) { return load( pix, size, depth, QRegion( 0, 0, size.width(), size.height())); } bool SceneOpenGL::Texture::load( const QImage& image, GLenum target ) { if( image.isNull()) return false; return load( QPixmap::fromImage( image ), target ); } bool SceneOpenGL::Texture::load( const QPixmap& pixmap, GLenum target ) { Q_UNUSED( target ); // SceneOpenGL::Texture::findTarget() detects the target if( pixmap.isNull()) return false; return load( pixmap.handle(), pixmap.size(), pixmap.depth()); } void SceneOpenGL::Texture::bind() { glEnable( mTarget ); glBindTexture( mTarget, mTexture ); if( tfp_mode ) { if ( options->glStrictBinding && damaged ) { // Update the texture with the new pixmap contents assert( glxpixmap != None ); glXReleaseTexImageEXT( display(), glxpixmap, GLX_FRONT_LEFT_EXT ); glXBindTexImageEXT( display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL ); setDirty(); // Mipmaps have to be regenerated after updating the texture } damaged = false; } enableFilter(); if( hasGLVersion( 1, 4, 0 )) { // Lod bias makes the trilinear-filtered texture look a bit sharper glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0f ); } } void SceneOpenGL::Texture::unbind() { if( hasGLVersion( 1, 4, 0 )) { glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f ); } GLTexture::unbind(); } //**************************************** // SceneOpenGL::Window //**************************************** SceneOpenGL::Window::Window( Toplevel* c ) : Scene::Window( c ) , texture() , topTexture() , leftTexture() , rightTexture() , bottomTexture() { } SceneOpenGL::Window::~Window() { discardTexture(); } // Bind the window pixmap to an OpenGL texture. bool SceneOpenGL::Window::bindTexture() { if( texture.texture() != None && toplevel->damage().isEmpty()) { // texture doesn't need updating, just bind it glBindTexture( texture.target(), texture.texture()); return true; } // Get the pixmap with the window contents Pixmap pix = toplevel->windowPixmap(); if( pix == None ) return false; bool success = texture.load( pix, toplevel->size(), toplevel->depth(), toplevel->damage()); if( success ) toplevel->resetDamage( QRect( toplevel->clientPos(), toplevel->clientSize() ) ); else kDebug( 1212 ) << "Failed to bind window"; return success; } void SceneOpenGL::Window::discardTexture() { texture.discard(); topTexture.discard(); leftTexture.discard(); rightTexture.discard(); bottomTexture.discard(); } // This call is used in SceneOpenGL::windowGeometryShapeChanged(), // which originally called discardTexture(), however this was causing performance // problems with the launch feedback icon - large number of texture rebinds. // Since the launch feedback icon does not resize, only changes shape, it // is not necessary to rebind the texture (with no strict binding), therefore // discard the texture only if size changes. void SceneOpenGL::Window::checkTextureSize() { if( texture.size() != size()) discardTexture(); } // when the window's composite pixmap is discarded, undo binding it to the texture void SceneOpenGL::Window::pixmapDiscarded() { texture.release(); } // paint the window void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintData data ) { // check if there is something to paint (e.g. don't paint if the window // is only opaque and only PAINT_WINDOW_TRANSLUCENT is requested) /* HACK: It seems this causes painting glitches, disable temporarily bool opaque = isOpaque() && data.opacity == 1.0; if(( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT )) { // We are only painting either opaque OR translucent windows, not both if( mask & PAINT_WINDOW_OPAQUE && !opaque ) return; // Only painting opaque and window is translucent if( mask & PAINT_WINDOW_TRANSLUCENT && opaque ) return; // Only painting translucent and window is opaque }*/ // paint only requested areas if( region != infiniteRegion()) // avoid integer overflow region.translate( -x(), -y()); if( region.isEmpty()) return; if( !bindTexture()) return; glPushMatrix(); // set texture filter if( options->glSmoothScale != 0 ) // default to yes { if( mask & PAINT_WINDOW_TRANSFORMED ) filter = ImageFilterGood; else if( mask & PAINT_SCREEN_TRANSFORMED ) filter = ImageFilterGood; else filter = ImageFilterFast; } else filter = ImageFilterFast; if( filter == ImageFilterGood ) - { - // avoid unneeded mipmap generation by only using trilinear - // filtering when it actually makes a difference, that is with - // minification or changed vertices - if( options->glSmoothScale == 2 - && ( data.quads.smoothNeeded() || data.xScale < 1 || data.yScale < 1 )) - { - texture.setFilter( GL_LINEAR_MIPMAP_LINEAR ); - } - else - texture.setFilter( GL_LINEAR ); - } + texture.setFilter( GL_LINEAR ); else texture.setFilter( GL_NEAREST ); // do required transformations int x = toplevel->x(); int y = toplevel->y(); double z = 0.0; if( mask & PAINT_WINDOW_TRANSFORMED ) { x += data.xTranslate; y += data.yTranslate; z += data.zTranslate; } glTranslatef( x, y, z ); if(( mask & PAINT_WINDOW_TRANSFORMED ) && ( data.xScale != 1 || data.yScale != 1 || data.zScale != 1 )) glScalef( data.xScale, data.yScale, data.zScale ); if(( mask & PAINT_WINDOW_TRANSFORMED ) && data.rotation ) { glTranslatef( data.rotation->xRotationPoint, data.rotation->yRotationPoint, data.rotation->zRotationPoint ); float xAxis = 0.0; float yAxis = 0.0; float zAxis = 0.0; switch( data.rotation->axis ) { case RotationData::XAxis: xAxis = 1.0; break; case RotationData::YAxis: yAxis = 1.0; break; case RotationData::ZAxis: zAxis = 1.0; break; } glRotatef( data.rotation->angle, xAxis, yAxis, zAxis ); glTranslatef( -data.rotation->xRotationPoint, -data.rotation->yRotationPoint, -data.rotation->zRotationPoint ); } region.translate( toplevel->x(), toplevel->y() ); // Back to screen coords WindowQuadList decoration = data.quads.select( WindowQuadDecoration ); // decorations Client *client = dynamic_cast(toplevel); Deleted *deleted = dynamic_cast(toplevel); if( client || deleted ) { bool noBorder = true; bool updateDeco = false; const QPixmap *left = NULL; const QPixmap *top = NULL; const QPixmap *right = NULL; const QPixmap *bottom = NULL; QRect topRect, leftRect, rightRect, bottomRect; if( client && !client->noBorder() ) { noBorder = false; updateDeco = client->decorationPixmapRequiresRepaint(); client->ensureDecorationPixmapsPainted(); client->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect, Client::WindowRelative); left = client->leftDecoPixmap(); top = client->topDecoPixmap(); right = client->rightDecoPixmap(); bottom = client->bottomDecoPixmap(); } if( deleted && !deleted->noBorder() ) { noBorder = false; left = deleted->leftDecoPixmap(); top = deleted->topDecoPixmap(); right = deleted->rightDecoPixmap(); bottom = deleted->bottomDecoPixmap(); deleted->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect); } if( !noBorder ) { WindowQuadList topList, leftList, rightList, bottomList; foreach( const WindowQuad& quad, decoration ) { if( topRect.contains( QPoint( quad.originalLeft(), quad.originalTop() ) ) ) { topList.append( quad ); continue; } if( bottomRect.contains( QPoint( quad.originalLeft(), quad.originalTop() ) ) ) { bottomList.append( quad ); continue; } if( leftRect.contains( QPoint( quad.originalLeft(), quad.originalTop() ) ) ) { leftList.append( quad ); continue; } if( rightRect.contains( QPoint( quad.originalLeft(), quad.originalTop() ) ) ) { rightList.append( quad ); continue; } } paintDecoration( top, DecorationTop, region, topRect, data, topList, updateDeco ); paintDecoration( left, DecorationLeft, region, leftRect, data, leftList, updateDeco ); paintDecoration( right, DecorationRight, region, rightRect, data, rightList, updateDeco ); paintDecoration( bottom, DecorationBottom, region, bottomRect, data, bottomList, updateDeco ); } } texture.bind(); texture.enableUnnormalizedTexCoords(); // paint the content if ( !(mask & PAINT_DECORATION_ONLY) ) { prepareStates( Content, data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader ); renderQuads( mask, region, data.quads.select( WindowQuadContents )); restoreStates( Content, data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader ); } texture.disableUnnormalizedTexCoords(); texture.unbind(); glPopMatrix(); } void SceneOpenGL::Window::paintDecoration( const QPixmap* decoration, TextureType decorationType, const QRegion& region, const QRect& rect, const WindowPaintData& data, const WindowQuadList& quads, bool updateDeco ) { if( quads.isEmpty()) return; SceneOpenGL::Texture* decorationTexture; switch( decorationType ) { case DecorationTop: decorationTexture = &topTexture; break; case DecorationLeft: decorationTexture = &leftTexture; break; case DecorationRight: decorationTexture = &rightTexture; break; case DecorationBottom: decorationTexture = &bottomTexture; break; default: return; } if( decorationTexture->texture() != None && !updateDeco ) { // texture doesn't need updating, just bind it glBindTexture( decorationTexture->target(), decorationTexture->texture()); } else if( !decoration->isNull() ) { bool success = decorationTexture->load( decoration->handle(), decoration->size(), decoration->depth() ); if( !success ) { kDebug( 1212 ) << "Failed to bind decoartion"; return; } } else return; if( filter == ImageFilterGood ) - { - // avoid unneeded mipmap generation by only using trilinear - // filtering when it actually makes a difference, that is with - // minification or changed vertices - if( options->glSmoothScale == 2 - && ( data.quads.smoothNeeded() || data.xScale < 1 || data.yScale < 1 )) - { - decorationTexture->setFilter( GL_LINEAR_MIPMAP_LINEAR ); - } - else - decorationTexture->setFilter( GL_LINEAR ); - } + decorationTexture->setFilter( GL_LINEAR ); else decorationTexture->setFilter( GL_NEAREST ); decorationTexture->setWrapMode( GL_CLAMP_TO_EDGE ); decorationTexture->bind(); decorationTexture->enableUnnormalizedTexCoords(); prepareStates( decorationType, data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader ); float* vertices; float* texcoords; makeDecorationArrays( &vertices, &texcoords, quads, rect ); if( data.shader ) { int texw = decoration->width(); int texh = decoration->height(); if( !GLTexture::NPOTTextureSupported() ) { kWarning( 1212 ) << "NPOT textures not supported, wasting some memory" ; texw = nearestPowerOfTwo(texw); texh = nearestPowerOfTwo(texh); } data.shader->setUniform("textureWidth", (float)texw); data.shader->setUniform("textureHeight", (float)texh); } renderGLGeometry( region, quads.count() * 4, vertices, texcoords, NULL, 2, 0 ); delete[] vertices; delete[] texcoords; restoreStates( decorationType, data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader ); decorationTexture->disableUnnormalizedTexCoords(); decorationTexture->unbind(); } void SceneOpenGL::Window::makeDecorationArrays( float** vertices, float** texcoords, const WindowQuadList& quads, const QRect& rect ) const { *vertices = new float[ quads.count() * 4 * 2 ]; *texcoords = new float[ quads.count() * 4 * 2 ]; float* vpos = *vertices; float* tpos = *texcoords; foreach( const WindowQuad& quad, quads ) { *vpos++ = quad[ 0 ].x(); *vpos++ = quad[ 0 ].y(); *vpos++ = quad[ 1 ].x(); *vpos++ = quad[ 1 ].y(); *vpos++ = quad[ 2 ].x(); *vpos++ = quad[ 2 ].y(); *vpos++ = quad[ 3 ].x(); *vpos++ = quad[ 3 ].y(); *tpos++ = quad.originalLeft()-rect.x(); *tpos++ = quad.originalTop()-rect.y(); *tpos++ = quad.originalRight()-rect.x(); *tpos++ = quad.originalTop()-rect.y(); *tpos++ = quad.originalRight()-rect.x(); *tpos++ = quad.originalBottom()-rect.y(); *tpos++ = quad.originalLeft()-rect.x(); *tpos++ = quad.originalBottom()-rect.y(); } } void SceneOpenGL::Window::renderQuads( int, const QRegion& region, const WindowQuadList& quads ) { if( quads.isEmpty()) return; // Render geometry float* vertices; float* texcoords; quads.makeArrays( &vertices, &texcoords ); renderGLGeometry( region, quads.count() * 4, vertices, texcoords, NULL, 2, 0 ); delete[] vertices; delete[] texcoords; } void SceneOpenGL::Window::prepareStates( TextureType type, double opacity, double brightness, double saturation, GLShader* shader ) { if(shader) prepareShaderRenderStates( type, opacity, brightness, saturation, shader ); else prepareRenderStates( type, opacity, brightness, saturation ); } void SceneOpenGL::Window::prepareShaderRenderStates( TextureType type, double opacity, double brightness, double saturation, GLShader* shader ) { // setup blending of transparent windows glPushAttrib( GL_ENABLE_BIT ); bool opaque = isOpaque() && opacity == 1.0; if( type != Content ) opaque = false; if( !opaque ) { glEnable( GL_BLEND ); glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); } shader->setUniform("opacity", (float)opacity); shader->setUniform("saturation", (float)saturation); shader->setUniform("brightness", (float)brightness); // setting texture width and heiht stored in shader // only set if it is set by an effect that is not negative float texw = shader->textureWidth(); if( texw >= 0.0f ) shader->setUniform("textureWidth", texw); float texh = shader->textureHeight(); if( texh >= 0.0f ) shader->setUniform("textureHeight", texh); } void SceneOpenGL::Window::prepareRenderStates( TextureType type, double opacity, double brightness, double saturation ) { Texture* tex; bool alpha = false; bool opaque = true; switch( type ) { case Content: tex = &texture; alpha = toplevel->hasAlpha(); opaque = isOpaque() && opacity == 1.0; break; case DecorationTop: tex = &topTexture; alpha = true; opaque = false; break; case DecorationLeft: tex = &leftTexture; alpha = true; opaque = false; break; case DecorationRight: tex = &rightTexture; alpha = true; opaque = false; break; case DecorationBottom: tex = &bottomTexture; alpha = true; opaque = false; break; default: return; } // setup blending of transparent windows glPushAttrib( GL_ENABLE_BIT ); if( !opaque ) { glEnable( GL_BLEND ); glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); } if( saturation != 1.0 && tex->saturationSupported()) { // First we need to get the color from [0; 1] range to [0.5; 1] range glActiveTexture( GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5}; glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant ); tex->bind(); // Then we take dot product of the result of previous pass and // saturation_constant. This gives us completely unsaturated // (greyscale) image // Note that both operands have to be in range [0.5; 1] since opengl // automatically substracts 0.5 from them glActiveTexture( GL_TEXTURE1 ); float saturation_constant[] = { 0.5 + 0.5*0.30, 0.5 + 0.5*0.59, 0.5 + 0.5*0.11, saturation }; glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant ); tex->bind(); // Finally we need to interpolate between the original image and the // greyscale image to get wanted level of saturation glActiveTexture( GL_TEXTURE2 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant ); // Also replace alpha by primary color's alpha here glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); // And make primary color contain the wanted opacity glColor4f( opacity, opacity, opacity, opacity ); tex->bind(); if( alpha || brightness != 1.0f ) { glActiveTexture( GL_TEXTURE3 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); // The color has to be multiplied by both opacity and brightness float opacityByBrightness = opacity * brightness; glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity ); if( alpha ) { // Multiply original texture's alpha by our opacity glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA ); } else { // Alpha will be taken from previous stage glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); } tex->bind(); } glActiveTexture(GL_TEXTURE0 ); } else if( opacity != 1.0 || brightness != 1.0 ) { // the window is additionally configured to have its opacity adjusted, // do it float opacityByBrightness = opacity * brightness; if( alpha) { glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity); } else { // Multiply color by brightness and replace alpha by opacity float constant[] = { opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity }; glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant ); } } else if( !alpha && opaque ) { float constant[] = { 1.0, 1.0, 1.0, 1.0 }; glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant ); } } void SceneOpenGL::Window::restoreStates( TextureType type, double opacity, double brightness, double saturation, GLShader* shader ) { if(shader) restoreShaderRenderStates( type, opacity, brightness, saturation, shader ); else restoreRenderStates( type, opacity, brightness, saturation ); } void SceneOpenGL::Window::restoreShaderRenderStates( TextureType type, double opacity, double brightness, double saturation, GLShader* shader ) { Q_UNUSED( type ); Q_UNUSED( opacity ); Q_UNUSED( brightness ); Q_UNUSED( saturation ); Q_UNUSED( shader ); glPopAttrib(); // ENABLE_BIT } void SceneOpenGL::Window::restoreRenderStates( TextureType type, double opacity, double brightness, double saturation ) { Texture* tex; switch( type ) { case Content: tex = &texture; break; case DecorationTop: tex = &topTexture; break; case DecorationLeft: tex = &leftTexture; break; case DecorationRight: tex = &rightTexture; break; case DecorationBottom: tex = &bottomTexture; break; default: return; } if( opacity != 1.0 || saturation != 1.0 || brightness != 1.0f ) { if( saturation != 1.0 && tex->saturationSupported()) { glActiveTexture(GL_TEXTURE3); glDisable( tex->target()); glActiveTexture(GL_TEXTURE2); glDisable( tex->target()); glActiveTexture(GL_TEXTURE1); glDisable( tex->target()); glActiveTexture(GL_TEXTURE0); } } glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glColor4f( 0, 0, 0, 0 ); glPopAttrib(); // ENABLE_BIT } //**************************************** // SceneOpenGL::EffectFrame //**************************************** GLTexture* SceneOpenGL::EffectFrame::m_unstyledTexture = NULL; QPixmap* SceneOpenGL::EffectFrame::m_unstyledPixmap = NULL; SceneOpenGL::EffectFrame::EffectFrame( EffectFrameImpl* frame ) : Scene::EffectFrame( frame ) , m_texture( NULL ) , m_textTexture( NULL ) , m_textPixmap( NULL ) , m_oldTextTexture( NULL ) , m_iconTexture( NULL ) , m_oldIconTexture( NULL ) , m_selectionTexture( NULL ) , m_unstyledVBO( NULL ) { if( m_effectFrame->style() == EffectFrameUnstyled && !m_unstyledTexture ) { updateUnstyledTexture(); } } SceneOpenGL::EffectFrame::~EffectFrame() { delete m_texture; delete m_textTexture; delete m_textPixmap; delete m_oldTextTexture; delete m_iconTexture; delete m_oldIconTexture; delete m_selectionTexture; delete m_unstyledVBO; } void SceneOpenGL::EffectFrame::free() { delete m_texture; m_texture = NULL; delete m_textTexture; m_textTexture = NULL; delete m_textPixmap; m_textPixmap = NULL; delete m_iconTexture; m_iconTexture = NULL; delete m_selectionTexture; m_selectionTexture = NULL; delete m_unstyledVBO; m_unstyledVBO = NULL; delete m_oldIconTexture; m_oldIconTexture = NULL; delete m_oldTextTexture; m_oldTextTexture = NULL; } void SceneOpenGL::EffectFrame::freeIconFrame() { delete m_iconTexture; m_iconTexture = NULL; } void SceneOpenGL::EffectFrame::freeTextFrame() { delete m_textTexture; m_textTexture = NULL; delete m_textPixmap; m_textPixmap = NULL; } void SceneOpenGL::EffectFrame::freeSelection() { delete m_selectionTexture; m_selectionTexture = NULL; } void SceneOpenGL::EffectFrame::crossFadeIcon() { delete m_oldIconTexture; m_oldIconTexture = m_iconTexture; m_iconTexture = NULL; } void SceneOpenGL::EffectFrame::crossFadeText() { delete m_oldTextTexture; m_oldTextTexture = m_textTexture; m_textTexture = NULL; } void SceneOpenGL::EffectFrame::render( QRegion region, double opacity, double frameOpacity ) { if( m_effectFrame->geometry().isEmpty() ) return; // Nothing to display region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL GLShader* shader = m_effectFrame->shader(); if( shader ) { shader->bind(); shader->setUniform("saturation", 1.0f); shader->setUniform("brightness", 1.0f); shader->setUniform("textureWidth", 1.0f); shader->setUniform("textureHeight", 1.0f); } glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); if( !shader ) glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glPushMatrix(); // Render the actual frame if( m_effectFrame->style() == EffectFrameUnstyled ) { if( !m_unstyledVBO ) { m_unstyledVBO = new GLVertexBuffer( GLVertexBuffer::Static ); const QRect& area = m_effectFrame->geometry().adjusted( -5, -5, 5, 5 ); const int roundness = 5; QVector verts, texCoords; verts.reserve( 84 ); texCoords.reserve( 84 ); // top left verts << area.left() << area.top(); texCoords << 0.0f << 0.0f; verts << area.left() << area.top() + roundness; texCoords << 0.0f << 0.5f; verts << area.left() + roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.left() + roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.left() << area.top() + roundness; texCoords << 0.0f << 0.5f; verts << area.left() + roundness << area.top(); texCoords << 0.5f << 0.0f; // top verts << area.left() + roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.left() + roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.left() + roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.top(); texCoords << 0.5f << 0.0f; // top right verts << area.right() - roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.right() - roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() << area.top(); texCoords << 1.0f << 0.0f; verts << area.right() - roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() << area.top() + roundness; texCoords << 1.0f << 0.5f; verts << area.right() << area.top(); texCoords << 1.0f << 0.0f; // bottom left verts << area.left() << area.bottom() - roundness; texCoords << 0.0f << 0.5f; verts << area.left() << area.bottom(); texCoords << 0.0f << 1.0f; verts << area.left() + roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.left() + roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.left() << area.bottom(); texCoords << 0.0f << 1.0f; verts << area.left() + roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; // bottom verts << area.left() + roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.left() + roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() - roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.left() + roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() - roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() - roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; // bottom right verts << area.right() - roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() << area.bottom() - roundness; texCoords << 1.0f << 0.5f; verts << area.right() - roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() << area.bottom(); texCoords << 1.0f << 1.0f; verts << area.right() << area.bottom() - roundness; texCoords << 1.0f << 0.5f; // center verts << area.left() << area.top() + roundness; texCoords << 0.0f << 0.5f; verts << area.left() << area.bottom() - roundness; texCoords << 0.0f << 0.5f; verts << area.right() << area.top() + roundness; texCoords << 1.0f << 0.5f; verts << area.left() << area.bottom() - roundness; texCoords << 0.0f << 0.5f; verts << area.right() << area.bottom() - roundness; texCoords << 1.0f << 0.5f; verts << area.right() << area.top() + roundness; texCoords << 1.0f << 0.5f; m_unstyledVBO->setData( verts.count() / 2, 2, verts.data(), texCoords.data() ); } if( shader ) shader->setUniform( "opacity", (float)(opacity * frameOpacity) ); else glColor4f( 0.0, 0.0, 0.0, opacity * frameOpacity ); m_unstyledTexture->bind(); m_unstyledVBO->render( region, GL_TRIANGLES ); m_unstyledTexture->unbind(); } else if( m_effectFrame->style() == EffectFrameStyled ) { if( !m_texture ) // Lazy creation updateTexture(); if( shader ) shader->setUniform( "opacity", (float)(opacity * frameOpacity) ); else glColor4f( 1.0, 1.0, 1.0, opacity * frameOpacity ); m_texture->bind(); qreal left, top, right, bottom; m_effectFrame->frame().getMargins( left, top, right, bottom ); // m_geometry is the inner geometry m_texture->render( region, m_effectFrame->geometry().adjusted( -left, -top, right, bottom )); m_texture->unbind(); if( !m_effectFrame->selection().isNull() ) { if( !m_selectionTexture ) // Lazy creation { QPixmap pixmap = m_effectFrame->selectionFrame().framePixmap(); m_selectionTexture = new Texture( pixmap.handle(), pixmap.size(), pixmap.depth() ); } glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); m_selectionTexture->bind(); m_selectionTexture->render( region, m_effectFrame->selection() ); m_selectionTexture->unbind(); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } } // Render icon if( !m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty() ) { QPoint topLeft( m_effectFrame->geometry().x(), m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2 ); if( m_effectFrame->isCrossFade() && m_oldIconTexture ) { if( shader ) shader->setUniform( "opacity", (float)opacity * (1.0f - (float)m_effectFrame->crossFadeProgress()) ); else glColor4f( 1.0, 1.0, 1.0, opacity * (1.0 - m_effectFrame->crossFadeProgress()) ); m_oldIconTexture->bind(); m_oldIconTexture->render( region, QRect( topLeft, m_effectFrame->iconSize() )); m_oldIconTexture->unbind(); if( shader ) shader->setUniform( "opacity", (float)opacity * (float)m_effectFrame->crossFadeProgress() ); else glColor4f( 1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress() ); } else { if( shader ) shader->setUniform( "opacity", (float)opacity ); else glColor4f( 1.0, 1.0, 1.0, opacity ); } if( !m_iconTexture ) // lazy creation { m_iconTexture = new Texture( m_effectFrame->icon().handle(), m_effectFrame->icon().size(), m_effectFrame->icon().depth() ); } m_iconTexture->bind(); m_iconTexture->render( region, QRect( topLeft, m_effectFrame->iconSize() )); m_iconTexture->unbind(); } // Render text if( !m_effectFrame->text().isEmpty() ) { if( m_effectFrame->isCrossFade() && m_oldTextTexture ) { if( shader ) shader->setUniform( "opacity", (float)opacity * (1.0f - (float)m_effectFrame->crossFadeProgress()) ); else glColor4f( 1.0, 1.0, 1.0, opacity * (1.0 - m_effectFrame->crossFadeProgress()) ); m_oldTextTexture->bind(); m_oldTextTexture->render( region, m_effectFrame->geometry() ); m_oldTextTexture->unbind(); if( shader ) shader->setUniform( "opacity", (float)opacity * (float)m_effectFrame->crossFadeProgress() ); else glColor4f( 1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress() ); } else { if( shader ) shader->setUniform( "opacity", (float)opacity ); else glColor4f( 1.0, 1.0, 1.0, opacity ); } if( !m_textTexture ) // Lazy creation updateTextTexture(); m_textTexture->bind(); m_textTexture->render( region, m_effectFrame->geometry() ); m_textTexture->unbind(); } if( shader ) shader->unbind(); glPopMatrix(); glPopAttrib(); } void SceneOpenGL::EffectFrame::updateTexture() { delete m_texture; if( m_effectFrame->style() == EffectFrameStyled ) { QPixmap pixmap = m_effectFrame->frame().framePixmap(); m_texture = new Texture( pixmap.handle(), pixmap.size(), pixmap.depth() ); } } void SceneOpenGL::EffectFrame::updateTextTexture() { delete m_textTexture; delete m_textPixmap; if( m_effectFrame->text().isEmpty() ) return; // Determine position on texture to paint text QRect rect( QPoint( 0, 0 ), m_effectFrame->geometry().size() ); if( !m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty() ) rect.setLeft( m_effectFrame->iconSize().width() ); // If static size elide text as required QString text = m_effectFrame->text(); if( m_effectFrame->isStatic() ) { QFontMetrics metrics( m_effectFrame->font() ); text = metrics.elidedText( text, Qt::ElideRight, rect.width() ); } m_textPixmap = new QPixmap( m_effectFrame->geometry().size() ); m_textPixmap->fill( Qt::transparent ); QPainter p( m_textPixmap ); p.setFont( m_effectFrame->font() ); if( m_effectFrame->style() == EffectFrameStyled ) p.setPen( m_effectFrame->styledTextColor() ); else // TODO: What about no frame? Custom color setting required p.setPen( Qt::white ); p.drawText( rect, m_effectFrame->alignment(), text ); p.end(); m_textTexture = new Texture( m_textPixmap->handle(), m_textPixmap->size(), m_textPixmap->depth() ); } void SceneOpenGL::EffectFrame::updateUnstyledTexture() { delete m_unstyledTexture; delete m_unstyledPixmap; // Based off circle() from kwinxrenderutils.cpp #define CS 8 m_unstyledPixmap = new QPixmap( 2 * CS, 2 * CS ); m_unstyledPixmap->fill( Qt::transparent ); QPainter p( m_unstyledPixmap ); p.setRenderHint( QPainter::Antialiasing ); p.setPen( Qt::NoPen ); p.setBrush( Qt::black ); p.drawEllipse( m_unstyledPixmap->rect() ); p.end(); #undef CS m_unstyledTexture = new Texture( m_unstyledPixmap->handle(), m_unstyledPixmap->size(), m_unstyledPixmap->depth() ); } void SceneOpenGL::EffectFrame::cleanup() { delete m_unstyledTexture; m_unstyledTexture = NULL; delete m_unstyledPixmap; m_unstyledPixmap = NULL; } } // namespace #endif