From 17655512776a6eac9baa1e087c58abb06493178a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Fri, 5 Jan 2007 16:45:56 +0000 Subject: [PATCH] Support for "input windows" that let effects intercept mouse events when they e.g. want to let the user select from window thumbnails. svn path=/branches/work/kwin_composite/; revision=620305 --- CMakeLists.txt | 1 + composite.cpp | 2 +- effects.cpp | 98 ++++++++++++++++++++++++++++++++++++++++++ effects.h | 15 +++++++ effects/test_input.cpp | 70 ++++++++++++++++++++++++++++++ effects/test_input.h | 40 +++++++++++++++++ events.cpp | 5 +++ unmanaged.cpp | 3 ++ 8 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 effects/test_input.cpp create mode 100644 effects/test_input.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3adf30b6c6..9519b30c46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ set(kwin_KDEINIT_SRCS effects/dialogparent.cpp effects/showfps.cpp effects/zoom.cpp + effects/test_input.cpp ) qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml workspace.h KWinInternal::Workspace ) diff --git a/composite.cpp b/composite.cpp index 710ffa00d4..8159816928 100644 --- a/composite.cpp +++ b/composite.cpp @@ -117,7 +117,7 @@ void Workspace::setupCompositing() kDebug( 1212 ) << "XRender compositing" << endl; else if( dynamic_cast< SceneBasic* >( scene )) kDebug( 1212 ) << "X compositing" << endl; - effects = new EffectsHandler( this ); + new EffectsHandler( this ); // sets also the 'effects' pointer addDamageFull(); foreach( Client* c, clients ) c->setupCompositing(); diff --git a/effects.cpp b/effects.cpp index 9ed76b4d68..9779d5613e 100644 --- a/effects.cpp +++ b/effects.cpp @@ -24,6 +24,8 @@ License. See the file "COPYING" for the exact licensing terms. #include "effects/showfps.h" #include "effects/zoom.h" +#include "effects/test_input.h" + namespace KWinInternal { @@ -51,6 +53,10 @@ void Effect::windowActivated( Toplevel* ) { } +void Effect::windowInputMouseEvent( Window, QEvent* ) + { + } + void Effect::prePaintScreen( int* mask, QRegion* region, int time ) { effects->prePaintScreen( mask, region, time ); @@ -91,6 +97,7 @@ EffectsHandler::EffectsHandler( Workspace* ws ) { if( !compositing()) return; + KWinInternal::effects = this; effects.append( new ShowFpsEffect( ws )); // effects.append( new ZoomEffect( ws )); // effects.append( new HowtoEffect ); @@ -100,12 +107,16 @@ EffectsHandler::EffectsHandler( Workspace* ws ) // effects.append( new FadeInEffect ); // effects.append( new ScaleInEffect ); // effects.append( new DialogParentEffect ); + +// effects.append( new TestInputEffect ); } EffectsHandler::~EffectsHandler() { foreach( Effect* e, effects ) delete e; + foreach( InputWindowPair pos, input_windows ) + XDestroyWindow( display(), pos.second ); } void EffectsHandler::windowUserMovedResized( Toplevel* c, bool first, bool last ) @@ -202,6 +213,93 @@ void EffectsHandler::postPaintWindow( Scene::Window* w ) // no special final code } +Window EffectsHandler::createInputWindow( Effect* e, int x, int y, int w, int h, const QCursor& cursor ) + { + XSetWindowAttributes attrs; + attrs.override_redirect = True; + Window win = XCreateWindow( display(), rootWindow(), x, y, w, h, 0, 0, InputOnly, CopyFromParent, + CWOverrideRedirect, &attrs ); +// TODO keeping on top? +// TODO enter/leave notify? + XSelectInput( display(), win, ButtonPressMask | ButtonReleaseMask | PointerMotionMask ); + XDefineCursor( display(), win, cursor.handle()); + XMapWindow( display(), win ); + input_windows.append( qMakePair( e, win )); + return win; + } + +Window EffectsHandler::createInputWindow( Effect* e, const QRect& r, const QCursor& cursor ) + { + return createInputWindow( e, r.x(), r.y(), r.width(), r.height(), cursor ); + } + +void EffectsHandler::destroyInputWindow( Window w ) + { + foreach( InputWindowPair pos, input_windows ) + { + if( pos.second == w ) + { + input_windows.removeAll( pos ); + XDestroyWindow( display(), w ); + return; + } + } + assert( false ); + } + +bool EffectsHandler::checkInputWindowEvent( XEvent* e ) + { + if( e->type != ButtonPress && e->type != ButtonRelease && e->type != MotionNotify ) + return false; + foreach( InputWindowPair pos, input_windows ) + { + if( pos.second == e->xany.window ) + { + switch( e->type ) + { + case ButtonPress: + case ButtonRelease: + { + XButtonEvent* e2 = &e->xbutton; + QMouseEvent ev( e->type == ButtonPress ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease, + QPoint( e2->x, e2->y ), QPoint( e2->x_root, e2->y_root ), x11ToQtMouseButton( e2->button ), + x11ToQtMouseButtons( e2->state ), x11ToQtKeyboardModifiers( e2->state )); + pos.first->windowInputMouseEvent( pos.second, &ev ); + break; // ---> + } + case MotionNotify: + { + XMotionEvent* e2 = &e->xmotion; + QMouseEvent ev( QEvent::MouseMove, QPoint( e2->x, e2->y ), QPoint( e2->x_root, e2->y_root ), + Qt::NoButton, x11ToQtMouseButtons( e2->state ), x11ToQtKeyboardModifiers( e2->state )); + pos.first->windowInputMouseEvent( pos.second, &ev ); + break; // ---> + } + } + return true; // eat event + } + } + return false; + } + +void EffectsHandler::checkInputWindowStacking() + { + if( input_windows.count() == 0 ) + return; + Window* wins = new Window[ input_windows.count() ]; + int pos = 0; + foreach( InputWindowPair it, input_windows ) + wins[ pos++ ] = it.second; + XRaiseWindow( display(), wins[ 0 ] ); + XRestackWindows( display(), wins, pos ); + delete[] wins; + } + +void EffectsHandler::activateWindow( Client* c ) + { + Workspace::self()->activateClient( c, true ); + } + EffectsHandler* effects; } // namespace diff --git a/effects.h b/effects.h index 6c9fc1a8ad..2f0d235a13 100644 --- a/effects.h +++ b/effects.h @@ -15,6 +15,8 @@ License. See the file "COPYING" for the exact licensing terms. #include "scene.h" +#include + namespace KWinInternal { @@ -73,6 +75,7 @@ class Effect virtual void windowAdded( Toplevel* c ); virtual void windowDeleted( Toplevel* c ); virtual void windowActivated( Toplevel* c ); + virtual void windowInputMouseEvent( Window w, QEvent* e ); }; class EffectsHandler @@ -87,14 +90,26 @@ class EffectsHandler void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time ); void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data ); void postPaintWindow( Scene::Window* w ); + // Functions for handling input - e.g. when an Expose-like effect is shown, an input window + // covering the whole screen is created and all mouse events will be intercepted by it. + // The effect's windowInputMouseEvent() will get called with such events. + Window createInputWindow( Effect* e, int x, int y, int w, int h, const QCursor& cursor ); + Window createInputWindow( Effect* e, const QRect& r, const QCursor& cursor ); + void destroyInputWindow( Window w ); + // functions that allow controlling windows/desktop + void activateWindow( Client* c ); // internal (used by kwin core or compositing code) void startPaint(); void windowUserMovedResized( Toplevel* c, bool first, bool last ); void windowAdded( Toplevel* c ); void windowDeleted( Toplevel* c ); void windowActivated( Toplevel* c ); + bool checkInputWindowEvent( XEvent* e ); + void checkInputWindowStacking(); private: QVector< Effect* > effects; + typedef QPair< Effect*, Window > InputWindowPair; + QList< InputWindowPair > input_windows; int current_paint_window; int current_paint_screen; }; diff --git a/effects/test_input.cpp b/effects/test_input.cpp new file mode 100644 index 0000000000..26391e5810 --- /dev/null +++ b/effects/test_input.cpp @@ -0,0 +1,70 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2007 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + Testing of handling input in effects. This testing effect moves all windows + by 100 pixels down, creates an input window that'll intercept all mouse events + and activates the window that's been clicked (click position needs to be + transformed). This is useful for effects that present something on the screen + and let the user interact with it (e.g. a list of window thumbnails and the + user can activate the window by clicking its thumbnail). + +*/ + +#include "test_input.h" + +#include +#include + +namespace KWinInternal +{ + +TestInputEffect::TestInputEffect() + { + input = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(), Qt::CrossCursor ); + } + +TestInputEffect::~TestInputEffect() + { + effects->destroyInputWindow( input ); + } + +void TestInputEffect::prePaintScreen( int* mask, QRegion* region, int time ) + { + *mask |= Scene::PAINT_SCREEN_TRANSFORMED; + effects->prePaintScreen( mask, region, time ); + } + +void TestInputEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) + { + data.yTranslate += 100; + effects->paintScreen( mask, region, data ); + } + +void TestInputEffect::windowInputMouseEvent( Window w, QEvent* e ) + { + assert( w == input ); + if( e->type() != QEvent::MouseButtonPress ) + return; + QPoint pos = static_cast< QMouseEvent* >( e )->pos(); + pos -= QPoint( 0, 100 ); // adjust for transformation + foreach( Client* c, Workspace::self()->stackingOrder()) + { + if( c->isShown( true ) && c->isOnCurrentDesktop() + && c->geometry().contains( pos )) + { + effects->activateWindow( c ); + return; + } + } + } + +} // namespace diff --git a/effects/test_input.h b/effects/test_input.h new file mode 100644 index 0000000000..eb8307b338 --- /dev/null +++ b/effects/test_input.h @@ -0,0 +1,40 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2007 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + Testing of handling input in effects. + +*/ + +#ifndef KWIN_TEST_INPUT_H +#define KWIN_TEST_INPUT_H + +#include + +namespace KWinInternal +{ + +class TestInputEffect + : public Effect + { + public: + TestInputEffect(); + virtual ~TestInputEffect(); + virtual void prePaintScreen( int* mask, QRegion* region, int time ); + virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data ); + virtual void windowInputMouseEvent( Window w, QEvent* e ); + private: + Window input; + }; + +} // namespace + +#endif diff --git a/events.cpp b/events.cpp index f4c58fa5cf..d29b0b4d62 100644 --- a/events.cpp +++ b/events.cpp @@ -23,6 +23,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "rules.h" #include "unmanaged.h" #include "scene.h" +#include "effects.h" #include #include @@ -220,6 +221,8 @@ bool Workspace::workspaceEvent( XEvent * e ) tab_box->handleMouseEvent( e ); return true; } + if( effects && effects->checkInputWindowEvent( e )) + return true; break; case KeyPress: { @@ -1634,6 +1637,8 @@ void Unmanaged::unmapNotifyEvent( XUnmapEvent* ) void Unmanaged::configureNotifyEvent( XConfigureEvent* e ) { + if( effects ) + effects->checkInputWindowStacking(); // keep them on top QRect newgeom( e->x, e->y, e->width, e->height ); if( newgeom == geom ) return; diff --git a/unmanaged.cpp b/unmanaged.cpp index 3786517da7..546079adc6 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -11,6 +11,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "unmanaged.h" #include "workspace.h" +#include "effects.h" #include @@ -62,6 +63,8 @@ bool Unmanaged::track( Window w ) detectShape( w ); setupCompositing(); ungrabXServer(); + if( effects ) + effects->checkInputWindowStacking(); return true; }