diff --git a/effects.cpp b/effects.cpp index 6c252c9e65..b80be3d509 100644 --- a/effects.cpp +++ b/effects.cpp @@ -532,6 +532,13 @@ bool EffectsHandlerImpl::hasKeyboardGrab() const return keyboard_grab_effect != NULL; } +void EffectsHandlerImpl::desktopResized(const QSize &size) +{ + scene->screenGeometryChanged(size); + emit screenGeometryChanged(size); + Workspace::self()->addRepaintFull(); +} + void EffectsHandlerImpl::slotPropertyNotify(Toplevel* t, long int atom) { if (!registered_atoms.contains(atom)) @@ -1243,6 +1250,21 @@ bool EffectsHandlerImpl::isEffectLoaded(const QString& name) return false; } +void EffectsHandlerImpl::reloadEffect(Effect *effect) +{ + QString effectName; + for (QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it) { + if ((*it).second == effect) { + effectName = (*it).first; + break; + } + } + if (!effectName.isNull()) { + unloadEffect(effectName); + loadEffect(effectName); + } +} + void EffectsHandlerImpl::effectsChanged() { loaded_effects.clear(); diff --git a/effects.h b/effects.h index f4ad2fbd85..4d49316856 100644 --- a/effects.h +++ b/effects.h @@ -160,7 +160,9 @@ public: bool borderActivated(ElectricBorder border); void grabbedKeyboardEvent(QKeyEvent* e); bool hasKeyboardGrab() const; + void desktopResized(const QSize &size); + virtual void reloadEffect(Effect *effect); bool loadEffect(const QString& name, bool checkDefault = false); void toggleEffect(const QString& name); void unloadEffect(const QString& name); diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp index a25355b01b..06c685450d 100644 --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -63,6 +63,7 @@ BlurEffect::BlurEffect() connect(effects, SIGNAL(windowAdded(EffectWindow*)), this, SLOT(slotWindowAdded(EffectWindow*))); connect(effects, SIGNAL(windowDeleted(EffectWindow*)), this, SLOT(slotWindowDeleted(EffectWindow*))); connect(effects, SIGNAL(propertyNotify(EffectWindow*,long)), this, SLOT(slotPropertyNotify(EffectWindow*,long))); + connect(effects, SIGNAL(screenGeometryChanged(QSize)), this, SLOT(slotScreenGeometryChanged())); } BlurEffect::~BlurEffect() @@ -76,6 +77,11 @@ BlurEffect::~BlurEffect() delete target; } +void BlurEffect::slotScreenGeometryChanged() +{ + effects->reloadEffect(this); +} + void BlurEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) diff --git a/effects/blur/blur.h b/effects/blur/blur.h index ea5f01daba..65ba3811ca 100644 --- a/effects/blur/blur.h +++ b/effects/blur/blur.h @@ -52,6 +52,7 @@ public Q_SLOTS: void slotWindowAdded(EffectWindow *w); void slotWindowDeleted(EffectWindow *w); void slotPropertyNotify(EffectWindow *w, long atom); + void slotScreenGeometryChanged(); private: QRect expand(const QRect &rect) const; diff --git a/geometry.cpp b/geometry.cpp index a414e263d1..bfc304bb2e 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -76,8 +76,9 @@ void Workspace::desktopResized() #ifdef KWIN_BUILD_SCREENEDGES m_screenEdge.update(true); #endif - if (compositing()) - compositeResetTimer.start(0); + if (effects) { + static_cast(effects)->desktopResized(geom.size()); + } } void Workspace::saveOldScreenSizes() diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 9f239c845a..b7d8184a22 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -795,6 +795,16 @@ public: virtual EffectFrame* effectFrame(EffectFrameStyle style, bool staticSize = true, const QPoint& position = QPoint(-1, -1), Qt::Alignment alignment = Qt::AlignCenter) const = 0; + /** + * Allows an effect to trigger a reload of itself. + * This can be used by an effect which needs to be reloaded when screen geometry changes. + * It is possible that the effect cannot be loaded again as it's supported method does no longer + * hold. + * @param effect The effect to reload + * @since 4.8 + **/ + virtual void reloadEffect(Effect *effect) = 0; + /** * Sends message over DCOP to reload given effect. * @param effectname effect's name without "kwin4_effect_" prefix. @@ -1033,6 +1043,15 @@ Q_SIGNALS: **/ void hideOutline(); + /** + * Signal emitted after the screen geometry changed (e.g. add of a monitor). + * Effects using displayWidth()/displayHeight() to cache information should + * react on this signal and update the caches. + * @param size The new screen size + * @since 4.8 + **/ + void screenGeometryChanged(const QSize &size); + protected: QVector< EffectPair > loaded_effects; QHash< QString, KLibrary* > effect_libraries; diff --git a/libkwineffects/kwinglutils.cpp b/libkwineffects/kwinglutils.cpp index 2a3740ee59..4102d3f1b4 100644 --- a/libkwineffects/kwinglutils.cpp +++ b/libkwineffects/kwinglutils.cpp @@ -689,6 +689,20 @@ GLShader *ShaderManager::pushShader(ShaderType type, bool reset) return shader; } +void ShaderManager::resetAllShaders() +{ + if (!m_inited || !m_valid) { + return; + } + pushShader(SimpleShader, true); + pushShader(GenericShader, true); + pushShader(ColorShader, true); + popShader(); + popShader(); + popShader(); +} + + void ShaderManager::pushShader(GLShader *shader) { // only bind shader if it is not already bound diff --git a/libkwineffects/kwinglutils.h b/libkwineffects/kwinglutils.h index 876cf3ad7f..c39f2dc361 100644 --- a/libkwineffects/kwinglutils.h +++ b/libkwineffects/kwinglutils.h @@ -321,6 +321,13 @@ public: **/ void popShader(); + /** + * Resets all shaders to the default uniform values. + * Only built in shaders are changed. + * @since 4.8 + **/ + void resetAllShaders(); + /** * Creates a GLShader with a built-in vertex shader and a custom fragment shader. * @param vertex The generic vertex shader diff --git a/overlaywindow.cpp b/overlaywindow.cpp index 43f1a52a1f..528dade5df 100644 --- a/overlaywindow.cpp +++ b/overlaywindow.cpp @@ -120,6 +120,13 @@ void OverlayWindow::setShape(const QRegion& reg) m_shape = reg; } +void OverlayWindow::resize(const QSize &size) +{ + assert(m_window != None); + XResizeWindow(display(), m_window, size.width(), size.height()); + setShape(QRegion(0, 0, size.width(), size.height())); +} + bool OverlayWindow::isVisible() const { return m_visible; diff --git a/overlaywindow.h b/overlaywindow.h index 14d2d58334..d2d999523b 100644 --- a/overlaywindow.h +++ b/overlaywindow.h @@ -37,6 +37,7 @@ public: void show(); void hide(); // hides and resets overlay window void setShape(const QRegion& reg); + void resize(const QSize &size); /// Destroys XComposite overlay window void destroy(); Window window() const; diff --git a/scene.cpp b/scene.cpp index 4589aa56b7..ef1058dfee 100644 --- a/scene.cpp +++ b/scene.cpp @@ -426,6 +426,11 @@ OverlayWindow* Scene::overlayWindow() return m_overlayWindow; } +void Scene::screenGeometryChanged(const QSize &size) +{ + m_overlayWindow->resize(size); +} + //**************************************** // Scene::Window //**************************************** diff --git a/scene.h b/scene.h index 17be282b5a..c227352522 100644 --- a/scene.h +++ b/scene.h @@ -60,6 +60,13 @@ public: virtual void windowAdded(Toplevel*) = 0; // a window has been destroyed virtual void windowDeleted(Deleted*) = 0; + /** + * Method invoked when the screen geometry is changed. + * Reimplementing classes should also invoke the parent method + * as it takes care of resizing the overlay window. + * @param size The new screen geometry size + **/ + virtual void screenGeometryChanged(const QSize &size); // Flags controlling how painting is done. enum { // Window (or at least part of it) will be painted opaque. diff --git a/scene_opengl.h b/scene_opengl.h index 9c0b057f96..5bc9d5e936 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -53,6 +53,7 @@ public: virtual void paint(QRegion damage, ToplevelList windows); virtual void windowAdded(Toplevel*); virtual void windowDeleted(Deleted*); + virtual void screenGeometryChanged(const QSize &size); protected: virtual void paintGenericScreen(int mask, ScreenPaintData data); @@ -70,6 +71,9 @@ private: bool initBufferConfigs(); bool initDrawableConfigs(); void waitSync(); +#ifndef KWIN_HAVE_OPENGLES + void setupModelViewProjectionMatrix(); +#endif void flushBuffer(int mask, QRegion damage); GC gcroot; class FBConfigInfo @@ -86,6 +90,7 @@ private: #ifndef KWIN_HAVE_OPENGLES Drawable buffer; GLXFBConfig fbcbuffer; + bool m_resetModelViewProjectionMatrix; #endif static bool db; #ifndef KWIN_HAVE_OPENGLES diff --git a/scene_opengl_egl.cpp b/scene_opengl_egl.cpp index 7a198b0f6f..90841338f5 100644 --- a/scene_opengl_egl.cpp +++ b/scene_opengl_egl.cpp @@ -218,6 +218,13 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage) XFlush(display()); } +void SceneOpenGL::screenGeometryChanged(const QSize &size) +{ + glViewport(0,0, size.width(), size.height()); + Scene::screenGeometryChanged(size); + ShaderManager::instance()->resetAllShaders(); +} + //**************************************** // SceneOpenGL::Texture //**************************************** diff --git a/scene_opengl_glx.cpp b/scene_opengl_glx.cpp index 35f04494de..d978605bb9 100644 --- a/scene_opengl_glx.cpp +++ b/scene_opengl_glx.cpp @@ -38,6 +38,7 @@ GLXDrawable SceneOpenGL::last_pixmap = None; SceneOpenGL::SceneOpenGL(Workspace* ws) : Scene(ws) , init_ok(false) + , m_resetModelViewProjectionMatrix(true) { initGLX(); // check for FBConfig support @@ -100,23 +101,7 @@ SceneOpenGL::SceneOpenGL(Workspace* ws) } // 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); + setupModelViewProjectionMatrix(); if (checkGLError("Init")) { kError(1212) << "OpenGL compositing setup failed"; return; // error @@ -152,6 +137,28 @@ SceneOpenGL::~SceneOpenGL() checkGLError("Cleanup"); } +void SceneOpenGL::setupModelViewProjectionMatrix() +{ + 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); + m_resetModelViewProjectionMatrix = false; +} + bool SceneOpenGL::initTfp() { if (glXBindTexImageEXT == NULL || glXReleaseTexImageEXT == NULL) @@ -451,6 +458,10 @@ void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels) grabXServer(); glXWaitX(); + if (m_resetModelViewProjectionMatrix) { + // reset model view projection matrix if required + setupModelViewProjectionMatrix(); + } glPushMatrix(); int mask = 0; #ifdef CHECK_GL_ERROR @@ -558,6 +569,33 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage) } } +void SceneOpenGL::screenGeometryChanged(const QSize &size) +{ + Scene::screenGeometryChanged(size); + glViewport(0,0, size.width(), size.height()); + if (m_overlayWindow->window() == None) { + glXMakeCurrent(display(), None, NULL); + glXDestroyPixmap(display(), glxbuffer); + XFreePixmap(display(), buffer); + XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbcbuffer); + buffer = XCreatePixmap(display(), rootWindow(), size.width(), size.height(), visual->depth); + XFree(visual); + glxbuffer = glXCreatePixmap(display(), fbcbuffer, buffer, NULL); + glXMakeCurrent(display(), glxbuffer, ctxbuffer); + // TODO: there seems some bug, some clients become black until an eg. un/remap - could be a general pixmap buffer issue, though + } + else { + glXMakeCurrent(display(), None, NULL); // deactivate context //// + XMoveResizeWindow(display(), buffer, 0,0, size.width(), size.height()); + m_overlayWindow->setup(buffer); + XSync(display(), false); // ensure X11 stuff has applied //// + glXMakeCurrent(display(), glxbuffer, ctxbuffer); // reactivate context //// + glViewport(0,0, size.width(), size.height()); // adjust viewport last - should btw. be superflous on the Pixmap buffer - iirc glXCreatePixmap sets the context anyway. //// + } + ShaderManager::instance()->resetAllShaders(); + m_resetModelViewProjectionMatrix = true; +} + //**************************************** // SceneOpenGL::Texture //**************************************** diff --git a/scene_xrender.cpp b/scene_xrender.cpp index a2f2f2a504..dea424a6e2 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -99,8 +99,32 @@ SceneXrender::SceneXrender(Workspace* ws) kError(1212) << "No XFixes v3+ extension available"; return; } + initXRender(true); +} + +SceneXrender::~SceneXrender() +{ + if (!init_ok) { + // TODO this probably needs to clean up whatever has been created until the failure + m_overlayWindow->destroy(); + return; + } + XRenderFreePicture(display(), front); + XRenderFreePicture(display(), buffer); + buffer = None; + m_overlayWindow->destroy(); + foreach (Window * w, windows) + delete w; +} + +void SceneXrender::initXRender(bool createOverlay) +{ + init_ok = false; + if (front != None) + XRenderFreePicture(display(), front); KXErrorHandler xerr; - if (m_overlayWindow->create()) { + bool haveOverlay = createOverlay ? m_overlayWindow->create() : (m_overlayWindow->window() != None); + if (haveOverlay) { m_overlayWindow->setup(None); XWindowAttributes attrs; XGetWindowAttributes(display(), m_overlayWindow->window(), &attrs); @@ -129,21 +153,6 @@ SceneXrender::SceneXrender(Workspace* ws) init_ok = true; } -SceneXrender::~SceneXrender() -{ - if (!init_ok) { - // TODO this probably needs to clean up whatever has been created until the failure - m_overlayWindow->destroy(); - return; - } - XRenderFreePicture(display(), front); - XRenderFreePicture(display(), buffer); - buffer = None; - m_overlayWindow->destroy(); - foreach (Window * w, windows) - delete w; -} - bool SceneXrender::initFailed() const { return !init_ok; @@ -153,6 +162,8 @@ bool SceneXrender::initFailed() const // so it is done manually using this buffer, void SceneXrender::createBuffer() { + if (buffer != None) + XRenderFreePicture(display(), buffer); Pixmap pixmap = XCreatePixmap(display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth(display(), DefaultScreen(display()))); buffer = XRenderCreatePicture(display(), pixmap, format, 0, 0); XFreePixmap(display(), pixmap); // The picture owns the pixmap now @@ -774,6 +785,12 @@ XRenderComposite(display(), PictOpOver, _PART_->x11PictureHandle(), decorationAl } } +void SceneXrender::screenGeometryChanged(const QSize &size) +{ + Scene::screenGeometryChanged(size); + initXRender(false); +} + //**************************************** // SceneXrender::EffectFrame //**************************************** diff --git a/scene_xrender.h b/scene_xrender.h index 23ff2addbd..c181f0dca9 100644 --- a/scene_xrender.h +++ b/scene_xrender.h @@ -49,6 +49,7 @@ public: virtual void paint(QRegion damage, ToplevelList windows); virtual void windowAdded(Toplevel*); virtual void windowDeleted(Deleted*); + virtual void screenGeometryChanged(const QSize &size); Picture bufferPicture(); protected: virtual void paintBackground(QRegion region); @@ -61,6 +62,7 @@ private: void paintTransformedScreen(int mask); void createBuffer(); void flushBuffer(int mask, QRegion damage); + void initXRender(bool createOverlay); XRenderPictFormat* format; Picture front; static Picture buffer;