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
This commit is contained in:
Luboš Luňák 2007-01-05 16:45:56 +00:00
parent 2e84c74fc8
commit 1765551277
8 changed files with 233 additions and 1 deletions

View file

@ -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 )

View file

@ -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();

View file

@ -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

View file

@ -15,6 +15,8 @@ License. See the file "COPYING" for the exact licensing terms.
#include "scene.h"
#include <qpair.h>
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;
};

70
effects/test_input.cpp Normal file
View file

@ -0,0 +1,70 @@
/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
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 <workspace.h>
#include <client.h>
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

40
effects/test_input.h Normal file
View file

@ -0,0 +1,40 @@
/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
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 <effects.h>
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

View file

@ -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 <QWhatsThis>
#include <QApplication>
@ -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;

View file

@ -11,6 +11,7 @@ License. See the file "COPYING" for the exact licensing terms.
#include "unmanaged.h"
#include "workspace.h"
#include "effects.h"
#include <X11/extensions/shape.h>
@ -62,6 +63,8 @@ bool Unmanaged::track( Window w )
detectShape( w );
setupCompositing();
ungrabXServer();
if( effects )
effects->checkInputWindowStacking();
return true;
}