summaryrefslogtreecommitdiffstats
path: root/kwin
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-09-07 19:42:15 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-09-07 19:42:15 +0000
commit9cc1e2c1aa2629d499e7555acd4906d6cc989cf9 (patch)
tree2a737f47bb029688c2f5ebef76869903cf4b4eec /kwin
parenteaa51fec106463800192924168eaf9f03f188655 (diff)
downloadtdebase-9cc1e2c1aa2629d499e7555acd4906d6cc989cf9.tar.gz
tdebase-9cc1e2c1aa2629d499e7555acd4906d6cc989cf9.zip
Merged in remaining kdebase bugfixes from the Chakra project
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1172677 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kwin')
-rw-r--r--kwin/KWinInterface.h3
-rw-r--r--kwin/activation.cpp49
-rw-r--r--kwin/client.cpp637
-rw-r--r--kwin/client.h52
-rw-r--r--kwin/events.cpp211
-rw-r--r--kwin/geometry.cpp15
-rw-r--r--kwin/kcmkwin/kwindecoration/kwindecoration.cpp227
-rw-r--r--kwin/kcmkwin/kwindecoration/kwindecoration.h13
-rw-r--r--kwin/layers.cpp59
-rw-r--r--kwin/lib/kdecoration.h15
-rw-r--r--kwin/manage.cpp1
-rw-r--r--kwin/options.cpp84
-rw-r--r--kwin/options.h54
-rw-r--r--kwin/sm.cpp2
-rw-r--r--kwin/sm.h1
-rw-r--r--kwin/useractions.cpp8
-rw-r--r--kwin/workspace.cpp19
-rw-r--r--kwin/workspace.h13
18 files changed, 1452 insertions, 11 deletions
diff --git a/kwin/KWinInterface.h b/kwin/KWinInterface.h
index eff3a88f2..2af0d03ff 100644
--- a/kwin/KWinInterface.h
+++ b/kwin/KWinInterface.h
@@ -23,6 +23,9 @@ class KWinInterface : virtual public DCOPObject
virtual void nextDesktop() = 0;
virtual void previousDesktop() = 0;
virtual void circulateDesktopApplications() = 0;
+ virtual void updateOverlappingShadows(unsigned long window) = 0;
+ virtual void setShadowed(unsigned long window, bool shadowed) = 0;
+
// kompmgr stuff
virtual void startKompmgr() = 0;
virtual void stopKompmgr() = 0;
diff --git a/kwin/activation.cpp b/kwin/activation.cpp
index f80a00f9a..c0f578634 100644
--- a/kwin/activation.cpp
+++ b/kwin/activation.cpp
@@ -227,6 +227,13 @@ void Workspace::setActiveClient( Client* c, allowed_t )
active_client->setActive( false, !c || !c->isModal() || c != active_client->transientFor() );
}
active_client = c;
+ if (set_active_client_recursion == 1)
+ {
+ // Only unset next_active_client if activateClient() wasn't called by
+ // Client::setActive() to set the active window to null before
+ // activating another window.
+ next_active_client = NULL;
+ }
Q_ASSERT( c == NULL || c->isActive());
if( active_client != NULL )
last_active_client = active_client;
@@ -324,6 +331,7 @@ void Workspace::takeActivity( Client* c, int flags, bool handled )
Client* modal = c->findModal();
if( modal != NULL && modal != c )
{
+ next_active_client = modal;
if( !modal->isOnDesktop( c->desktop()))
{
modal->setDesktop( c->desktop());
@@ -351,11 +359,14 @@ void Workspace::takeActivity( Client* c, int flags, bool handled )
c->setActive( true );
focusToNull();
}
+ if( c->wantsInput())
+ next_active_client = c;
flags &= ~ActivityFocus;
handled = false; // no point, can't get clicks
}
if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed
{
+ next_active_client = c;
kdWarning( 1212 ) << "takeActivity: not shown" << endl;
return;
}
@@ -856,7 +867,45 @@ void Client::setActive( bool act, bool updateOpacity_)
updateShadowSize();
if ( active )
+ {
Notify::raise( Notify::Activate );
+ if (options->shadowEnabled(true))
+ {
+ if (options->shadowEnabled(false))
+ {
+ // Wait for inactive shadow to expose occluded windows and give
+ // them a chance to redraw before painting the active shadow
+ removeShadow();
+ drawDelayedShadow();
+ if (!isDesktop() &&
+ this != workspace()->topClientOnDesktop(desktop()))
+ // If the newly activated window's isn't the desktop, wait
+ // for its shadow to draw, then redraw any shadows
+ // overlapping it.
+ drawOverlappingShadows(true);
+ }
+ else
+ drawShadow();
+ }
+ }
+ else
+ {
+ removeShadow();
+
+ if (options->shadowEnabled(false))
+ if (this == workspace()->topClientOnDesktop(desktop()))
+ {
+ /* If the newly deactivated window is the top client on the
+ * desktop, then the newly activated window is below it; ensure
+ * that the deactivated window's shadow draws after the
+ * activated window's shadow.
+ */
+ if ((shadowAfterClient = workspace()->activeClient()))
+ drawShadowAfter(shadowAfterClient);
+ }
+ else
+ drawDelayedShadow();
+ }
if( !active )
cancelAutoRaise();
diff --git a/kwin/client.cpp b/kwin/client.cpp
index 5f281f2ab..0d2c5cfbf 100644
--- a/kwin/client.cpp
+++ b/kwin/client.cpp
@@ -11,9 +11,12 @@ License. See the file "COPYING" for the exact licensing terms.
#include "client.h"
+#include <math.h>
+
#include <tqapplication.h>
#include <tqpainter.h>
#include <tqdatetime.h>
+#include <tqimage.h>
#include <kprocess.h>
#include <unistd.h>
#include <kstandarddirs.h>
@@ -39,9 +42,25 @@ extern Time qt_x_time;
extern Atom qt_window_role;
extern Atom qt_sm_client_id;
+// wait 200 ms before drawing shadow after move/resize
+static const int SHADOW_DELAY = 200;
+
namespace KWinInternal
{
+/* TODO: Remove this once X has real translucency.
+ *
+ * A list of the regions covered by all shadows and the Clients to which they
+ * belong. Used to redraw shadows when a window overlapping or underlying a
+ * shadow is moved, resized, or hidden.
+ */
+struct ShadowRegion
+ {
+ TQRegion region;
+ Client *client;
+ };
+static TQValueList<ShadowRegion> shadowRegions;
+
/*
Creating a client:
@@ -100,6 +119,13 @@ Client::Client( Workspace *ws )
autoRaiseTimer = 0;
shadeHoverTimer = 0;
+ shadowDelayTimer = new TQTimer(this);
+ opacityCache = &activeOpacityCache;
+ shadowAfterClient = NULL;
+ shadowWidget = NULL;
+ shadowMe = true;
+ connect(shadowDelayTimer, TQT_SIGNAL(timeout()), TQT_SLOT(drawShadow()));
+
// set the initial mapping state
mapping_state = WithdrawnState;
desk = 0; // no desktop yet
@@ -145,7 +171,7 @@ Client::Client( Workspace *ws )
maxmode_restore = MaximizeRestore;
cmap = None;
-
+
frame_geometry = TQRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
client_size = TQSize( 100, 100 );
custom_opacity = false;
@@ -188,6 +214,8 @@ void Client::releaseWindow( bool on_shutdown )
if (!custom_opacity) setOpacity(FALSE);
if (moveResizeMode)
leaveMoveResize();
+ removeShadow();
+ drawIntersectingShadows();
finishWindowRules();
++postpone_geometry_updates;
// grab X during the release to make removing of properties, setting to withdrawn state
@@ -248,6 +276,8 @@ void Client::destroyClient()
StackingUpdatesBlocker blocker( workspace());
if (moveResizeMode)
leaveMoveResize();
+ removeShadow();
+ drawIntersectingShadows();
finishWindowRules();
++postpone_geometry_updates;
setModal( false );
@@ -304,6 +334,7 @@ void Client::updateDecoration( bool check_workspace_pos, bool force )
if( do_show )
decoration->widget()->show();
updateFrameExtents();
+ updateOpacityCache();
}
void Client::destroyDecoration()
@@ -434,6 +465,12 @@ void Client::resizeDecoration( const TQSize& s )
TQResizeEvent e( s, oldsize );
TQApplication::sendEvent( decoration->widget(), &e );
}
+ if (!moveResizeMode && options->shadowEnabled(isActive()))
+ {
+ // If the user is manually resizing, let Client::leaveMoveResize()
+ // decide when to redraw the shadow
+ updateOpacityCache();
+ }
}
bool Client::noBorder() const
@@ -471,6 +508,7 @@ void Client::updateShape()
noborder = true;
updateDecoration( true );
}
+ updateOpacityCache();
if ( shape() )
{
XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding,
@@ -862,6 +900,16 @@ void Client::setShade( ShadeMode mode )
XMapWindow( qt_xdisplay(), wrapperId());
XMapWindow( qt_xdisplay(), window());
XDeleteProperty (qt_xdisplay(), client, atoms->net_wm_window_shade);
+ if (options->shadowEnabled(false))
+ {
+ for (ClientList::ConstIterator it = transients().begin();
+ it != transients().end(); ++it)
+ {
+ (*it)->removeShadow();
+ (*it)->drawDelayedShadow();
+ }
+ }
+
if ( isActive() )
workspace()->requestFocus( this );
}
@@ -946,6 +994,589 @@ void Client::updateVisibility()
}
}
+void Client::setShadowed(bool shadowed)
+{
+ bool wasShadowed;
+
+ wasShadowed = isShadowed();
+ shadowMe = options->shadowEnabled(isActive()) ? shadowed : false;
+
+ if (shadowMe) {
+ if (!wasShadowed)
+ drawShadow();
+ }
+ else {
+ if (wasShadowed) {
+ removeShadow();
+
+ if (!activeOpacityCache.isNull())
+ activeOpacityCache.resize(0);
+ if (!inactiveOpacityCache.isNull())
+ inactiveOpacityCache.resize(0);
+ }
+ }
+}
+
+void Client::updateOpacityCache()
+{
+ if (!activeOpacityCache.isNull())
+ activeOpacityCache.resize(0);
+ if (!inactiveOpacityCache.isNull())
+ inactiveOpacityCache.resize(0);
+
+ if (!moveResizeMode) {
+ // If the user is manually resizing, let Client::finishMoveResize()
+ // decide when to redraw the shadow
+ removeShadow();
+ drawIntersectingShadows();
+ if (options->shadowEnabled(isActive()))
+ drawDelayedShadow();
+ }
+}
+
+/*!
+ Redraw shadows that were previously occluding or occluded by this window,
+ to avoid visual glitches.
+ */
+void Client::drawIntersectingShadows() {
+ //Client *reshadowClient;
+ TQRegion region;
+ //TQPtrList<Client> reshadowClients;
+ TQValueList<Client *> reshadowClients;
+ TQValueListIterator<ShadowRegion> it;
+ TQValueListIterator<Client *> it2;
+
+ if (!options->shadowEnabled(false))
+ // No point in redrawing overlapping/overlapped shadows if only the
+ // active window has a shadow.
+ return;
+
+ region = shapeBoundingRegion;
+
+ // Generate list of Clients whose shadows need to be redrawn. That is,
+ // those that are currently intersecting or intersected by other windows or
+ // shadows.
+ for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it)
+ if ((isOnAllDesktops() || (*it).client->isOnCurrentDesktop()) &&
+ !(*it).region.intersect(region).isEmpty())
+ reshadowClients.append((*it).client);
+
+ // Redraw shadows for each of the Clients in the list generated above
+ for (it2 = reshadowClients.begin(); it2 != reshadowClients.end();
+ ++it2) {
+ (*it2)->removeShadow();
+ (*it2)->drawDelayedShadow();
+ }
+}
+
+/*!
+ Redraw shadows that are above the current window in the stacking order.
+ Furthermore, redraw them in the same order as they come in the stacking order
+ from bottom to top.
+ */
+void Client::drawOverlappingShadows(bool waitForMe)
+{
+ Client *aClient;
+ TQRegion region;
+ TQValueList<Client *> reshadowClients;
+ ClientList stacking_order;
+ ClientList::ConstIterator it;
+ TQValueListIterator<ShadowRegion> it2;
+ TQValueListIterator<Client *> it3;
+
+ if (!options->shadowEnabled(false))
+ // No point in redrawing overlapping/overlapped shadows if only the
+ // active window has a shadow.
+ return;
+
+ region = shapeBoundingRegion;
+
+ stacking_order = workspace()->stackingOrder();
+ for (it = stacking_order.fromLast(); it != stacking_order.end(); --it) {
+ // Find the position of this window in the stacking order.
+ if ((*it) == this)
+ break;
+ }
+ ++it;
+ while (it != stacking_order.end()) {
+ if ((*it)->windowType() == NET::Dock) {
+ // This function is only interested in windows whose shadows don't
+ // have weird stacking rules.
+ ++it;
+ continue;
+ }
+
+ // Generate list of Clients whose shadows need to be redrawn. That is,
+ // those that are currently overlapping or overlapped by other windows
+ // or shadows. The list should be in order from bottom to top in the
+ // stacking order.
+ for (it2 = shadowRegions.begin(); it2 != shadowRegions.end(); ++it2) {
+ if ((*it2).client == (*it)) {
+ if ((isOnAllDesktops() || (*it2).client->isOnCurrentDesktop())
+ && !(*it2).region.intersect(region).isEmpty())
+ reshadowClients.append((*it2).client);
+ }
+ }
+ ++it;
+ }
+
+ // Redraw shadows for each of the Clients in the list generated above
+ for (it3 = reshadowClients.begin(); it3 != reshadowClients.end(); ++it3) {
+ (*it3)->removeShadow();
+ if (it3 == reshadowClients.begin()) {
+ if (waitForMe)
+ (*it3)->drawShadowAfter(this);
+ else
+ (*it3)->drawDelayedShadow();
+ }
+ else {
+ --it3;
+ aClient = (*it3);
+ ++it3;
+ (*it3)->drawShadowAfter(aClient);
+ }
+ }
+}
+
+/*!
+ Draw shadow after some time has elapsed, to give recently exposed windows a
+ chance to repaint before a shadow gradient is drawn over them.
+ */
+void Client::drawDelayedShadow()
+{
+ shadowDelayTimer->stop();
+ shadowDelayTimer->start(SHADOW_DELAY, true);
+}
+
+/*!
+ Draw shadow immediately after the specified Client's shadow finishes drawing.
+ */
+void Client::drawShadowAfter(Client *after)
+{
+ shadowAfterClient = after;
+ connect(after, TQT_SIGNAL(shadowDrawn()), TQT_SLOT(drawShadow()));
+}
+
+/*!
+ Draw a shadow under this window and XShape the shadow accordingly.
+ */
+void Client::drawShadow()
+{
+ Window shadows[2];
+ XRectangle *shapes;
+ int i, count, ordering;
+
+ // If we are waiting for another Client's shadow to be drawn, stop waiting now
+ if (shadowAfterClient != NULL) {
+ disconnect(shadowAfterClient, TQT_SIGNAL(shadowDrawn()), this, TQT_SLOT(drawShadow()));
+ shadowAfterClient = NULL;
+ }
+
+ if (!isOnCurrentDesktop())
+ return;
+
+ /* Store this window's ShapeBoundingRegion even if shadows aren't drawn for
+ * this type of window. Otherwise, drawIntersectingShadows() won't update
+ * properly when this window is moved/resized/hidden/closed.
+ */
+ shapes = XShapeGetRectangles(qt_xdisplay(), frameId(), ShapeBounding,
+ &count, &ordering);
+ if (!shapes)
+ // XShape extension not supported
+ shapeBoundingRegion = TQRegion(x(), y(), width(), height());
+ else {
+ shapeBoundingRegion = TQRegion();
+ for (i = 0; i < count; i++) {
+ // Translate XShaped window into a TQRegion
+ TQRegion shapeRectangle(shapes[i].x, shapes[i].y, shapes[i].width,
+ shapes[i].height);
+ shapeBoundingRegion += shapeRectangle;
+ }
+ if (isShade())
+ // Since XResize() doesn't change a window's XShape regions, ensure that
+ // shapeBoundingRegion is not taller than the window's shaded height,
+ // or the bottom shadow will appear to be missing
+ shapeBoundingRegion &= TQRegion(0, 0, width(), height());
+ shapeBoundingRegion.translate(x(), y());
+ }
+
+ if (!isShadowed() || hidden || isMinimized() ||
+ maximizeMode() == MaximizeFull ||
+ !options->shadowWindowType(windowType())) {
+ XFree(shapes);
+
+ // Tell whatever Clients are listening that this Client's shadow has been drawn.
+ // It hasn't, but there's no sense waiting for something that won't happen.
+ emit shadowDrawn();
+
+ return;
+ }
+
+ removeShadow();
+
+ TQMemArray<QRgb> pixelData;
+ TQPixmap shadowPixmap;
+ TQRect shadow;
+ TQRegion exposedRegion;
+ ShadowRegion shadowRegion;
+ int thickness, xOffset, yOffset;
+
+ thickness = options->shadowThickness(isActive());
+ xOffset = options->shadowXOffset(isActive());
+ yOffset = options->shadowYOffset(isActive());
+ opacityCache = active? &activeOpacityCache : &inactiveOpacityCache;
+
+ shadow.setRect(x() - thickness + xOffset, y() - thickness + yOffset,
+ width() + thickness * 2, height() + thickness * 2);
+ shadowPixmap.resize(shadow.size());
+
+ // Create a fake drop-down shadow effect via blended Xwindows
+ shadowWidget = new TQWidget(0, 0, WStyle_Customize | WX11BypassWM);
+ shadowWidget->setGeometry(shadow);
+ XSelectInput(qt_xdisplay(), shadowWidget->winId(),
+ ButtonPressMask | ButtonReleaseMask | StructureNotifyMask);
+ shadowWidget->installEventFilter(this);
+
+ if (!shapes) {
+ // XShape extension not supported
+ exposedRegion = getExposedRegion(shapeBoundingRegion, shadow.x(),
+ shadow.y(), shadow.width(), shadow.height(), thickness,
+ xOffset, yOffset);
+ shadowRegion.region = exposedRegion;
+ shadowRegion.client = this;
+ shadowRegions.append(shadowRegion);
+
+ if (opacityCache->isNull())
+ imposeRegionShadow(shadowPixmap, shapeBoundingRegion,
+ exposedRegion, thickness,
+ options->shadowOpacity(isActive()));
+ else
+ imposeCachedShadow(shadowPixmap, exposedRegion);
+ }
+ else {
+ TQMemArray<TQRect> exposedRects;
+ TQMemArray<TQRect>::Iterator it, itEnd;
+ XRectangle *shadowShapes;
+
+ exposedRegion = getExposedRegion(shapeBoundingRegion, shadow.x(),
+ shadow.y(), shadow.width(), shadow.height(), thickness,
+ xOffset, yOffset);
+ shadowRegion.region = exposedRegion;
+ shadowRegion.client = this;
+ shadowRegions.append(shadowRegion);
+
+ // XShape the shadow
+ exposedRects = exposedRegion.rects();
+ i = 0;
+ itEnd = exposedRects.end();
+ shadowShapes = new XRectangle[exposedRects.count()];
+ for (it = exposedRects.begin(); it != itEnd; ++it) {
+ shadowShapes[i].x = (*it).x();
+ shadowShapes[i].y = (*it).y();
+ shadowShapes[i].width = (*it).width();
+ shadowShapes[i].height = (*it).height();
+ i++;
+ }
+ XShapeCombineRectangles(qt_xdisplay(), shadowWidget->winId(),
+ ShapeBounding, -x() + thickness - xOffset,
+ -y() + thickness - yOffset, shadowShapes, i, ShapeSet,
+ Unsorted);
+ delete [] shadowShapes;
+
+ if (opacityCache->isNull())
+ imposeRegionShadow(shadowPixmap, shapeBoundingRegion,
+ exposedRegion, thickness,
+ options->shadowOpacity(isActive()));
+ else
+ imposeCachedShadow(shadowPixmap, exposedRegion);
+ }
+
+ XFree(shapes);
+
+ // Set the background pixmap
+ //shadowPixmap.convertFromImage(shadowImage);
+ shadowWidget->setErasePixmap(shadowPixmap);
+
+ // Restack shadows under this window so that shadows drawn for a newly
+ // focused (but not raised) window don't overlap any windows above it.
+ if (isDock()) {
+ ClientList stacking_order = workspace()->stackingOrder();
+ for (ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
+ if ((*it)->isDesktop())
+ {
+ ++it;
+ shadows[0] = (*it)->frameId();
+ shadows[1] = shadowWidget->winId();
+ }
+ }
+ else {
+ shadows[0] = frameId();
+ if (shadowWidget != NULL)
+ shadows[1] = shadowWidget->winId();
+ }
+
+ XRestackWindows(qt_xdisplay(), shadows, 2);
+
+ // Don't use TQWidget::show() so we don't confuse QEffects, thus causing
+ // broken focus.
+ XMapWindow(qt_xdisplay(), shadowWidget->winId());
+
+ // Tell whatever Clients are listening that this Client's shadow has been drawn.
+ emit shadowDrawn();
+}
+
+/*!
+ Remove shadow under this window.
+ */
+void Client::removeShadow()
+{
+ TQValueList<ShadowRegion>::Iterator it;
+
+ shadowDelayTimer->stop();
+
+ if (shadowWidget != NULL) {
+ for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it)
+ if ((*it).client == this) {
+ shadowRegions.remove(it);
+ break;
+ }
+ delete shadowWidget;
+ shadowWidget = NULL;
+ }
+}
+
+/*!
+ Calculate regions in which the shadow will be visible given the window's
+ origin, height and width and the shadow's thickness, and X- and Y-offsets.
+ */
+TQRegion Client::getExposedRegion(TQRegion occludedRegion, int x, int y, int w,
+ int h, int thickness, int xOffset, int yOffset)
+{
+ TQRegion exposedRegion;
+
+ exposedRegion = TQRegion(x, y, w, h);
+ exposedRegion -= occludedRegion;
+
+ if (thickness > 0) {
+ // Limit exposedRegion to include only where a shadow of the specified
+ // thickness will be drawn
+ TQMemArray<TQRect> occludedRects;
+ TQMemArray<TQRect>::Iterator it, itEnd;
+ TQRegion shadowRegion;
+
+ occludedRects = occludedRegion.rects();
+ itEnd = occludedRects.end();
+ for (it = occludedRects.begin(); it != itEnd; ++it) {
+ // Expand each of the occluded region's shape rectangles to contain
+ // where a shadow of the specified thickness will be drawn. Create
+ // a new TQRegion that contains the expanded occluded region
+ it->setTop(it->top() - thickness + yOffset);
+ it->setLeft(it->left() - thickness + xOffset);
+ it->setRight(it->right() + thickness + xOffset);
+ it->setBottom(it->bottom() + thickness + yOffset);
+ shadowRegion += TQRegion(*it);
+ }
+ exposedRegion -= exposedRegion - shadowRegion;
+ }
+
+ return exposedRegion;
+}
+
+/*!
+ Draw shadow gradient around this window using cached opacity values.
+ */
+void Client::imposeCachedShadow(TQPixmap &pixmap, TQRegion exposed)
+{
+ QRgb pixel;
+ double opacity;
+ int red, green, blue, pixelRed, pixelGreen, pixelBlue;
+ int subW, subH, w, h, x, y, zeroX, zeroY;
+ TQImage image;
+ TQMemArray<TQRect>::Iterator it, itEnd;
+ TQMemArray<TQRect> rectangles;
+ TQPixmap subPixmap;
+ Window rootWindow;
+ int thickness, windowX, windowY, xOffset, yOffset;
+
+ rectangles = exposed.rects();
+ rootWindow = qt_xrootwin();
+ thickness = options->shadowThickness(isActive());
+ windowX = this->x();
+ windowY = this->y();
+ xOffset = options->shadowXOffset(isActive());
+ yOffset = options->shadowYOffset(isActive());
+ options->shadowColour(isActive()).rgb(&red, &green, &blue);
+ w = pixmap.width();
+ h = pixmap.height();
+
+ itEnd = rectangles.end();
+ for (it = rectangles.begin(); it != itEnd; ++it) {
+ subW = (*it).width();
+ subH = (*it).height();
+ subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(),
+ subW, subH);
+ zeroX = (*it).x() - windowX + thickness - xOffset;
+ zeroY = (*it).y() - windowY + thickness - yOffset;
+ image = subPixmap.convertToImage();
+
+ for (x = 0; x < subW; x++) {
+ for (y = 0; y < subH; y++) {
+ opacity = (*(opacityCache))[(zeroY + y) * w + zeroX + x];
+ pixel = image.pixel(x, y);
+ pixelRed = qRed(pixel);
+ pixelGreen = qGreen(pixel);
+ pixelBlue = qBlue(pixel);
+ image.setPixel(x, y,
+ qRgb((int)(pixelRed + (red - pixelRed) * opacity),
+ (int)(pixelGreen + (green - pixelGreen) * opacity),
+ (int)(pixelBlue + (blue - pixelBlue) * opacity)));
+ }
+ }
+
+ subPixmap.convertFromImage(image);
+ bitBlt(&pixmap, zeroX, zeroY, &subPixmap);
+ }
+}
+
+/*!
+ Draw shadow around this window using calculated opacity values.
+ */
+void Client::imposeRegionShadow(TQPixmap &pixmap, TQRegion occluded,
+ TQRegion exposed, int thickness, double maxOpacity)
+{
+ register int distance, intersectCount, i, j, x, y;
+ QRgb pixel;
+ double decay, factor, opacity;
+ int red, green, blue, pixelRed, pixelGreen, pixelBlue;
+ int halfMaxIntersects, lineIntersects, maxIntersects, maxY;
+ int irBottom, irLeft, irRight, irTop, yIncrement;
+ int subW, subH, w, h, zeroX, zeroY;
+ TQImage image;
+ TQMemArray<TQRect>::Iterator it, itEnd;
+ TQMemArray<TQRect> rectangles;
+ TQPixmap subPixmap;
+ Window rootWindow;
+ int windowX, windowY, xOffset, yOffset;
+
+ rectangles = exposed.rects();
+ rootWindow = qt_xrootwin();
+ windowX = this->x();
+ windowY = this->y();
+ xOffset = options->shadowXOffset(isActive());
+ yOffset = options->shadowYOffset(isActive());
+ options->shadowColour(isActive()).rgb(&red, &green, &blue);
+ maxIntersects = thickness * thickness * 4 + (thickness * 4) + 1;
+ halfMaxIntersects = maxIntersects / 2;
+ lineIntersects = thickness * 2 + 1;
+ factor = maxIntersects / maxOpacity;
+ decay = (lineIntersects / 0.0125 - factor) / pow((double)maxIntersects, 3.0);
+ w = pixmap.width();
+ h = pixmap.height();
+ xOffset = options->shadowXOffset(isActive());
+ yOffset = options->shadowYOffset(isActive());
+
+ opacityCache->resize(0);
+ opacityCache->resize(w * h);
+ occluded.translate(-windowX + thickness, -windowY + thickness);
+
+ itEnd = rectangles.end();
+ for (it = rectangles.begin(); it != itEnd; ++it) {
+ subW = (*it).width();
+ subH = (*it).height();
+ subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(),
+ subW, subH);
+ maxY = subH;
+ zeroX = (*it).x() - windowX + thickness - xOffset;
+ zeroY = (*it).y() - windowY + thickness - yOffset;
+ image = subPixmap.convertToImage();
+
+ intersectCount = 0;
+ opacity = -1;
+ y = 0;
+ yIncrement = 1;
+ for (x = 0; x < subW; x++) {
+ irLeft = zeroX + x - thickness;
+ irRight = zeroX + x + thickness;
+
+ while (y != maxY) {
+ // horizontal row about to leave the intersect region, not
+ // necessarily the top row
+ irTop = zeroY + y - thickness * yIncrement;
+ // horizontal row that just came into the intersect region,
+ // not necessarily the bottom row
+ irBottom = zeroY + y + thickness * yIncrement;
+
+ if (opacity == -1) {
+ // If occluded pixels caused an intersect count to be
+ // skipped, recount it
+ intersectCount = 0;
+
+ for (j = irTop; j != irBottom; j += yIncrement) {
+ // irTop is not necessarily larger than irBottom and
+ // yIncrement isn't necessarily positive
+ for (i = irLeft; i <= irRight; i++) {
+ if (occluded.contains(TQPoint(i, j)))
+ intersectCount++;
+ }
+ }
+ }
+ else {
+ if (intersectCount < 0)
+ intersectCount = 0;
+
+ for (i = irLeft; i <= irRight; i++) {
+ if (occluded.contains(TQPoint(i, irBottom)))
+ intersectCount++;
+ }
+ }
+
+ distance = maxIntersects - intersectCount;
+ opacity = intersectCount / (factor + pow((double)distance, 3.0) * decay);
+
+ (*(opacityCache))[(zeroY + y) * w + zeroX + x] = opacity;
+ pixel = image.pixel(x, y);
+ pixelRed = qRed(pixel);
+ pixelGreen = qGreen(pixel);
+ pixelBlue = qBlue(pixel);
+ image.setPixel(x, y,
+ qRgb((int)(pixelRed + (red - pixelRed) * opacity),
+ (int)(pixelGreen + (green - pixelGreen) * opacity),
+ (int)(pixelBlue + (blue - pixelBlue) * opacity)));
+
+ for (i = irLeft; i <= irRight; i++) {
+ if (occluded.contains(TQPoint(i, irTop)))
+ intersectCount--;
+ }
+
+ y += yIncrement;
+ }
+ y -= yIncrement;
+
+ irTop += yIncrement;
+ for (j = irTop; j != irBottom; j += yIncrement) {
+ if (occluded.contains(TQPoint(irLeft, j)))
+ intersectCount--;
+ }
+ irRight++;
+ for (j = irTop; j != irBottom; j += yIncrement) {
+ if (occluded.contains(TQPoint(irRight, j)))
+ intersectCount++;
+ }
+
+ yIncrement *= -1;
+ if (yIncrement < 0)
+ // Scan Y-axis bottom-up for next X-coordinate iteration
+ maxY = -1;
+ else
+ // Scan Y-axis top-down for next X-coordinate iteration
+ maxY = subH;
+ }
+
+ subPixmap.convertFromImage(image);
+ bitBlt(&pixmap, zeroX, zeroY, &subPixmap);
+ }
+}
+
/*!
Sets the client window's mapping state. Possible values are
WithdrawnState, IconicState, NormalState.
@@ -989,6 +1620,8 @@ void Client::rawShow()
XMapWindow( qt_xdisplay(), wrapper );
XMapWindow( qt_xdisplay(), client );
}
+ if (options->shadowEnabled(isActive()))
+ drawDelayedShadow();
}
/*!
@@ -1004,6 +1637,8 @@ void Client::rawHide()
// which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
// will be missed is also very minimal, so I don't think it's needed to grab the server
// here.
+ removeShadow();
+ drawIntersectingShadows();
XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
XUnmapWindow( qt_xdisplay(), frame );
XUnmapWindow( qt_xdisplay(), wrapper );
diff --git a/kwin/client.h b/kwin/client.h
index 306873a1e..1c4d45f51 100644
--- a/kwin/client.h
+++ b/kwin/client.h
@@ -200,6 +200,19 @@ class Client : public TQObject, public KDecorationDefines
void updateDecoration( bool check_workspace_pos, bool force = false );
void checkBorderSizes();
+ // drop shadow
+ bool isShadowed() const;
+ void setShadowed(bool shadowed);
+ Window shadowId() const;
+ // Aieee, a friend function! Unpleasant, yes, but it's needed by
+ // raiseClient() to redraw a window's shadow when it is active prior to
+ // being raised.
+ friend void Workspace::raiseClient(Client *);
+ // Wouldn't you know it, friend functions breed. This one's needed to
+ // enable a DCOP function that causes all shadows obscuring a changed
+ // window to be redrawn.
+ friend void Workspace::updateOverlappingShadows(WId);
+
// shape extensions
bool shape() const;
void updateShape();
@@ -312,6 +325,8 @@ class Client : public TQObject, public KDecorationDefines
void autoRaise();
void shadeHover();
void shortcutActivated();
+ void updateOpacityCache();
+
private:
friend class Bridge; // FRAME
@@ -348,12 +363,29 @@ class Client : public TQObject, public KDecorationDefines
bool buttonReleaseEvent( Window w, int button, int state, int x, int y, int x_root, int y_root );
bool motionNotifyEvent( Window w, int state, int x, int y, int x_root, int y_root );
+ // drop shadows
+ void drawIntersectingShadows();
+ void drawOverlappingShadows(bool waitForMe);
+ TQRegion getExposedRegion(TQRegion occludedRegion, int x, int y,
+ int w, int h, int thickness, int xOffset, int yOffset);
+ void imposeCachedShadow(TQPixmap &pixmap, TQRegion exposed);
+ void imposeRegionShadow(TQPixmap &pixmap, TQRegion occluded,
+ TQRegion exposed, int thickness, double maxOpacity = 0.75);
+
void processDecorationButtonPress( int button, int state, int x, int y, int x_root, int y_root );
private slots:
void pingTimeout();
void processKillerExited();
void demandAttentionKNotify();
+ void drawShadow();
+ void drawShadowAfter(Client *after);
+ void drawDelayedShadow();
+ void removeShadow();
+
+ signals:
+ void shadowDrawn();
+
private:
// ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1
@@ -531,6 +563,16 @@ class Client : public TQObject, public KDecorationDefines
bool pending_geometry_update;
bool shade_geometry_change;
int border_left, border_right, border_top, border_bottom;
+
+ Client* shadowAfterClient;
+ TQWidget* shadowWidget;
+ TQMemArray<double> activeOpacityCache;
+ TQMemArray<double> inactiveOpacityCache;
+ TQMemArray<double>* opacityCache;
+ TQRegion shapeBoundingRegion;
+ TQTimer* shadowDelayTimer;
+ bool shadowMe;
+
TQRegion _mask;
static bool check_active_modal; // see Client::checkActiveModal()
KShortcut _shortcut;
@@ -880,6 +922,16 @@ inline void Client::plainResize( const TQSize& s, ForceGeometry_t force )
plainResize( s.width(), s.height(), force );
}
+inline bool Client::isShadowed() const
+ {
+ return shadowMe;
+ }
+
+inline Window Client::shadowId() const
+ {
+ return shadowWidget != NULL ? shadowWidget->winId() : None;
+ }
+
inline void Client::resizeWithChecks( const TQSize& s, ForceGeometry_t force )
{
resizeWithChecks( s.width(), s.height(), force );
diff --git a/kwin/events.cpp b/kwin/events.cpp
index ba6321a7c..a30e915e1 100644
--- a/kwin/events.cpp
+++ b/kwin/events.cpp
@@ -1078,6 +1078,217 @@ int qtToX11State( Qt::ButtonState state )
// for the decoration window cannot be (easily) intercepted as X11 events
bool Client::eventFilter( TQObject* o, TQEvent* e )
{
+ if (o == shadowWidget)
+ {
+ if (e->type() == TQEvent::MouseButtonRelease)
+ {
+ int buttonMask, buttonPressed, x, y, x_root, y_root;
+ unsigned int mask;
+ TQMouseEvent *qe = (TQMouseEvent *)e;
+ Window inner_window, parent_window, pointer_window, root_window;
+ XButtonEvent xe;
+
+ removeShadow();
+ switch (qe->button())
+ {
+ case Qt::MidButton:
+ buttonMask = Button2Mask;
+ buttonPressed = Button2;
+ break;
+ case Qt::RightButton:
+ buttonMask = Button3Mask;
+ buttonPressed = Button3;
+ break;
+ default:
+ buttonMask = Button1Mask;
+ buttonPressed = Button1;
+ break;
+ }
+
+ // find the window under the cursor that should receive the
+ // simulated events
+ root_window = qt_xrootwin();
+ XQueryPointer(qt_xdisplay(), root_window, &root_window,
+ &pointer_window, &x_root, &y_root, &x, &y, &mask);
+
+ if (pointer_window != None)
+ {
+ // Save the child window immediately under the window
+ // decoration, if any. This is so that we can send an event to
+ // the immediate descendant of a window's window decoration,
+ // which causes KWin to refocus windows properly
+ parent_window = pointer_window;
+ XQueryPointer(qt_xdisplay(), parent_window, &root_window,
+ &pointer_window, &x_root, &y_root, &x, &y, &mask);
+ inner_window = pointer_window;
+
+ while (pointer_window != None)
+ {
+ // Recursively query for the child window under the pointer,
+ // using the returned child window as the parent window for
+ // the subsequent query. When no child window is left, we've
+ // found the child that will receive the simulated event
+ parent_window = pointer_window;
+ XQueryPointer(qt_xdisplay(), parent_window, &root_window,
+ &pointer_window, &x_root, &y_root, &x, &y, &mask);
+ }
+ pointer_window = parent_window;
+ }
+ else
+ inner_window = None;
+
+ // simulate a mouse button press
+ xe.type = ButtonPress;
+ xe.display = qt_xdisplay();
+ xe.root = qt_xrootwin();
+ xe.subwindow = None;
+ xe.time = CurrentTime;
+ xe.x = x;
+ xe.y = y;
+ xe.x_root = x_root;
+ xe.y_root = y_root;
+ xe.state = 0;
+ xe.button = buttonPressed;
+ xe.same_screen = True;
+ if (inner_window != None && inner_window != pointer_window)
+ {
+ xe.window = inner_window;
+ XSendEvent(qt_xdisplay(), inner_window, True, ButtonPressMask,
+ (XEvent *)&xe);
+ }
+ xe.window = pointer_window;
+ XSendEvent(qt_xdisplay(), pointer_window, True, ButtonPressMask,
+ (XEvent *)&xe);
+
+ // simulate a mouse button release
+ xe.type = ButtonRelease;
+ xe.display = qt_xdisplay();
+ xe.root = qt_xrootwin();
+ xe.subwindow = None;
+ xe.time = CurrentTime;
+ xe.x = x;
+ xe.y = y;
+ xe.x_root = x_root;
+ xe.y_root = y_root;
+ xe.state = buttonMask;
+ xe.button = buttonPressed;
+ xe.same_screen = True;
+ if (inner_window != None && inner_window != pointer_window)
+ {
+ xe.window = inner_window;
+ XSendEvent(qt_xdisplay(), inner_window, True, ButtonReleaseMask,
+ (XEvent *)&xe);
+ }
+ xe.window = pointer_window;
+ XSendEvent(qt_xdisplay(), pointer_window, True, ButtonReleaseMask,
+ (XEvent *)&xe);
+
+ drawDelayedShadow();
+
+ return true;
+ }
+ else if (e->type() == TQEvent::Wheel)
+ {
+ int x, y, x_root, y_root;
+ unsigned int buttonMask, buttonPressed, mask;
+ TQWheelEvent *wheelEvent = (TQWheelEvent *)e;
+ Window inner_window, parent_window, pointer_window,
+ root_window;
+ XButtonEvent xe;
+
+ removeShadow();
+
+ // state and button parameters passed to XSendEvent depend on the
+ // direction in which the mouse wheel was rolled
+ buttonMask = wheelEvent->delta() > 0 ? Button4Mask : Button5Mask;
+ buttonPressed = wheelEvent->delta() > 0 ? Button4 : Button5;
+
+ // find the window under the cursor that should receive the
+ // simulated events
+ root_window = qt_xrootwin();
+ XQueryPointer(qt_xdisplay(), root_window, &root_window,
+ &pointer_window, &x_root, &y_root, &x, &y, &mask);
+
+ if (pointer_window != None)
+ {
+ // Save the child window immediately under the window
+ // decoration, if any. This is so that we can send an event to
+ // the immediate descendant of a window's window decoration,
+ // which causes KWin to refocus windows properly
+ parent_window = pointer_window;
+ XQueryPointer(qt_xdisplay(), parent_window, &root_window,
+ &pointer_window, &x_root, &y_root, &x, &y, &mask);
+ inner_window = pointer_window;
+
+ while (pointer_window != None)
+ {
+ // Recursively query for the child window under the pointer,
+ // using the returned child window as the parent window for
+ // the subsequent query. When no child window is left, we've
+ // found the child that will receive the simulated event
+ parent_window = pointer_window;
+ XQueryPointer(qt_xdisplay(), parent_window, &root_window,
+ &pointer_window, &x_root, &y_root, &x, &y, &mask);
+ }
+ pointer_window = parent_window;
+ }
+ else
+ inner_window = None;
+
+ // simulate a mouse button press
+ xe.type = ButtonPress;
+ xe.display = qt_xdisplay();
+ xe.root = qt_xrootwin();
+ xe.subwindow = None;
+ xe.time = CurrentTime;
+ xe.x = x;
+ xe.y = y;
+ xe.x_root = x_root;
+ xe.y_root = y_root;
+ xe.state = 0;
+ xe.same_screen = True;
+ if (inner_window != None && inner_window != pointer_window)
+ {
+ xe.button = buttonPressed;
+ xe.window = inner_window;
+ XSendEvent(qt_xdisplay(), inner_window, True, ButtonPressMask,
+ (XEvent *)&xe);
+ }
+ xe.button = buttonPressed;
+ xe.window = pointer_window;
+ XSendEvent(qt_xdisplay(), pointer_window, True, ButtonPressMask,
+ (XEvent *)&xe);
+
+ // simulate a mouse button release
+ xe.type = ButtonRelease;
+ xe.display = qt_xdisplay();
+ xe.root = qt_xrootwin();
+ xe.subwindow = None;
+ xe.time = CurrentTime;
+ xe.x = x;
+ xe.y = y;
+ xe.x_root = x_root;
+ xe.y_root = y_root;
+ xe.same_screen = True;
+ if (inner_window != None && inner_window != pointer_window)
+ {
+ xe.window = inner_window;
+ xe.state = buttonMask;
+ xe.button = buttonPressed;
+ XSendEvent(qt_xdisplay(), inner_window, True, ButtonReleaseMask,
+ (XEvent *)&xe);
+ }
+ xe.state = buttonMask;
+ xe.button = buttonPressed;
+ xe.window = pointer_window;
+ XSendEvent(qt_xdisplay(), pointer_window, True, ButtonReleaseMask,
+ (XEvent *)&xe);
+
+ drawDelayedShadow();
+
+ return true;
+ }
+ }
if( decoration == NULL
|| o != decoration->widget())
return false;
diff --git a/kwin/geometry.cpp b/kwin/geometry.cpp
index f531bdfb8..3fd278360 100644
--- a/kwin/geometry.cpp
+++ b/kwin/geometry.cpp
@@ -1063,6 +1063,15 @@ void Client::checkDirection( int new_diff, int old_diff, TQRect& rect, const TQR
rect.moveLeft( area.right() - 5 );
}
}
+ if (!moveResizeMode && options->shadowEnabled(isActive()))
+ {
+ // If the user is manually resizing, let Client::leaveMoveResize()
+ // decide when to redraw the shadow
+ removeShadow();
+ drawIntersectingShadows();
+ if (options->shadowEnabled(isActive()))
+ drawDelayedShadow();
+ }
}
/*!
@@ -2322,6 +2331,7 @@ bool Client::startMoveResize()
}
if ( maximizeMode() != MaximizeRestore )
resetMaximize();
+ removeShadow();
moveResizeMode = true;
workspace()->setClientIsMoving(this);
initialMoveResizeGeom = moveResizeGeom = geometry();
@@ -2390,6 +2400,11 @@ void Client::leaveMoveResize()
moveResizeMode = false;
delete eater;
eater = 0;
+ if (options->shadowEnabled(isActive()))
+ {
+ drawIntersectingShadows();
+ updateOpacityCache();
+ }
}
// This function checks if it actually makes sense to perform a restricted move/resize.
diff --git a/kwin/kcmkwin/kwindecoration/kwindecoration.cpp b/kwin/kcmkwin/kwindecoration/kwindecoration.cpp
index 68aec85ff..193450086 100644
--- a/kwin/kcmkwin/kwindecoration/kwindecoration.cpp
+++ b/kwin/kcmkwin/kwindecoration/kwindecoration.cpp
@@ -28,6 +28,8 @@
*/
#include <assert.h>
+#include <math.h>
+
#include <tqdir.h>
#include <tqfileinfo.h>
#include <tqlayout.h>
@@ -39,8 +41,10 @@
#include <tqlabel.h>
#include <tqfile.h>
#include <tqslider.h>
+#include <tqspinbox.h>
#include <kapplication.h>
+#include <kcolorbutton.h>
#include <kcombobox.h>
#include <kdebug.h>
#include <kdesktopfile.h>
@@ -153,6 +157,164 @@ KWinDecorationModule::KWinDecorationModule(TQWidget* parent, const char* name, c
preview->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding);
tabWidget->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Maximum);
+ // Page 3 (Window Shadows)
+ TQHBox *inactiveShadowColourHBox, *shadowColourHBox;
+ TQHBox *inactiveShadowOpacityHBox, *shadowOpacityHBox;
+ TQHBox *inactiveShadowXOffsetHBox, *shadowXOffsetHBox;
+ TQHBox *inactiveShadowYOffsetHBox, *shadowYOffsetHBox;
+ TQHBox *inactiveShadowThicknessHBox, *shadowThicknessHBox;
+ TQLabel *inactiveShadowColourLabel, *shadowColourLabel;
+ TQLabel *inactiveShadowOpacityLabel, *shadowOpacityLabel;
+ TQLabel *inactiveShadowXOffsetLabel, *shadowXOffsetLabel;
+ TQLabel *inactiveShadowYOffsetLabel, *shadowYOffsetLabel;
+ TQLabel *inactiveShadowThicknessLabel, *shadowThicknessLabel;
+
+ shadowPage = new TQVBox(tabWidget);
+ shadowPage->setSpacing(KDialog::spacingHint());
+ shadowPage->setMargin(KDialog::marginHint());
+
+ cbWindowShadow = new TQCheckBox(
+ i18n("&Draw a drop shadow under windows"), shadowPage);
+ TQWhatsThis::add(cbWindowShadow,
+ i18n("Enabling this checkbox will allow you to choose a kind of "
+ "drop shadow to draw under each window."));
+
+ activeShadowSettings = new TQGroupBox(1, Qt::Horizontal,
+ i18n("Active Window Shadow"), shadowPage);
+ inactiveShadowSettings = new TQGroupBox(1, Qt::Horizontal,
+ i18n("Inactive Window Shadows"), shadowPage);
+ whichShadowSettings = new TQGroupBox(3, Qt::Horizontal,
+ i18n("Draw Shadow Under Normal Windows And..."), shadowPage);
+
+ cbShadowDocks = new TQCheckBox(i18n("Docks and &panels"),
+ whichShadowSettings);
+ connect(cbShadowDocks, TQT_SIGNAL(toggled(bool)),
+ TQT_SLOT(slotSelectionChanged()));
+ cbShadowOverrides = new TQCheckBox(i18n("O&verride windows"),
+ whichShadowSettings);
+ connect(cbShadowOverrides, TQT_SIGNAL(toggled(bool)),
+ TQT_SLOT(slotSelectionChanged()));
+ cbShadowTopMenus = new TQCheckBox(i18n("&Top menu"),
+ whichShadowSettings);
+ connect(cbShadowTopMenus, TQT_SIGNAL(toggled(bool)),
+ TQT_SLOT(slotSelectionChanged()));
+ cbInactiveShadow = new TQCheckBox(
+ i18n("Draw shadow under &inactive windows"), inactiveShadowSettings);
+ connect(cbInactiveShadow, TQT_SIGNAL(toggled(bool)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ shadowColourHBox = new TQHBox(activeShadowSettings);
+ shadowColourHBox->setSpacing(KDialog::spacingHint());
+ shadowColourLabel = new TQLabel(i18n("Colour:"), shadowColourHBox);
+ shadowColourButton = new KColorButton(shadowColourHBox);
+ connect(shadowColourButton, TQT_SIGNAL(changed(const TQColor &)), TQT_SLOT(slotSelectionChanged()));
+
+ inactiveShadowColourHBox = new TQHBox(inactiveShadowSettings);
+ inactiveShadowColourHBox->setSpacing(KDialog::spacingHint());
+ inactiveShadowColourLabel = new TQLabel(i18n("Colour:"), inactiveShadowColourHBox);
+ inactiveShadowColourButton = new KColorButton(inactiveShadowColourHBox);
+ connect(inactiveShadowColourButton, TQT_SIGNAL(changed(const TQColor &)), TQT_SLOT(slotSelectionChanged()));
+
+ shadowOpacityHBox = new TQHBox(activeShadowSettings);
+ shadowOpacityHBox->setSpacing(KDialog::spacingHint());
+ shadowOpacityLabel = new TQLabel(i18n("Maximum opacity:"), shadowOpacityHBox);
+ shadowOpacitySlider = new TQSlider(1, 100, 10, 50, Qt::Horizontal,
+ shadowOpacityHBox);
+ shadowOpacitySlider->setTickmarks(TQSlider::Below);
+ shadowOpacitySlider->setTickInterval(10);
+ shadowOpacitySpinBox = new TQSpinBox(1, 100, 1, shadowOpacityHBox);
+ shadowOpacitySpinBox->setSuffix(" %");
+ connect(shadowOpacitySlider, TQT_SIGNAL(valueChanged(int)), shadowOpacitySpinBox,
+ TQT_SLOT(setValue(int)));
+ connect(shadowOpacitySpinBox, TQT_SIGNAL(valueChanged(int)), shadowOpacitySlider,
+ TQT_SLOT(setValue(int)));
+ connect(shadowOpacitySlider, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ inactiveShadowOpacityHBox = new TQHBox(inactiveShadowSettings);
+ inactiveShadowOpacityHBox->setSpacing(KDialog::spacingHint());
+ inactiveShadowOpacityLabel = new TQLabel(i18n("Maximum opacity:"),
+ inactiveShadowOpacityHBox);
+ inactiveShadowOpacitySlider = new TQSlider(1, 100, 10, 50, Qt::Horizontal,
+ inactiveShadowOpacityHBox);
+ inactiveShadowOpacitySlider->setTickmarks(TQSlider::Below);
+ inactiveShadowOpacitySlider->setTickInterval(10);
+ inactiveShadowOpacitySpinBox = new TQSpinBox(1, 100, 1,
+ inactiveShadowOpacityHBox);
+ inactiveShadowOpacitySpinBox->setSuffix(" %");
+ connect(inactiveShadowOpacitySlider, TQT_SIGNAL(valueChanged(int)),
+ inactiveShadowOpacitySpinBox,
+ TQT_SLOT(setValue(int)));
+ connect(inactiveShadowOpacitySpinBox, TQT_SIGNAL(valueChanged(int)),
+ inactiveShadowOpacitySlider,
+ TQT_SLOT(setValue(int)));
+ connect(inactiveShadowOpacitySlider, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ shadowXOffsetHBox = new TQHBox(activeShadowSettings);
+ shadowXOffsetHBox->setSpacing(KDialog::spacingHint());
+ shadowXOffsetLabel = new TQLabel(
+ i18n("Offset rightward (may be negative):"),
+ shadowXOffsetHBox);
+ shadowXOffsetSpinBox = new TQSpinBox(-1024, 1024, 1, shadowXOffsetHBox);
+ shadowXOffsetSpinBox->setSuffix(i18n(" pixels"));
+ connect(shadowXOffsetSpinBox, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ inactiveShadowXOffsetHBox = new TQHBox(inactiveShadowSettings);
+ inactiveShadowXOffsetHBox->setSpacing(KDialog::spacingHint());
+ inactiveShadowXOffsetLabel = new TQLabel(
+ i18n("Offset rightward (may be negative):"),
+ inactiveShadowXOffsetHBox);
+ inactiveShadowXOffsetSpinBox = new TQSpinBox(-1024, 1024, 1,
+ inactiveShadowXOffsetHBox);
+ inactiveShadowXOffsetSpinBox->setSuffix(i18n(" pixels"));
+ connect(inactiveShadowXOffsetSpinBox, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ shadowYOffsetHBox = new TQHBox(activeShadowSettings);
+ shadowYOffsetHBox->setSpacing(KDialog::spacingHint());
+ shadowYOffsetLabel = new TQLabel(
+ i18n("Offset downward (may be negative):"),
+ shadowYOffsetHBox);
+ shadowYOffsetSpinBox = new TQSpinBox(-1024, 1024, 1, shadowYOffsetHBox);
+ shadowYOffsetSpinBox->setSuffix(i18n(" pixels"));
+ connect(shadowYOffsetSpinBox, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ inactiveShadowYOffsetHBox = new TQHBox(inactiveShadowSettings);
+ inactiveShadowYOffsetHBox->setSpacing(KDialog::spacingHint());
+ inactiveShadowYOffsetLabel = new TQLabel(
+ i18n("Offset downward (may be negative):"),
+ inactiveShadowYOffsetHBox);
+ inactiveShadowYOffsetSpinBox = new TQSpinBox(-1024, 1024, 1,
+ inactiveShadowYOffsetHBox);
+ inactiveShadowYOffsetSpinBox->setSuffix(i18n(" pixels"));
+ connect(inactiveShadowYOffsetSpinBox, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ shadowThicknessHBox = new TQHBox(activeShadowSettings);
+ shadowThicknessHBox->setSpacing(KDialog::spacingHint());
+ shadowThicknessLabel = new TQLabel(
+ i18n("Thickness to either side of window:"),
+ shadowThicknessHBox);
+ shadowThicknessSpinBox = new TQSpinBox(1, 100, 1,
+ shadowThicknessHBox);
+ shadowThicknessSpinBox->setSuffix(i18n(" pixels"));
+ connect(shadowThicknessSpinBox, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
+ inactiveShadowThicknessHBox = new TQHBox(inactiveShadowSettings);
+ inactiveShadowThicknessHBox->setSpacing(KDialog::spacingHint());
+ inactiveShadowThicknessLabel = new TQLabel(
+ i18n("Thickness to either side of window:"),
+ inactiveShadowThicknessHBox);
+ inactiveShadowThicknessSpinBox = new TQSpinBox(1, 100, 1,
+ inactiveShadowThicknessHBox);
+ inactiveShadowThicknessSpinBox->setSuffix(i18n(" pixels"));
+ connect(inactiveShadowThicknessSpinBox, TQT_SIGNAL(valueChanged(int)),
+ TQT_SLOT(slotSelectionChanged()));
+
// Load all installed decorations into memory
// Set up the decoration lists and other UI settings
findDecorations();
@@ -162,6 +324,7 @@ KWinDecorationModule::KWinDecorationModule(TQWidget* parent, const char* name, c
tabWidget->insertTab( pluginPage, i18n("&Window Decoration") );
tabWidget->insertTab( buttonPage, i18n("&Buttons") );
+ tabWidget->insertTab( shadowPage, i18n("&Shadows") );
connect( buttonPositionWidget, TQT_SIGNAL(changed()), this, TQT_SLOT(slotButtonsChanged()) ); // update preview etc.
connect( buttonPositionWidget, TQT_SIGNAL(changed()), this, TQT_SLOT(slotSelectionChanged()) ); // emit changed()...
@@ -171,7 +334,12 @@ KWinDecorationModule::KWinDecorationModule(TQWidget* parent, const char* name, c
connect( cbUseCustomButtonPositions, TQT_SIGNAL(clicked()), TQT_SLOT(slotSelectionChanged()) );
connect(cbUseCustomButtonPositions, TQT_SIGNAL(toggled(bool)), buttonPositionWidget, TQT_SLOT(setEnabled(bool)));
connect(cbUseCustomButtonPositions, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotButtonsChanged()) );
+ connect(cbWindowShadow, TQT_SIGNAL(toggled(bool)), activeShadowSettings, TQT_SLOT(setEnabled(bool)));
+ connect(cbWindowShadow, TQT_SIGNAL(toggled(bool)), inactiveShadowSettings, TQT_SLOT(setEnabled(bool)));
+ connect(cbWindowShadow, TQT_SIGNAL(toggled(bool)), whichShadowSettings, TQT_SLOT(setEnabled(bool)));
+
connect( cbShowToolTips, TQT_SIGNAL(clicked()), TQT_SLOT(slotSelectionChanged()) );
+ connect( cbWindowShadow, TQT_SIGNAL(clicked()), TQT_SLOT(slotSelectionChanged()) );
connect( cBorder, TQT_SIGNAL( activated( int )), TQT_SLOT( slotBorderChanged( int )));
// connect( cbUseMiniWindows, TQT_SIGNAL(clicked()), TQT_SLOT(slotSelectionChanged()) );
@@ -465,6 +633,28 @@ void KWinDecorationModule::readConfig( KConfig* conf )
border_size = BorderNormal;
checkSupportedBorderSizes();
+ // Shadows tab
+ // ===========
+ bool shadowEnabled = conf->readBoolEntry("ShadowEnabled", false);
+ cbWindowShadow->setChecked(shadowEnabled);
+ activeShadowSettings->setEnabled(shadowEnabled);
+ inactiveShadowSettings->setEnabled(shadowEnabled);
+ whichShadowSettings->setEnabled(shadowEnabled);
+ shadowColourButton->setColor(conf->readColorEntry("ShadowColour", &Qt::black));
+ shadowOpacitySlider->setValue((int)ceil(conf->readDoubleNumEntry("ShadowOpacity", 0.70) * 100));
+ shadowXOffsetSpinBox->setValue(conf->readNumEntry("ShadowXOffset", 0));
+ shadowYOffsetSpinBox->setValue(conf->readNumEntry("ShadowYOffset", 10));
+ cbShadowDocks->setChecked(conf->readBoolEntry("ShadowDocks", false));
+ cbShadowOverrides->setChecked(conf->readBoolEntry("ShadowOverrides", false));
+ cbShadowTopMenus->setChecked(conf->readBoolEntry("ShadowTopMenus", false));
+ shadowThicknessSpinBox->setValue(conf->readNumEntry("ShadowThickness", 10));
+ cbInactiveShadow->setChecked(conf->readBoolEntry("InactiveShadowEnabled", false));
+ inactiveShadowColourButton->setColor(conf->readColorEntry("InactiveShadowColour", &Qt::black));
+ inactiveShadowOpacitySlider->setValue((int)ceil(conf->readDoubleNumEntry("InactiveShadowOpacity", 0.70) * 100));
+ inactiveShadowXOffsetSpinBox->setValue(conf->readNumEntry("InactiveShadowXOffset", 0));
+ inactiveShadowYOffsetSpinBox->setValue(conf->readNumEntry("InactiveShadowYOffset", 5));
+ inactiveShadowThicknessSpinBox->setValue(conf->readNumEntry("InactiveShadowThickness", 5));
+
emit KCModule::changed(false);
}
@@ -489,6 +679,27 @@ void KWinDecorationModule::writeConfig( KConfig* conf )
conf->writeEntry("ButtonsOnRight", buttonPositionWidget->buttonsRight() );
conf->writeEntry("BorderSize", border_size );
+ // Shadow settings
+ conf->writeEntry("ShadowEnabled", cbWindowShadow->isChecked());
+ conf->writeEntry("ShadowColour", shadowColourButton->color());
+ conf->writeEntry("ShadowOpacity", shadowOpacitySlider->value() / 100.0);
+ conf->writeEntry("ShadowXOffset", shadowXOffsetSpinBox->value());
+ conf->writeEntry("ShadowYOffset", shadowYOffsetSpinBox->value());
+ conf->writeEntry("ShadowThickness", shadowThicknessSpinBox->value());
+ conf->writeEntry("ShadowDocks", cbShadowDocks->isChecked());
+ conf->writeEntry("ShadowOverrides", cbShadowOverrides->isChecked());
+ conf->writeEntry("ShadowTopMenus", cbShadowTopMenus->isChecked());
+ conf->writeEntry("InactiveShadowEnabled", cbInactiveShadow->isChecked());
+ conf->writeEntry("InactiveShadowColour", inactiveShadowColourButton->color());
+ conf->writeEntry("InactiveShadowOpacity",
+ inactiveShadowOpacitySlider->value() / 100.0);
+ conf->writeEntry("InactiveShadowXOffset",
+ inactiveShadowXOffsetSpinBox->value());
+ conf->writeEntry("InactiveShadowYOffset",
+ inactiveShadowYOffsetSpinBox->value());
+ conf->writeEntry("InactiveShadowThickness",
+ inactiveShadowThicknessSpinBox->value());
+
oldLibraryName = currentLibraryName;
currentLibraryName = libName;
@@ -541,6 +752,7 @@ void KWinDecorationModule::defaults()
cbUseCustomButtonPositions->setChecked( false );
buttonPositionWidget->setEnabled( false );
cbShowToolTips->setChecked( true );
+ cbWindowShadow->setChecked( false );
// cbUseMiniWindows->setChecked( false);
// Don't set default for now
// decorationList->setSelected(
@@ -552,6 +764,21 @@ void KWinDecorationModule::defaults()
border_size = BorderNormal;
checkSupportedBorderSizes();
+ shadowColourButton->setColor(Qt::black);
+ shadowOpacitySlider->setValue(70);
+ shadowXOffsetSpinBox->setValue(0);
+ shadowYOffsetSpinBox->setValue(10);
+ shadowThicknessSpinBox->setValue(10);
+ cbShadowDocks->setChecked(false);
+ cbShadowOverrides->setChecked(false);
+ cbShadowTopMenus->setChecked(false);
+ cbInactiveShadow->setChecked(false);
+ inactiveShadowColourButton->setColor(Qt::black);
+ inactiveShadowOpacitySlider->setValue(70);
+ inactiveShadowXOffsetSpinBox->setValue(0);
+ inactiveShadowYOffsetSpinBox->setValue(5);
+ inactiveShadowThicknessSpinBox->setValue(5);
+
// Set plugin defaults
emit pluginDefaults();
}
diff --git a/kwin/kcmkwin/kwindecoration/kwindecoration.h b/kwin/kcmkwin/kwindecoration/kwindecoration.h
index c4a123352..53b3ed6ae 100644
--- a/kwin/kcmkwin/kwindecoration/kwindecoration.h
+++ b/kwin/kcmkwin/kwindecoration/kwindecoration.h
@@ -127,6 +127,19 @@ class KWinDecorationModule : public KCModule, virtual public KWinDecorationIface
// Page 2
ButtonPositionWidget *buttonPositionWidget;
TQVBox* buttonPage;
+
+ // Page 3
+ TQVBox *shadowPage;
+ KColorButton *inactiveShadowColourButton, *shadowColourButton;
+ TQCheckBox *cbShadowDocks, *cbShadowOverrides, *cbShadowTopMenus;
+ TQCheckBox *cbInactiveShadow, *cbWindowShadow;
+ TQGroupBox *activeShadowSettings, *inactiveShadowSettings;
+ TQGroupBox *whichShadowSettings;
+ TQSlider *inactiveShadowOpacitySlider, *shadowOpacitySlider;
+ TQSpinBox *inactiveShadowOpacitySpinBox, *shadowOpacitySpinBox;
+ TQSpinBox *inactiveShadowXOffsetSpinBox, *shadowXOffsetSpinBox;
+ TQSpinBox *inactiveShadowYOffsetSpinBox, *shadowYOffsetSpinBox;
+ TQSpinBox *inactiveShadowThicknessSpinBox, *shadowThicknessSpinBox;
};
diff --git a/kwin/layers.cpp b/kwin/layers.cpp
index 40a7583a4..8d942c731 100644
--- a/kwin/layers.cpp
+++ b/kwin/layers.cpp
@@ -134,37 +134,89 @@ void Workspace::propagateClients( bool propagate_new_clients )
// when passig pointers around.
// restack the windows according to the stacking order
+#if 0
Window* new_stack = new Window[ stacking_order.count() + 2 ];
int pos = 0;
+#endif
+ NET::WindowType t;
+ Window shadow;
+ Window *dock_shadow_stack, *window_stack;
+ int i, numDocks, pos, topmenu_space_pos;
+
+ dock_shadow_stack = new Window[ stacking_order.count() * 2 ];
+ window_stack = new Window[ stacking_order.count() * 2 + 2 ];
+ i = 0;
+ pos = 0;
+ topmenu_space_pos = 1; // not 0, that's supportWindow !!!
+
// Stack all windows under the support window. The support window is
// not used for anything (besides the NETWM property), and it's not shown,
// but it was lowered after kwin startup. Stacking all clients below
// it ensures that no client will be ever shown above override-redirect
// windows (e.g. popups).
+#if 0
new_stack[ pos++ ] = supportWindow->winId();
int topmenu_space_pos = 1; // not 0, that's supportWindow !!!
+#endif
+ window_stack[pos++] = supportWindow->winId();
for( ClientList::ConstIterator it = stacking_order.fromLast();
it != stacking_order.end();
--it )
{
+#if 0
new_stack[ pos++ ] = (*it)->frameId();
if( (*it)->belongsToLayer() >= DockLayer )
topmenu_space_pos = pos;
- }
+#endif
+ t = (*it)->windowType();
+ switch (t)
+ {
+ case NET::Dock:
+ window_stack[pos++] = (*it)->frameId();
+ if ((shadow = (*it)->shadowId()) != None)
+ dock_shadow_stack[i++] = shadow;
+ break;
+ case NET::Desktop:
+ numDocks = i;
+ for (i = 0; i < numDocks; i++)
+ // Shadows for dock windows go just above the desktop
+ window_stack[pos++] = dock_shadow_stack[i];
+ window_stack[pos++] = (*it)->frameId();
+ break;
+ case NET::TopMenu:
+ topmenu_space_pos = pos;
+ // fall through
+ default:
+ window_stack[pos++] = (*it)->frameId();
+ if ((shadow = (*it)->shadowId()) != None)
+ // If the current window also has a shadow, place it
+ // immediately under the current window
+ window_stack[pos++] = shadow;
+ }
+ }
if( topmenu_space != NULL )
{ // make sure the topmenu space is below all topmenus, fullscreens, etc.
for( int i = pos;
i > topmenu_space_pos;
--i )
+#if 0
new_stack[ i ] = new_stack[ i - 1 ];
new_stack[ topmenu_space_pos ] = topmenu_space->winId();
+#endif
+ window_stack[ i ] = window_stack[ i - 1 ];
+ window_stack[ topmenu_space_pos ] = topmenu_space->winId();
++pos;
}
// TODO isn't it too inefficient to restart always all clients?
// TODO don't restack not visible windows?
assert( new_stack[ 0 ] = supportWindow->winId());
+#if 0
XRestackWindows(qt_xdisplay(), new_stack, pos);
delete [] new_stack;
+#endif
+ XRestackWindows(qt_xdisplay(), window_stack, pos);
+ delete [] dock_shadow_stack;
+ delete [] window_stack;
if ( propagate_new_clients )
{
@@ -342,6 +394,11 @@ void Workspace::raiseClient( Client* c )
unconstrained_stacking_order.remove( c );
unconstrained_stacking_order.append( c );
+ if (options->shadowEnabled(c->isActive()))
+ {
+ c->removeShadow();
+ c->drawDelayedShadow();
+ }
if( !c->isSpecialWindow())
{
diff --git a/kwin/lib/kdecoration.h b/kwin/lib/kdecoration.h
index b4f62d6f8..f41f19041 100644
--- a/kwin/lib/kdecoration.h
+++ b/kwin/lib/kdecoration.h
@@ -97,6 +97,7 @@ public:
LowerOp,
FullScreenOp,
NoBorderOp,
+ ShadowOp,
NoOp,
SetupWindowShortcutOp,
ApplicationRulesOp ///< @since 3.5
@@ -116,7 +117,7 @@ public:
ColorHandle, ///< The color for the resize handle
NUM_COLORS
};
-
+
/**
* These flags specify which settings changed when rereading settings.
* Each setting in class KDecorationOptions specifies its matching flag.
@@ -130,7 +131,7 @@ public:
SettingTooltips = 1 << 4, ///< The tooltip setting was changed
SettingBorder = 1 << 5 ///< The border size setting was changed
};
-
+
/**
* Border size. KDecorationOptions::preferredBorderSize() returns
* one of these values.
@@ -261,7 +262,7 @@ public:
* The changed flags for this setting is SettingTooltips.
*/
bool showTooltips() const;
-
+
/**
* The preferred border size selected by the user, e.g. for accessibility
* reasons, or when using high resolution displays. It's up to the decoration
@@ -322,9 +323,9 @@ class KWIN_EXPORT KDecoration
* Destroys the KDecoration.
*/
virtual ~KDecoration();
-
+
// requests from decoration
-
+
/**
* Returns the KDecorationOptions object, which is used to access
* configuration settings for the decoration.
@@ -417,7 +418,7 @@ class KWIN_EXPORT KDecoration
* to support older code). For a description of all window types,
* see the definition of the NET::WindowType type. Note that
* some window types never have decorated windows.
- *
+ *
* An example of usage:
* @code
* const unsigned long supported_types = NET::NormalMask | NET::DesktopMask
@@ -671,7 +672,7 @@ class KWIN_EXPORT KDecoration
/**
* This function is called to reset the decoration on settings changes.
* It is usually invoked by calling KDecorationFactory::resetDecorations().
- *
+ *
* @param changed Specifies which settings were changed, given by the SettingXXX masks
*/
virtual void reset( unsigned long changed );
diff --git a/kwin/manage.cpp b/kwin/manage.cpp
index e56a877b4..df385867a 100644
--- a/kwin/manage.cpp
+++ b/kwin/manage.cpp
@@ -372,6 +372,7 @@ bool Client::manage( Window w, bool isMapped )
setSkipTaskbar( session->skipTaskbar, true );
setSkipPager( session->skipPager );
setShade( session->shaded ? ShadeNormal : ShadeNone );
+ setShadowed( session->shadowed );
if( session->maximized != MaximizeRestore )
{
maximize( (MaximizeMode) session->maximized );
diff --git a/kwin/options.cpp b/kwin/options.cpp
index 5f0190347..cab703051 100644
--- a/kwin/options.cpp
+++ b/kwin/options.cpp
@@ -193,7 +193,24 @@ unsigned long Options::updateSettings()
if (resetKompmgr)
config->writeEntry("ResetKompmgr",FALSE);
-
+ // window drop shadows
+ config->setGroup("Style");
+ shadow_colour = config->readColorEntry("ShadowColour", &Qt::black);
+ shadow_docks = config->readBoolEntry("ShadowDocks", false);
+ shadow_overrides = config->readBoolEntry("ShadowOverrides", false);
+ shadow_topMenus = config->readBoolEntry("ShadowTopMenus", false);
+ shadow_inactive_colour = config->readColorEntry("InactiveShadowColour", &Qt::black);
+ shadow_inactive_enabled = config->readBoolEntry("InactiveShadowEnabled", false);
+ shadow_inactive_opacity = config->readDoubleNumEntry("InactiveShadowOpacity", 0.70);
+ shadow_inactive_thickness = config->readNumEntry("InactiveShadowThickness", 5);
+ shadow_inactive_x_offset = config->readNumEntry("InactiveShadowXOffset", 0);
+ shadow_inactive_y_offset = config->readNumEntry("InactiveShadowYOffset", 5);
+ shadow_enabled = config->readBoolEntry("ShadowEnabled", false);
+ shadow_opacity = config->readDoubleNumEntry("ShadowOpacity", 0.70);
+ shadow_thickness = config->readNumEntry("ShadowThickness", 10);
+ shadow_x_offset = config->readNumEntry("ShadowXOffset", 0);
+ shadow_y_offset = config->readNumEntry("ShadowYOffset", 10);
+
// Read button tooltip animation effect from kdeglobals
// Since we want to allow users to enable window decoration tooltips
@@ -243,6 +260,8 @@ Options::WindowOperation Options::windowOperation(const TQString &name, bool res
return HMaximizeOp;
else if (name == "Lower")
return LowerOp;
+ else if (name == "Shadow")
+ return ShadowOp;
return NoOp;
}
@@ -285,6 +304,69 @@ bool Options::showGeometryTip()
return show_geometry_tip;
}
+TQColor &Options::shadowColour(bool active)
+ {
+ return active ? shadow_colour : shadow_inactive_colour;
+ }
+
+bool Options::shadowWindowType(NET::WindowType t)
+ {
+ bool retval;
+
+ switch (t)
+ {
+ case NET::Dialog:
+ case NET::Normal:
+ retval = true;
+ break;
+ case NET::Desktop:
+ case NET::Menu:
+ case NET::Toolbar:
+ retval = false;
+ break;
+ case NET::Dock:
+ retval = shadow_docks;
+ break;
+ case NET::Override:
+ retval = shadow_overrides;
+ break;
+ case NET::TopMenu:
+ retval = shadow_topMenus;
+ break;
+ default:
+ retval = false;
+ break;
+ }
+
+ return retval;
+ }
+
+bool Options::shadowEnabled(bool active)
+ {
+ return active ? shadow_enabled :
+ (shadow_enabled && shadow_inactive_enabled);
+ }
+
+double Options::shadowOpacity(bool active)
+ {
+ return active ? shadow_opacity : shadow_inactive_opacity;
+ }
+
+int Options::shadowThickness(bool active)
+ {
+ return active ? shadow_thickness : shadow_inactive_thickness;
+ }
+
+int Options::shadowXOffset(bool active)
+ {
+ return active ? shadow_x_offset : shadow_inactive_x_offset;
+ }
+
+int Options::shadowYOffset(bool active)
+ {
+ return active ? shadow_y_offset : shadow_inactive_y_offset;
+ }
+
int Options::electricBorders()
{
return electric_borders;
diff --git a/kwin/options.h b/kwin/options.h
index 82dc6cdc1..dbaa8855f 100644
--- a/kwin/options.h
+++ b/kwin/options.h
@@ -268,6 +268,45 @@ class Options : public KDecorationOptions
*/
bool showGeometryTip();
+ /**
+ * @returns A TQColor representing the colour that window drop shadows should
+ * be.
+ */
+ TQColor &shadowColour(bool active=true);
+
+ /**
+ * @returns true if shadows should be drawn around windows of the
+ * specified type
+ */
+ bool shadowWindowType(NET::WindowType t);
+
+ /**
+ * @returns true if window shadows should be drawn
+ */
+ bool shadowEnabled(bool active=true);
+
+ /**
+ * @returns Window shadow's opacity between 0.01 and 1.00.
+ */
+ double shadowOpacity(bool active=true);
+
+ /**
+ * @returns How thick a shadow should be to either side of of a window.
+ */
+ int shadowThickness(bool active=true);
+
+ /**
+ * @returns Number of pixels along the X-axis by which to offset window
+ * shadows.
+ */
+ int shadowXOffset(bool active=true);
+
+ /**
+ * @returns Number of pixels along the Y-axis by which to offset window
+ * shadows.
+ */
+ int shadowYOffset(bool active=true);
+
enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 };
/**
* @returns true if electric borders are enabled. With electric borders
@@ -336,6 +375,21 @@ class Options : public KDecorationOptions
bool show_geometry_tip;
bool topmenus;
bool desktop_topmenu;
+ TQColor shadow_colour;
+ TQColor shadow_inactive_colour;
+ bool shadow_docks;
+ bool shadow_overrides;
+ bool shadow_topMenus;
+ bool shadow_inactive_enabled;
+ bool shadow_enabled;
+ double shadow_inactive_opacity;
+ double shadow_opacity;
+ int shadow_inactive_thickness;
+ int shadow_thickness;
+ int shadow_inactive_x_offset;
+ int shadow_x_offset;
+ int shadow_inactive_y_offset;
+ int shadow_y_offset;
// List of window classes for which not to use focus stealing prevention
TQStringList ignoreFocusStealingClasses;
diff --git a/kwin/sm.cpp b/kwin/sm.cpp
index d13c6b18b..31cada7e0 100644
--- a/kwin/sm.cpp
+++ b/kwin/sm.cpp
@@ -106,6 +106,7 @@ void Workspace::storeSession( KConfig* config, SMSavePhase phase )
// the config entry is called "sticky" for back. comp. reasons
config->writeEntry( TQString("sticky")+n, c->isOnAllDesktops() );
config->writeEntry( TQString("shaded")+n, c->isShade() );
+ config->writeEntry( TQString("shadowed")+n, c->isShadowed() );
// the config entry is called "staysOnTop" for back. comp. reasons
config->writeEntry( TQString("staysOnTop")+n, c->keepAbove() );
config->writeEntry( TQString("keepBelow")+n, c->keepBelow() );
@@ -172,6 +173,7 @@ void Workspace::loadSessionInfo()
info->minimized = config->readBoolEntry( TQString("iconified")+n, FALSE );
info->onAllDesktops = config->readBoolEntry( TQString("sticky")+n, FALSE );
info->shaded = config->readBoolEntry( TQString("shaded")+n, FALSE );
+ info->shadowed = config->readBoolEntry( TQString("shadowed")+n, TRUE );
info->keepAbove = config->readBoolEntry( TQString("staysOnTop")+n, FALSE );
info->keepBelow = config->readBoolEntry( TQString("keepBelow")+n, FALSE );
info->skipTaskbar = config->readBoolEntry( TQString("skipTaskbar")+n, FALSE );
diff --git a/kwin/sm.h b/kwin/sm.h
index 9321b3745..065ce621d 100644
--- a/kwin/sm.h
+++ b/kwin/sm.h
@@ -39,6 +39,7 @@ struct SessionInfo
bool minimized;
bool onAllDesktops;
bool shaded;
+ bool shadowed;
bool keepAbove;
bool keepBelow;
bool skipTaskbar;
diff --git a/kwin/useractions.cpp b/kwin/useractions.cpp
index 88bfaf035..e825ac846 100644
--- a/kwin/useractions.cpp
+++ b/kwin/useractions.cpp
@@ -65,6 +65,7 @@ TQPopupMenu* Workspace::clientPopup()
advanced_popup->insertItem( SmallIconSet( "window_fullscreen" ),
i18n("&Fullscreen")+'\t'+keys->shortcut("Window Fullscreen").seq(0).toString(), Options::FullScreenOp );
advanced_popup->insertItem( i18n("&No Border")+'\t'+keys->shortcut("Window No Border").seq(0).toString(), Options::NoBorderOp );
+ advanced_popup->insertItem( i18n("Shad&ow"), Options::ShadowOp );
advanced_popup->insertItem( SmallIconSet("key_bindings"),
i18n("Window &Shortcut...")+'\t'+keys->shortcut("Setup Window Shortcut").seq(0).toString(), Options::SetupWindowShortcutOp );
advanced_popup->insertItem( SmallIconSet( "wizard" ), i18n("&Special Window Settings..."), Options::WindowRulesOp );
@@ -172,6 +173,10 @@ void Workspace::clientPopupAboutToShow()
advanced_popup->setItemEnabled( Options::FullScreenOp, active_popup_client->userCanSetFullScreen() );
advanced_popup->setItemChecked( Options::NoBorderOp, active_popup_client->noBorder() );
advanced_popup->setItemEnabled( Options::NoBorderOp, active_popup_client->userCanSetNoBorder() );
+
+ advanced_popup->setItemEnabled( Options::ShadowOp, (options->shadowWindowType(active_popup_client->windowType()) && options->shadowEnabled(active_popup_client->isActive())) );
+ advanced_popup->setItemChecked( Options::ShadowOp, active_popup_client->isShadowed() );
+
popup->setItemEnabled( Options::MinimizeOp, active_popup_client->isMinimizable() );
popup->setItemEnabled( Options::CloseOp, active_popup_client->isCloseable() );
if (options->useTranslucency)
@@ -398,6 +403,9 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op )
case Options::ShadeOp:
c->performMouseCommand( Options::MouseShade, TQCursor::pos());
break;
+ case Options::ShadowOp:
+ c->setShadowed( !c->isShadowed() );
+ break;
case Options::OnAllDesktopsOp:
c->setOnAllDesktops( !c->isOnAllDesktops() );
break;
diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp
index bc8914f5b..fe8ad8faa 100644
--- a/kwin/workspace.cpp
+++ b/kwin/workspace.cpp
@@ -90,6 +90,7 @@ Workspace::Workspace( bool restore )
rules_updates_disabled( false ),
active_client (0),
last_active_client (0),
+ next_active_client (0),
most_recently_raised (0),
movingClient(0),
pending_take_activity ( NULL ),
@@ -699,6 +700,24 @@ void Workspace::updateFocusChains( Client* c, FocusChainChange change )
}
}
+void Workspace::updateOverlappingShadows(unsigned long window)
+ {
+ Client *client;
+
+ if ((client = findClient(WindowMatchPredicate((WId)window))))
+ // Redraw overlapping shadows without waiting for the specified window
+ // to redraw its own shadow
+ client->drawOverlappingShadows(false);
+ }
+
+void Workspace::setShadowed(unsigned long window, bool shadowed)
+ {
+ Client *client;
+
+ if ((client = findClient(WindowMatchPredicate((WId)window))))
+ client->setShadowed(shadowed);
+ }
+
void Workspace::updateCurrentTopMenu()
{
if( !managingTopMenus())
diff --git a/kwin/workspace.h b/kwin/workspace.h
index d369497da..55043276e 100644
--- a/kwin/workspace.h
+++ b/kwin/workspace.h
@@ -231,6 +231,8 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin
void unclutterDesktop();
void doNotManage(TQString);
bool setCurrentDesktop( int new_desktop );
+ void updateOverlappingShadows(WId window);
+ void setShadowed(WId window, bool shadowed);
void nextDesktop();
void previousDesktop();
void circulateDesktopApplications();
@@ -518,6 +520,7 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin
Client* active_client;
Client* last_active_client;
+ Client* next_active_client; // will be active after active_client deactivates
Client* most_recently_raised; // used _only_ by raiseOrLowerClient()
Client* movingClient;
Client* pending_take_activity;
@@ -704,7 +707,15 @@ inline bool Workspace::initializing() const
inline Client* Workspace::activeClient() const
{
- return active_client;
+ // next_active_client is a kludge for drop shadows. If a window that is
+ // activated is not also raised (i.e. when focus follows mouse), then the
+ // newly activated window and its shadow won't cover visual artifacts that
+ // might exist in the inactive window's shadow. We work around this by
+ // (re)drawing the inactive window's shadow after the active window's shadow
+ // is drawn, but to do that the inactive window needs to know which window
+ // will become active next. next_active_client is a Client pointer for that
+ // purpose.
+ return next_active_client != NULL ? next_active_client : active_client;
}
inline Client* Workspace::mostRecentlyActivatedClient() const