PageRenderTime 38ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/kde-workspace-4.8.97/kwin/effects/logout/logout.cpp

#
C++ | 451 lines | 345 code | 44 blank | 62 comment | 89 complexity | a802d8650ec0ee8c134669f108d66528 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, LGPL-3.0
  1. /********************************************************************
  2. KWin - the KDE window manager
  3. This file is part of the KDE project.
  4. Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
  5. Copyright (C) 2009 Martin Grдяlin <kde@martin-graesslin.com>
  6. Copyright (C) 2009, 2010 Lucas Murray <lmurray@undefinedfire.com>
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *********************************************************************/
  18. #include "logout.h"
  19. #include "kwinglutils.h"
  20. #include <math.h>
  21. #include <kconfiggroup.h>
  22. #include <kdebug.h>
  23. #include <KDE/KStandardDirs>
  24. #include <QtGui/QMatrix4x4>
  25. #include <QtGui/QVector2D>
  26. namespace KWin
  27. {
  28. KWIN_EFFECT(logout, LogoutEffect)
  29. LogoutEffect::LogoutEffect()
  30. : progress(0.0)
  31. , displayEffect(false)
  32. , logoutWindow(NULL)
  33. , logoutWindowClosed(true)
  34. , logoutWindowPassed(false)
  35. , canDoPersistent(false)
  36. , ignoredWindows()
  37. , m_vignettingShader(NULL)
  38. , m_blurShader(NULL)
  39. {
  40. // Persistent effect
  41. logoutAtom = XInternAtom(display(), "_KDE_LOGGING_OUT", False);
  42. effects->registerPropertyType(logoutAtom, true);
  43. // Block KSMServer's effect
  44. char net_wm_cm_name[ 100 ];
  45. sprintf(net_wm_cm_name, "_NET_WM_CM_S%d", DefaultScreen(display()));
  46. Atom net_wm_cm = XInternAtom(display(), net_wm_cm_name, False);
  47. Window sel = XGetSelectionOwner(display(), net_wm_cm);
  48. Atom hack = XInternAtom(display(), "_KWIN_LOGOUT_EFFECT", False);
  49. XChangeProperty(display(), sel, hack, hack, 8, PropModeReplace, (unsigned char*)&hack, 1);
  50. // the atom is not removed when effect is destroyed, this is temporary anyway
  51. blurTexture = NULL;
  52. blurTarget = NULL;
  53. reconfigure(ReconfigureAll);
  54. connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*)));
  55. connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*)));
  56. connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*)));
  57. connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long)));
  58. }
  59. LogoutEffect::~LogoutEffect()
  60. {
  61. delete blurTexture;
  62. delete blurTarget;
  63. delete m_vignettingShader;
  64. delete m_blurShader;
  65. }
  66. void LogoutEffect::reconfigure(ReconfigureFlags)
  67. {
  68. frameDelay = 0;
  69. KConfigGroup conf = effects->effectConfig("Logout");
  70. useBlur = conf.readEntry("UseBlur", true);
  71. delete blurTexture;
  72. blurTexture = NULL;
  73. delete blurTarget;
  74. blurTarget = NULL;
  75. blurSupported = false;
  76. delete m_blurShader;
  77. m_blurShader = NULL;
  78. }
  79. void LogoutEffect::prePaintScreen(ScreenPrePaintData& data, int time)
  80. {
  81. if (!displayEffect && progress == 0.0) {
  82. if (blurTexture) {
  83. delete blurTexture;
  84. blurTexture = NULL;
  85. delete blurTarget;
  86. blurTarget = NULL;
  87. blurSupported = false;
  88. }
  89. } else if (!blurTexture) {
  90. blurSupported = false;
  91. delete blurTarget; // catch as we just tested the texture ;-P
  92. if (effects->compositingType() == OpenGLCompositing && GLTexture::NPOTTextureSupported() && GLRenderTarget::blitSupported() && useBlur) {
  93. // TODO: It seems that it is not possible to create a GLRenderTarget that has
  94. // a different size than the display right now. Most likely a KWin core bug.
  95. // Create texture and render target
  96. blurTexture = new GLTexture(displayWidth(), displayHeight());
  97. blurTexture->setFilter(GL_LINEAR_MIPMAP_LINEAR);
  98. blurTexture->setWrapMode(GL_CLAMP_TO_EDGE);
  99. blurTarget = new GLRenderTarget(*blurTexture);
  100. if (blurTarget->valid())
  101. blurSupported = true;
  102. // As creating the render target takes time it can cause the first two frames of the
  103. // blur animation to be jerky. For this reason we only start the animation after the
  104. // third frame.
  105. frameDelay = 2;
  106. }
  107. }
  108. if (frameDelay)
  109. --frameDelay;
  110. else {
  111. if (displayEffect)
  112. progress = qMin(1.0, progress + time / animationTime(2000.0));
  113. else if (progress > 0.0)
  114. progress = qMax(0.0, progress - time / animationTime(500.0));
  115. }
  116. if (blurSupported && progress > 0.0) {
  117. data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
  118. }
  119. effects->prePaintScreen(data, time);
  120. }
  121. void LogoutEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
  122. {
  123. if (progress > 0.0) {
  124. if (effects->compositingType() == KWin::OpenGLCompositing) {
  125. // In OpenGL mode we add vignetting and, if supported, a slight blur
  126. if (blurSupported) {
  127. // When using blur we render everything to an FBO and as such don't do the vignetting
  128. // until after we render the FBO to the screen.
  129. if (w == logoutWindow) {
  130. // Window is rendered after the FBO
  131. windowOpacity = data.opacity;
  132. data.opacity = 0.0; // Cheat, we need the opacity for later but don't want to blur it
  133. } else {
  134. if (logoutWindowPassed || ignoredWindows.contains(w)) {
  135. // Window is rendered after the FBO
  136. windows.append(w);
  137. windowsOpacities[ w ] = data.opacity;
  138. data.opacity = 0.0;
  139. } else // Window is added to the FBO
  140. data.saturation *= (1.0 - progress * 0.2);
  141. }
  142. } else {
  143. // If we are not blurring then we are not rendering to an FBO
  144. if (w == logoutWindow)
  145. // This is the logout window don't alter it but render our vignetting now
  146. renderVignetting();
  147. else if (!logoutWindowPassed && !ignoredWindows.contains(w))
  148. // Window is in the background, desaturate
  149. data.saturation *= (1.0 - progress * 0.2);
  150. // All other windows are unaltered
  151. }
  152. }
  153. if (effects->compositingType() == KWin::XRenderCompositing) {
  154. // Since we can't do vignetting in XRender just do a stronger desaturation and darken
  155. if (w != logoutWindow && !logoutWindowPassed && !ignoredWindows.contains(w)) {
  156. data.saturation *= (1.0 - progress * 0.8);
  157. data.brightness *= (1.0 - progress * 0.3);
  158. }
  159. }
  160. if (w == logoutWindow ||
  161. ignoredWindows.contains(w)) // HACK: All windows past the first ignored one should not be
  162. // blurred as it affects the stacking order.
  163. // All following windows are on top of the logout window and should not be altered either
  164. logoutWindowPassed = true;
  165. }
  166. effects->paintWindow(w, mask, region, data);
  167. }
  168. void LogoutEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
  169. {
  170. effects->paintScreen(mask, region, data);
  171. if (effects->compositingType() == KWin::OpenGLCompositing && progress > 0.0) {
  172. if (!blurSupported) {
  173. if (!logoutWindowPassed)
  174. // The logout window has been deleted but we still want to fade out the vignetting, thus
  175. // render it on the top of everything if still animating. We don't check if logoutWindow
  176. // is set as it may still be even if it wasn't rendered.
  177. renderVignetting();
  178. } else {
  179. GLRenderTarget::pushRenderTarget(blurTarget);
  180. blurTarget->blitFromFramebuffer();
  181. GLRenderTarget::popRenderTarget();
  182. //--------------------------
  183. // Render the screen effect
  184. renderBlurTexture();
  185. // Vignetting (Radial gradient with transparent middle and black edges)
  186. renderVignetting();
  187. //--------------------------
  188. // Render the logout window
  189. if (logoutWindow) {
  190. int winMask = logoutWindow->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE;
  191. WindowPaintData winData(logoutWindow);
  192. winData.opacity = windowOpacity;
  193. effects->drawWindow(logoutWindow, winMask, region, winData);
  194. }
  195. // Render all windows on top of logout window
  196. foreach (EffectWindow * w, windows) {
  197. int winMask = w->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE;
  198. WindowPaintData winData(w);
  199. winData.opacity = windowsOpacities[ w ];
  200. effects->drawWindow(w, winMask, region, winData);
  201. }
  202. windows.clear();
  203. windowsOpacities.clear();
  204. }
  205. }
  206. }
  207. void LogoutEffect::postPaintScreen()
  208. {
  209. if ((progress != 0.0 && progress != 1.0) || frameDelay)
  210. effects->addRepaintFull();
  211. if (progress > 0.0)
  212. logoutWindowPassed = false;
  213. effects->postPaintScreen();
  214. }
  215. void LogoutEffect::slotWindowAdded(EffectWindow* w)
  216. {
  217. if (isLogoutDialog(w)) {
  218. logoutWindow = w;
  219. logoutWindowClosed = false; // So we don't blur the window on close
  220. progress = 0.0;
  221. displayEffect = true;
  222. ignoredWindows.clear();
  223. effects->addRepaintFull();
  224. } else if (canDoPersistent)
  225. // TODO: Add parent
  226. ignoredWindows.append(w);
  227. }
  228. void LogoutEffect::slotWindowClosed(EffectWindow* w)
  229. {
  230. if (w == logoutWindow) {
  231. logoutWindowClosed = true;
  232. if (!canDoPersistent)
  233. displayEffect = false; // Fade back to normal
  234. effects->addRepaintFull();
  235. }
  236. }
  237. void LogoutEffect::slotWindowDeleted(EffectWindow* w)
  238. {
  239. windows.removeAll(w);
  240. ignoredWindows.removeAll(w);
  241. if (w == logoutWindow)
  242. logoutWindow = NULL;
  243. }
  244. bool LogoutEffect::isLogoutDialog(EffectWindow* w)
  245. {
  246. // TODO there should be probably a better way (window type?)
  247. if (w->windowClass() == "ksmserver ksmserver"
  248. && (w->windowRole() == "logoutdialog" || w->windowRole() == "logouteffect")) {
  249. return true;
  250. }
  251. return false;
  252. }
  253. void LogoutEffect::renderVignetting()
  254. {
  255. if (!ShaderManager::instance()->isValid()) {
  256. renderVignettingLegacy();
  257. return;
  258. }
  259. if (!m_vignettingShader) {
  260. m_vignettingShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::ColorShader,
  261. KGlobal::dirs()->findResource("data", "kwin/vignetting.frag"));
  262. if (!m_vignettingShader->isValid()) {
  263. kDebug(1212) << "Vignetting Shader failed to load";
  264. return;
  265. }
  266. } else if (!m_vignettingShader->isValid()) {
  267. // shader broken
  268. return;
  269. }
  270. // need to get the projection matrix from the ortho shader for the vignetting shader
  271. QMatrix4x4 projection = ShaderManager::instance()->pushShader(KWin::ShaderManager::SimpleShader)->getUniformMatrix4x4("projection");
  272. ShaderManager::instance()->popShader();
  273. ShaderManager::instance()->pushShader(m_vignettingShader);
  274. m_vignettingShader->setUniform(KWin::GLShader::ProjectionMatrix, projection);
  275. m_vignettingShader->setUniform("u_progress", (float)progress * 0.9f);
  276. glEnable(GL_BLEND);
  277. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  278. glEnable(GL_SCISSOR_TEST);
  279. const QRect fullArea = effects->clientArea(FullArea, 0, 0);
  280. for (int screen = 0; screen < effects->numScreens(); screen++) {
  281. const QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
  282. glScissor(screenGeom.x(), displayHeight() - screenGeom.y() - screenGeom.height(),
  283. screenGeom.width(), screenGeom.height()); // GL coords are flipped
  284. const float cenX = screenGeom.x() + screenGeom.width() / 2;
  285. const float cenY = fullArea.height() - screenGeom.y() - screenGeom.height() / 2;
  286. const float r = float((screenGeom.width() > screenGeom.height())
  287. ? screenGeom.width() : screenGeom.height()) * 0.8f; // Radius
  288. m_vignettingShader->setUniform("u_center", QVector2D(cenX, cenY));
  289. m_vignettingShader->setUniform("u_radius", r);
  290. QVector<float> vertices;
  291. vertices << screenGeom.x() << screenGeom.y();
  292. vertices << screenGeom.x() << screenGeom.y() + screenGeom.height();
  293. vertices << screenGeom.x() + screenGeom.width() << screenGeom.y();
  294. vertices << screenGeom.x() + screenGeom.width() << screenGeom.y() + screenGeom.height();
  295. GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
  296. vbo->setData(vertices.count()/2, 2, vertices.constData(), NULL);
  297. vbo->render(GL_TRIANGLE_STRIP);
  298. }
  299. glDisable(GL_SCISSOR_TEST);
  300. glDisable(GL_BLEND);
  301. ShaderManager::instance()->popShader();
  302. }
  303. void LogoutEffect::renderVignettingLegacy()
  304. {
  305. #ifndef KWIN_HAVE_OPENGLES
  306. glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT);
  307. glEnable(GL_BLEND); // If not already (Such as when rendered straight to the screen)
  308. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  309. for (int screen = 0; screen < effects->numScreens(); screen++) {
  310. QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
  311. glScissor(screenGeom.x(), displayHeight() - screenGeom.y() - screenGeom.height(),
  312. screenGeom.width(), screenGeom.height()); // GL coords are flipped
  313. glEnable(GL_SCISSOR_TEST); // Geom must be set before enable
  314. const float cenX = screenGeom.x() + screenGeom.width() / 2;
  315. const float cenY = screenGeom.y() + screenGeom.height() / 2;
  316. const float a = M_PI / 16.0f; // Angle of increment
  317. const float r = float((screenGeom.width() > screenGeom.height())
  318. ? screenGeom.width() : screenGeom.height()) * 0.8f; // Radius
  319. glBegin(GL_TRIANGLE_FAN);
  320. glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
  321. glVertex3f(cenX, cenY, 0.0f);
  322. glColor4f(0.0f, 0.0f, 0.0f, progress * 0.9f);
  323. for (float i = 0.0f; i <= M_PI * 2.01f; i += a)
  324. glVertex3f(cenX + r * cos(i), cenY + r * sin(i), 0.0f);
  325. glEnd();
  326. glDisable(GL_SCISSOR_TEST);
  327. }
  328. glPopAttrib();
  329. #endif
  330. }
  331. void LogoutEffect::renderBlurTexture()
  332. {
  333. if (!ShaderManager::instance()->isValid()) {
  334. renderBlurTextureLegacy();
  335. return;
  336. }
  337. if (!m_blurShader) {
  338. m_blurShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::SimpleShader,
  339. KGlobal::dirs()->findResource("data", "kwin/logout-blur.frag"));
  340. if (!m_blurShader->isValid()) {
  341. kDebug(1212) << "Logout blur shader failed to load";
  342. }
  343. } else if (!m_blurShader->isValid()) {
  344. // shader is broken - no need to continue here
  345. return;
  346. }
  347. // Unmodified base image
  348. ShaderManager::instance()->pushShader(m_blurShader);
  349. m_blurShader->setUniform(GLShader::Offset, QVector2D(0, 0));
  350. m_blurShader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0));
  351. m_blurShader->setUniform(GLShader::Saturation, 1.0);
  352. m_blurShader->setUniform(GLShader::AlphaToOne, 1);
  353. m_blurShader->setUniform("u_alphaProgress", (float)progress * 0.4f);
  354. glEnable(GL_BLEND);
  355. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  356. blurTexture->bind();
  357. blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight()));
  358. blurTexture->unbind();
  359. glDisable(GL_BLEND);
  360. ShaderManager::instance()->popShader();
  361. checkGLError("Render blur texture");
  362. }
  363. void LogoutEffect::renderBlurTextureLegacy()
  364. {
  365. #ifndef KWIN_HAVE_OPENGLES
  366. glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT);
  367. // Unmodified base image
  368. blurTexture->bind();
  369. blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight()));
  370. // Blurred image
  371. GLfloat bias[1];
  372. glGetTexEnvfv(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias);
  373. glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 1.75);
  374. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  375. glEnable(GL_BLEND);
  376. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  377. glColor4f(1.0f, 1.0f, 1.0f, progress * 0.4);
  378. blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight()));
  379. glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias[0]);
  380. blurTexture->unbind();
  381. glPopAttrib();
  382. #endif
  383. }
  384. void LogoutEffect::slotPropertyNotify(EffectWindow* w, long a)
  385. {
  386. if (w || a != logoutAtom)
  387. return; // Not our atom
  388. QByteArray byteData = effects->readRootProperty(logoutAtom, logoutAtom, 8);
  389. if (byteData.length() < 1) {
  390. // Property was deleted
  391. displayEffect = false;
  392. return;
  393. }
  394. // We are using a compatible KSMServer therefore only terminate the effect when the
  395. // atom is deleted, not when the dialog is closed.
  396. canDoPersistent = true;
  397. effects->addRepaintFull();
  398. }
  399. bool LogoutEffect::isActive() const
  400. {
  401. return progress != 0 || logoutWindow;
  402. }
  403. } // namespace