1
/*****************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
8
You can Freely distribute this program under the GNU General Public
9
License. See the file "COPYING" for the exact licensing terms.
10
******************************************************************/
14
This file contains things relevant to handling incoming events.
18
#include <config-X11.h>
21
#include "workspace.h"
26
#include "unmanaged.h"
31
#include <QApplication>
33
#include <kkeyserver.h>
35
#include <X11/extensions/shape.h>
36
#include <X11/Xatom.h>
40
#include <X11/extensions/Xrandr.h>
46
// ****************************************
48
// ****************************************
50
WinInfo::WinInfo( Client * c, Display * display, Window window,
51
Window rwin, const unsigned long pr[], int pr_size )
52
: NETWinInfo( display, window, rwin, pr, pr_size, NET::WindowManager ), m_client( c )
56
void WinInfo::changeDesktop(int desktop)
58
m_client->workspace()->sendClientToDesktop( m_client, desktop, true );
61
void WinInfo::changeState( unsigned long state, unsigned long mask )
63
mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore
64
mask &= ~NET::Hidden; // clients are not allowed to change this directly
65
state &= mask; // for safety, clear all other bits
67
if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) == 0 )
68
m_client->setFullScreen( false, false );
69
if ( (mask & NET::Max) == NET::Max )
70
m_client->setMaximize( state & NET::MaxVert, state & NET::MaxHoriz );
71
else if ( mask & NET::MaxVert )
72
m_client->setMaximize( state & NET::MaxVert, m_client->maximizeMode() & Client::MaximizeHorizontal );
73
else if ( mask & NET::MaxHoriz )
74
m_client->setMaximize( m_client->maximizeMode() & Client::MaximizeVertical, state & NET::MaxHoriz );
76
if ( mask & NET::Shaded )
77
m_client->setShade( state & NET::Shaded ? ShadeNormal : ShadeNone );
78
if ( mask & NET::KeepAbove)
79
m_client->setKeepAbove( (state & NET::KeepAbove) != 0 );
80
if ( mask & NET::KeepBelow)
81
m_client->setKeepBelow( (state & NET::KeepBelow) != 0 );
82
if( mask & NET::SkipTaskbar )
83
m_client->setSkipTaskbar( ( state & NET::SkipTaskbar ) != 0, true );
84
if( mask & NET::SkipPager )
85
m_client->setSkipPager( ( state & NET::SkipPager ) != 0 );
86
if( mask & NET::DemandsAttention )
87
m_client->demandAttention(( state & NET::DemandsAttention ) != 0 );
88
if( mask & NET::Modal )
89
m_client->setModal( ( state & NET::Modal ) != 0 );
90
// unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() )
91
if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) != 0 )
92
m_client->setFullScreen( true, false );
95
void WinInfo::disable()
97
m_client = NULL; // only used when the object is passed to Deleted
100
// ****************************************
102
// ****************************************
104
RootInfo::RootInfo( Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr[], int pr_num, int scr )
105
: NETRootInfo( dpy, w, name, pr, pr_num, scr )
110
void RootInfo::changeNumberOfDesktops(int n)
112
workspace->setNumberOfDesktops( n );
115
void RootInfo::changeCurrentDesktop(int d)
117
workspace->setCurrentDesktop( d );
120
void RootInfo::changeActiveWindow( Window w, NET::RequestSource src, Time timestamp, Window active_window )
122
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
124
if( timestamp == CurrentTime )
125
timestamp = c->userTime();
126
if( src != NET::FromApplication && src != FromTool )
128
if( src == NET::FromTool )
129
workspace->activateClient( c, true ); // force
130
else // NET::FromApplication
133
if( workspace->allowClientActivation( c, timestamp ))
134
workspace->activateClient( c );
135
// if activation of the requestor's window would be allowed, allow activation too
136
else if( active_window != None
137
&& ( c2 = workspace->findClient( WindowMatchPredicate( active_window ))) != NULL
138
&& workspace->allowClientActivation( c2,
139
timestampCompare( timestamp, c2->userTime() > 0 ? timestamp : c2->userTime())))
140
workspace->activateClient( c );
142
c->demandAttention();
147
void RootInfo::restackWindow( Window w, RequestSource src, Window above, int detail, Time timestamp )
149
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
151
if( timestamp == CurrentTime )
152
timestamp = c->userTime();
153
if( src != NET::FromApplication && src != FromTool )
155
c->restackWindow( above, detail, src, timestamp, true );
159
void RootInfo::gotTakeActivity( Window w, Time timestamp, long flags )
161
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
162
workspace->handleTakeActivity( c, timestamp, flags );
165
void RootInfo::closeWindow(Window w)
167
Client* c = workspace->findClient( WindowMatchPredicate( w ));
172
void RootInfo::moveResize(Window w, int x_root, int y_root, unsigned long direction)
174
Client* c = workspace->findClient( WindowMatchPredicate( w ));
177
updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp
178
c->NETMoveResize( x_root, y_root, (Direction)direction);
182
void RootInfo::moveResizeWindow(Window w, int flags, int x, int y, int width, int height )
184
Client* c = workspace->findClient( WindowMatchPredicate( w ));
186
c->NETMoveResizeWindow( flags, x, y, width, height );
189
void RootInfo::gotPing( Window w, Time timestamp )
191
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
192
c->gotPing( timestamp );
195
void RootInfo::changeShowingDesktop( bool showing )
197
workspace->setShowingDesktop( showing );
200
// ****************************************
202
// ****************************************
205
Handles workspace specific XEvents
207
bool Workspace::workspaceEvent( XEvent * e )
209
if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) )
211
mouse_emulation = false;
214
if( effects && static_cast< EffectsHandlerImpl* >( effects )->hasKeyboardGrab()
215
&& ( e->type == KeyPress || e->type == KeyRelease ))
216
return false; // let Qt process it, it'll be intercepted again in eventFilter()
218
if( e->type == PropertyNotify || e->type == ClientMessage )
220
unsigned long dirty[ NETRootInfo::PROPERTIES_SIZE ];
221
rootInfo->event( e, dirty, NETRootInfo::PROPERTIES_SIZE );
222
if( dirty[ NETRootInfo::PROTOCOLS ] & NET::DesktopNames )
223
saveDesktopSettings();
224
if( dirty[ NETRootInfo::PROTOCOLS2 ] & NET::WM2DesktopLayout )
225
updateDesktopLayout();
228
// events that should be handled before Clients can get them
233
was_user_interaction = true;
236
if ( tab_grab || control_grab )
238
tab_box->handleMouseEvent( e );
241
if( effects && static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent( e ))
246
was_user_interaction = true;
248
KKeyServer::xEventToQt(e, &keyQt);
249
kDebug(125) << "Workspace::keyPress( " << keyQt << " )";
252
movingClient->keyPressEvent(keyQt);
255
if( tab_grab || control_grab )
257
tabBoxKeyPress( keyQt );
263
was_user_interaction = true;
264
if( tab_grab || control_grab )
266
tabBoxKeyRelease( e->xkey );
272
if( Client* c = findClient( WindowMatchPredicate( e->xany.window )))
274
if( c->windowEvent( e ))
277
else if( Client* c = findClient( WrapperIdMatchPredicate( e->xany.window )))
279
if( c->windowEvent( e ))
282
else if( Client* c = findClient( FrameIdMatchPredicate( e->xany.window )))
284
if( c->windowEvent( e ))
287
else if( Unmanaged* c = findUnmanaged( WindowMatchPredicate( e->xany.window )))
289
if( c->windowEvent( e ))
294
Window special = findSpecialEventWindow( e );
295
if( special != None )
296
if( Client* c = findClient( WindowMatchPredicate( special )))
298
if( c->windowEvent( e ))
302
if( movingClient != NULL && movingClient->moveResizeGrabWindow() == e->xany.window
303
&& ( e->type == MotionNotify || e->type == ButtonPress || e->type == ButtonRelease ))
305
if( movingClient->windowEvent( e ))
312
if ( e->xcreatewindow.parent == rootWindow() &&
313
!QWidget::find( e->xcreatewindow.window) &&
314
!e->xcreatewindow.override_redirect )
316
// see comments for allowClientActivation()
318
XChangeProperty(display(), e->xcreatewindow.window,
319
atoms->kde_net_wm_user_creation_time, XA_CARDINAL,
320
32, PropModeReplace, (unsigned char *)&t, 1);
326
return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt
330
//do not confuse Qt with these events. After all, _we_ are the
331
//window manager who does the reparenting.
342
// e->xmaprequest.window is different from e->xany.window
343
// TODO this shouldn't be necessary now
344
Client* c = findClient( WindowMatchPredicate( e->xmaprequest.window ));
347
// don't check for the parent being the root window, this breaks when some app unmaps
348
// a window, changes something and immediately maps it back, without giving KWin
349
// a chance to reparent it back to root
350
// since KWin can get MapRequest only for root window children and
351
// children of WindowWrapper (=clients), the check is AFAIK useless anyway
352
// Note: Now the save-set support in Client::mapRequestEvent() actually requires that
353
// this code doesn't check the parent to be root.
354
// if ( e->xmaprequest.parent == root ) {
355
c = createClient( e->xmaprequest.window, false );
356
if( c == NULL ) // refused to manage, simply map it (most probably override redirect)
357
XMapRaised( display(), e->xmaprequest.window );
363
updateFocusChains( c, FocusChainUpdate );
370
if( e->xmap.override_redirect )
372
Unmanaged* c = findUnmanaged( WindowMatchPredicate( e->xmap.window ));
374
c = createUnmanaged( e->xmap.window );
376
return c->windowEvent( e );
378
return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt
383
if ( QWhatsThis::inWhatsThisMode() )
385
QWidget* w = QWidget::find( e->xcrossing.window );
387
QWhatsThis::leaveWhatsThisMode();
389
if( electricBorderEvent(e))
395
if ( !QWhatsThis::inWhatsThisMode() )
397
// TODO is this cliente ever found, given that client events are searched above?
398
Client* c = findClient( FrameIdMatchPredicate( e->xcrossing.window ));
399
if ( c && e->xcrossing.detail != NotifyInferior )
400
QWhatsThis::leaveWhatsThisMode();
403
case ConfigureRequest:
405
if ( e->xconfigurerequest.parent == rootWindow())
408
wc.border_width = e->xconfigurerequest.border_width;
409
wc.x = e->xconfigurerequest.x;
410
wc.y = e->xconfigurerequest.y;
411
wc.width = e->xconfigurerequest.width;
412
wc.height = e->xconfigurerequest.height;
414
wc.stack_mode = Above;
415
unsigned int value_mask = e->xconfigurerequest.value_mask
416
& ( CWX | CWY | CWWidth | CWHeight | CWBorderWidth );
417
XConfigureWindow( display(), e->xconfigurerequest.window, value_mask, &wc );
423
if ( mouse_emulation )
424
return keyPressMouseEmulation( e->xkey );
427
if ( mouse_emulation )
431
if( e->xfocus.window == rootWindow()
432
&& ( e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot ))
434
updateXTime(); // focusToNull() uses xTime(), which is old now (FocusIn has no timestamp)
437
XGetInputFocus( display(), &focus, &revert );
438
if( focus == None || focus == PointerRoot )
440
//kWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" ;
441
Client *c = mostRecentlyActivatedClient();
443
requestFocus( c, true );
444
else if( activateNextClient( NULL ))
452
return true; // always eat these, they would tell Qt that KWin is the active app
454
if( electricBorderEvent( e ))
458
if( e->xexpose.window == rootWindow() && compositing()) // root window needs repainting
459
addRepaint( e->xexpose.x, e->xexpose.y, e->xexpose.width, e->xexpose.height );
462
if( e->type == Extensions::randrNotifyEvent() && Extensions::randrAvailable() )
465
XRRUpdateConfiguration( e );
469
// desktopResized() should take care of when the size or
470
// shape of the desktop has changed, but we also want to
471
// catch refresh rate changes
473
QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
476
else if( e->type == Extensions::syncAlarmNotifyEvent() && Extensions::syncAvailable())
479
foreach( Client* c, clients )
480
c->syncEvent( reinterpret_cast< XSyncAlarmNotifyEvent* >( e ));
481
foreach( Client* c, desktops )
482
c->syncEvent( reinterpret_cast< XSyncAlarmNotifyEvent* >( e ));
490
// Used only to filter events that need to be processed by Qt first
491
// (e.g. keyboard input to be composed), otherwise events are
492
// handle by the XEvent filter above
493
bool Workspace::workspaceEvent( QEvent* e )
495
if(( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease || e->type() == QEvent::ShortcutOverride )
496
&& effects && static_cast< EffectsHandlerImpl* >( effects )->hasKeyboardGrab())
498
static_cast< EffectsHandlerImpl* >( effects )->grabbedKeyboardEvent( static_cast< QKeyEvent* >( e ));
504
// Some events don't have the actual window which caused the event
505
// as e->xany.window (e.g. ConfigureRequest), but as some other
506
// field in the XEvent structure.
507
Window Workspace::findSpecialEventWindow( XEvent* e )
512
return e->xcreatewindow.window;
514
return e->xdestroywindow.window;
516
return e->xunmap.window;
518
return e->xmap.window;
520
return e->xmaprequest.window;
522
return e->xreparent.window;
523
case ConfigureNotify:
524
return e->xconfigure.window;
526
return e->xgravity.window;
527
case ConfigureRequest:
528
return e->xconfigurerequest.window;
529
case CirculateNotify:
530
return e->xcirculate.window;
531
case CirculateRequest:
532
return e->xcirculaterequest.window;
538
// ****************************************
540
// ****************************************
543
General handler for XEvents concerning the client window
545
bool Client::windowEvent( XEvent* e )
547
if( e->xany.window == window()) // avoid doing stuff on frame or wrapper
549
unsigned long dirty[ 2 ];
550
double old_opacity = opacity();
551
info->event( e, dirty, 2 ); // pass through the NET stuff
553
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 )
555
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName ) != 0 )
557
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut ) != 0
558
|| ( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut ) != 0 )
560
if( isTopMenu()) // the fallback mode of KMenuBar may alter the strut
561
checkWorkspacePosition(); // restore it
562
workspace()->updateClientArea();
564
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
566
// Note there's a difference between userTime() and info->userTime()
567
// info->userTime() is the value of the property, userTime() also includes
568
// updates of the time done by KWin (ButtonPress on windowrapper etc.).
569
if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime ) != 0 )
571
workspace()->setWasUserInteraction();
572
updateUserTime( info->userTime());
574
if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
576
if( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry )
578
if( demandAttentionKNotifyTimer != NULL )
579
demandAttentionKNotify();
581
if( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2Opacity )
586
scene->windowOpacityChanged( this );
588
static_cast<EffectsHandlerImpl*>(effects)->windowOpacityChanged( effectWindow(), old_opacity );
591
{ // forward to the frame if there's possibly another compositing manager running
592
NETWinInfo i( display(), frameId(), rootWindow(), 0 );
593
i.setOpacity( info->opacity());
601
unmapNotifyEvent( &e->xunmap );
604
destroyNotifyEvent( &e->xdestroywindow );
607
// this one may pass the event to workspace
608
return mapRequestEvent( &e->xmaprequest );
609
case ConfigureRequest:
610
configureRequestEvent( &e->xconfigurerequest );
613
propertyNotifyEvent( &e->xproperty );
617
workspace()->setWasUserInteraction();
621
workspace()->setWasUserInteraction();
622
buttonPressEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
623
e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
626
// don't update user time on releases
627
// e.g. if the user presses Alt+F2, the Alt release
628
// would appear as user input to the currently active window
631
// don't update user time on releases
632
// e.g. if the user presses Alt+F2, the Alt release
633
// would appear as user input to the currently active window
634
buttonReleaseEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
635
e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
638
motionNotifyEvent( e->xmotion.window, e->xmotion.state,
639
e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root );
640
workspace()->updateFocusMousePosition( QPoint( e->xmotion.x_root, e->xmotion.y_root ));
643
enterNotifyEvent( &e->xcrossing );
644
// MotionNotify is guaranteed to be generated only if the mouse
645
// move start and ends in the window; for cases when it only
646
// starts or only ends there, Enter/LeaveNotify are generated.
647
// Fake a MotionEvent in such cases to make handle of mouse
648
// events simpler (Qt does that too).
649
motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
650
e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
651
workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ));
654
motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
655
e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
656
leaveNotifyEvent( &e->xcrossing );
657
// not here, it'd break following enter notify handling
658
// workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ));
661
focusInEvent( &e->xfocus );
664
focusOutEvent( &e->xfocus );
669
clientMessageEvent( &e->xclient );
671
case ColormapChangeMask:
672
if( e->xany.window == window())
674
cmap = e->xcolormap.colormap;
676
workspace()->updateColormap();
680
if( e->xany.window == window())
682
if( e->type == Extensions::shapeNotifyEvent() )
684
detectShape( window()); // workaround for #19644
688
if( e->xany.window == frameId())
691
if( e->type == Extensions::damageNotifyEvent())
692
damageNotifyEvent( reinterpret_cast< XDamageNotifyEvent* >( e ));
697
return true; // eat all events
701
Handles map requests of the client window
703
bool Client::mapRequestEvent( XMapRequestEvent* e )
705
if( e->window != window())
707
// Special support for the save-set feature, which is a bit broken.
708
// If there's a window from one client embedded in another one,
709
// e.g. using XEMBED, and the embedder suddenly looses its X connection,
710
// save-set will reparent the embedded window to its closest ancestor
711
// that will remains. Unfortunately, with reparenting window managers,
712
// this is not the root window, but the frame (or in KWin's case,
713
// it's the wrapper for the client window). In this case,
714
// the wrapper will get ReparentNotify for a window it won't know,
715
// which will be ignored, and then it gets MapRequest, as save-set
716
// always maps. Returning true here means that Workspace::workspaceEvent()
717
// will handle this MapRequest and manage this window (i.e. act as if
718
// it was reparented to root window).
719
if( e->parent == wrapperId())
721
return true; // no messing with frame etc.
723
if( isTopMenu() && workspace()->managingTopMenus())
724
return true; // kwin controls these
725
switch ( mappingState() )
728
assert( false ); // WMs are not supposed to manage clients in Withdrawn state,
729
// manage(); // after initial mapping manage() is called from createClient()
732
// also copied in clientMessage()
736
setShade( ShadeNone );
737
if( !isOnCurrentDesktop())
739
if( workspace()->allowClientActivation( this ))
740
workspace()->activateClient( this );
746
// TODO fake MapNotify?
753
Handles unmap notify events of the client window
755
void Client::unmapNotifyEvent( XUnmapEvent* e )
757
if( e->window != window())
759
if( e->event != wrapperId())
760
{ // most probably event from root window when initially reparenting
762
if( e->event == rootWindow() && e->send_event )
763
ignore = false; // XWithdrawWindow()
767
switch( mappingState())
773
// maybe we will be destroyed soon. Check this first.
775
if( XCheckTypedWindowEvent (display(), window(),
776
DestroyNotify, &ev) ) // TODO I don't like this much
778
destroyClient(); // deletes this
788
void Client::destroyNotifyEvent( XDestroyWindowEvent* e )
790
if( e->window != window())
797
Handles client messages for the client window
799
void Client::clientMessageEvent( XClientMessageEvent* e )
801
if( e->window != window())
802
return; // ignore frame/wrapper
804
if ( e->message_type == atoms->kde_wm_change_state )
806
if( isTopMenu() && workspace()->managingTopMenus())
807
return; // kwin controls these
808
bool avoid_animation = ( e->data.l[ 1 ] );
809
if( e->data.l[ 0 ] == IconicState )
811
else if( e->data.l[ 0 ] == NormalState )
812
{ // copied from mapRequest()
814
unminimize( avoid_animation );
816
setShade( ShadeNone );
817
if( !isOnCurrentDesktop())
819
if( workspace()->allowClientActivation( this ))
820
workspace()->activateClient( this );
826
else if ( e->message_type == atoms->wm_change_state)
828
if( isTopMenu() && workspace()->managingTopMenus())
829
return; // kwin controls these
830
if ( e->data.l[0] == IconicState )
838
Handles configure requests of the client window
840
void Client::configureRequestEvent( XConfigureRequestEvent* e )
842
if( e->window != window())
843
return; // ignore frame/wrapper
844
if ( isResize() || isMove())
845
return; // we have better things to do right now
847
if( fullscreen_mode == FullScreenNormal ) // refuse resizing of fullscreen windows
848
{ // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode
849
sendSyntheticConfigureNotify();
852
if( isSplash() // no manipulations with splashscreens either
853
|| isTopMenu()) // topmenus neither
855
sendSyntheticConfigureNotify();
859
if ( e->value_mask & CWBorderWidth )
861
// first, get rid of a window border
863
unsigned int value_mask = 0;
866
value_mask = CWBorderWidth;
867
XConfigureWindow( display(), window(), value_mask, & wc );
870
if( e->value_mask & ( CWX | CWY | CWHeight | CWWidth ))
871
configureRequest( e->value_mask, e->x, e->y, e->width, e->height, 0, false );
873
if ( e->value_mask & CWStackMode )
874
restackWindow( e->above, e->detail, NET::FromApplication, userTime(), false );
876
// Sending a synthetic configure notify always is fine, even in cases where
877
// the ICCCM doesn't require this - it can be though of as 'the WM decided to move
878
// the window later'. The client should not cause that many configure request,
879
// so this should not have any significant impact. With user moving/resizing
880
// the it should be optimized though (see also Client::setGeometry()/plainResize()/move()).
881
sendSyntheticConfigureNotify();
883
// SELI TODO accept configure requests for isDesktop windows (because kdesktop
884
// may get XRANDR resize event before kwin), but check it's still at the bottom?
889
Handles property changes of the client window
891
void Client::propertyNotifyEvent( XPropertyEvent* e )
893
Toplevel::propertyNotifyEvent( e );
894
if( e->window != window())
895
return; // ignore frame/wrapper
898
case XA_WM_NORMAL_HINTS:
904
case XA_WM_ICON_NAME:
907
case XA_WM_TRANSIENT_FOR:
912
getIcons(); // because KWin::icon() uses WMHints as fallback
915
if ( e->atom == atoms->wm_protocols )
916
getWindowProtocols();
917
else if( e->atom == atoms->motif_wm_hints )
919
else if( e->atom == atoms->net_wm_sync_request_counter )
926
void Client::enterNotifyEvent( XCrossingEvent* e )
928
if( e->window != frameId())
929
return; // care only about entering the whole frame
930
if( e->mode == NotifyNormal ||
931
( !options->focusPolicyIsReasonable() &&
932
e->mode == NotifyUngrab ) )
935
if (options->shadeHover && isShade())
937
delete shadeHoverTimer;
938
shadeHoverTimer = new QTimer( this );
939
connect( shadeHoverTimer, SIGNAL( timeout() ), this, SLOT( shadeHover() ));
940
shadeHoverTimer->setSingleShot( true );
941
shadeHoverTimer->start( options->shadeHoverInterval );
944
if ( options->focusPolicy == Options::ClickToFocus )
947
if ( options->autoRaise && !isDesktop() &&
948
!isDock() && !isTopMenu() && workspace()->focusChangeEnabled() &&
949
workspace()->topClientOnDesktop( workspace()->currentDesktop()) != this )
951
delete autoRaiseTimer;
952
autoRaiseTimer = new QTimer( this );
953
connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) );
954
autoRaiseTimer->setSingleShot( true );
955
autoRaiseTimer->start( options->autoRaiseInterval );
958
QPoint currentPos( e->x_root, e->y_root );
959
if ( options->focusPolicy != Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) )
961
// for FocusFollowsMouse, change focus only if the mouse has actually been moved, not if the focus
962
// change came because of window changes (e.g. closing a window) - #92290
963
if( options->focusPolicy != Options::FocusFollowsMouse
964
|| currentPos != workspace()->focusMousePosition())
966
if ( options->delayFocus )
967
workspace()->requestDelayFocus( this );
969
workspace()->requestFocus( this );
975
void Client::leaveNotifyEvent( XCrossingEvent* e )
977
if( e->window != frameId())
978
return; // care only about leaving the whole frame
979
if ( e->mode == NotifyNormal )
983
mode = PositionCenter;
986
bool lostMouse = !rect().contains( QPoint( e->x, e->y ) );
987
// 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations
988
// (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event
989
// comes after leaving the rect) - so lets check if the pointer is really outside the window
991
// TODO this still sucks if a window appears above this one - it should lose the mouse
992
// if this window is another client, but not if it's a popup ... maybe after KDE3.1 :(
993
// (repeat after me 'AARGHL!')
994
if ( !lostMouse && e->detail != NotifyInferior )
999
if( XQueryPointer( display(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5 ) == False
1001
lostMouse = true; // really lost the mouse
1006
workspace()->cancelDelayFocus();
1008
if ( shade_mode == ShadeHover && !moveResizeMode && !buttonDown )
1009
setShade( ShadeNormal );
1011
if ( options->focusPolicy == Options::FocusStrictlyUnderMouse )
1012
if ( isActive() && lostMouse )
1013
workspace()->requestFocus( 0 ) ;
1018
#define XCapL KKeyServer::modXLock()
1019
#define XNumL KKeyServer::modXNumLock()
1020
#define XScrL KKeyServer::modXScrollLock()
1021
void Client::grabButton( int modifier )
1023
unsigned int mods[ 8 ] =
1025
0, XCapL, XNumL, XNumL | XCapL,
1026
XScrL, XScrL | XCapL,
1027
XScrL | XNumL, XScrL | XNumL | XCapL
1032
XGrabButton( display(), AnyButton,
1033
modifier | mods[ i ],
1034
wrapperId(), false, ButtonPressMask,
1035
GrabModeSync, GrabModeAsync, None, None );
1038
void Client::ungrabButton( int modifier )
1040
unsigned int mods[ 8 ] =
1042
0, XCapL, XNumL, XNumL | XCapL,
1043
XScrL, XScrL | XCapL,
1044
XScrL | XNumL, XScrL | XNumL | XCapL
1049
XUngrabButton( display(), AnyButton,
1050
modifier | mods[ i ], wrapperId());
1057
Releases the passive grab for some modifier combinations when a
1058
window becomes active. This helps broken X programs that
1059
missinterpret LeaveNotify events in grab mode to work properly
1060
(Motif, AWT, Tk, ...)
1062
void Client::updateMouseGrab()
1064
if( workspace()->globalShortcutsDisabled())
1066
XUngrabButton( display(), AnyButton, AnyModifier, wrapperId());
1067
// keep grab for the simple click without modifiers if needed (see below)
1068
bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this;
1069
if( !( !options->clickRaise || not_obscured ))
1073
if( isActive() && !workspace()->forcedGlobalMouseGrab()) // see Workspace::establishTabBoxGrab()
1075
// first grab all modifier combinations
1076
XGrabButton( display(), AnyButton, AnyModifier, wrapperId(), false,
1078
GrabModeSync, GrabModeAsync,
1080
// remove the grab for no modifiers only if the window
1081
// is unobscured or if the user doesn't want click raise
1082
// (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is
1083
// the most recently raised window)
1084
bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this;
1085
if( !options->clickRaise || not_obscured )
1086
ungrabButton( None );
1089
ungrabButton( ShiftMask );
1090
ungrabButton( ControlMask );
1091
ungrabButton( ControlMask | ShiftMask );
1095
XUngrabButton( display(), AnyButton, AnyModifier, wrapperId());
1096
// simply grab all modifier combinations
1097
XGrabButton(display(), AnyButton, AnyModifier, wrapperId(), false,
1099
GrabModeSync, GrabModeAsync,
1104
// Qt propagates mouse events up the widget hierachy, which means events
1105
// for the decoration window cannot be (easily) intercepted as X11 events
1106
bool Client::eventFilter( QObject* o, QEvent* e )
1108
if( decoration == NULL
1109
|| o != decoration->widget())
1111
if( e->type() == QEvent::MouseButtonPress )
1113
QMouseEvent* ev = static_cast< QMouseEvent* >( e );
1114
return buttonPressEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->buttons(), ev->modifiers() ),
1115
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1117
if( e->type() == QEvent::MouseButtonRelease )
1119
QMouseEvent* ev = static_cast< QMouseEvent* >( e );
1120
return buttonReleaseEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->buttons(), ev->modifiers() ),
1121
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1123
if( e->type() == QEvent::MouseMove ) // FRAME i fake z enter/leave?
1125
QMouseEvent* ev = static_cast< QMouseEvent* >( e );
1126
return motionNotifyEvent( decorationId(), qtToX11State( ev->buttons(), ev->modifiers() ),
1127
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1129
if( e->type() == QEvent::Wheel )
1131
QWheelEvent* ev = static_cast< QWheelEvent* >( e );
1132
bool r = buttonPressEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->buttons(), ev->modifiers() ),
1133
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1134
r = r || buttonReleaseEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->buttons(), ev->modifiers() ),
1135
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1138
if( e->type() == QEvent::Resize )
1140
QResizeEvent* ev = static_cast< QResizeEvent* >( e );
1141
// Filter out resize events that inform about size different than frame size.
1142
// This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync.
1143
// These events only seem to be delayed events from initial resizing before show() was called
1144
// on the decoration widget.
1145
if( ev->size() != size())
1147
// HACK: Avoid decoration redraw delays. On resize Qt sets WA_WStateConfigPending
1148
// which delays all painting until a matching ConfigureNotify event comes.
1149
// But this process itself is the window manager, so it's not needed
1150
// to wait for that event, the geometry is known.
1151
// Note that if Qt in the future changes how this flag is handled and what it
1152
// triggers then this may potentionally break things. See mainly QETWidget::translateConfigEvent().
1153
decoration->widget()->setAttribute( Qt::WA_WState_ConfigPending, false );
1154
decoration->widget()->update();
1160
// return value matters only when filtering events before decoration gets them
1161
bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root )
1165
if( w == wrapperId())
1166
XAllowEvents(display(), SyncPointer, CurrentTime ); //xTime());
1170
if( w == wrapperId() || w == frameId() || w == decorationId())
1171
{ // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
1173
workspace()->setWasUserInteraction();
1174
uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ?
1175
KKeyServer::modXMeta() :
1176
KKeyServer::modXAlt();
1177
bool bModKeyHeld = keyModX != 0 && ( state & KKeyServer::accelModMaskX()) == keyModX;
1180
&& button == Button1 && !bModKeyHeld )
1181
{ // hide splashwindow if the user clicks on it
1183
if( w == wrapperId())
1184
XAllowEvents(display(), SyncPointer, CurrentTime ); //xTime());
1188
Options::MouseCommand com = Options::MouseNothing;
1189
bool was_action = false;
1190
bool perform_handled = false;
1197
com = options->commandAll1();
1200
com = options->commandAll2();
1203
com = options->commandAll3();
1207
com = options->operationWindowMouseWheel( button == Button4 ? 120 : -120 );
1212
{ // inactive inner window
1213
if( !isActive() && w == wrapperId())
1216
perform_handled = true;
1220
com = options->commandWindow1();
1223
com = options->commandWindow2();
1226
com = options->commandWindow3();
1229
com = Options::MouseActivateAndPassClick;
1232
// active inner window
1233
if( isActive() && w == wrapperId()
1234
&& options->clickRaise && button < 4 ) // exclude wheel
1236
com = Options::MouseActivateRaiseAndPassClick;
1238
perform_handled = true;
1243
bool replay = performMouseCommand( com, QPoint( x_root, y_root), perform_handled );
1245
if ( isSpecialWindow())
1248
if( w == wrapperId()) // these can come only from a grab
1249
XAllowEvents(display(), replay? ReplayPointer : SyncPointer, CurrentTime ); //xTime());
1254
if( w == wrapperId()) // these can come only from a grab
1256
XAllowEvents(display(), ReplayPointer, CurrentTime ); //xTime());
1259
if( w == decorationId())
1260
return false; // don't eat decoration events
1262
processDecorationButtonPress( button, state, x, y, x_root, y_root );
1267
// this function processes button press events only after decoration decides not to handle them,
1268
// unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them
1269
void Client::processDecorationButtonPress( int button, int /*state*/, int x, int y, int x_root, int y_root )
1271
Options::MouseCommand com = Options::MouseNothing;
1272
bool active = isActive();
1273
if ( !wantsInput() ) // we cannot be active, use it anyway
1276
if ( button == Button1 )
1277
com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1();
1278
else if ( button == Button2 )
1279
com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2();
1280
else if ( button == Button3 )
1281
com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
1282
if( button == Button1
1283
&& com != Options::MouseOperationsMenu // actions where it's not possible to get the matching
1284
&& com != Options::MouseMinimize ) // mouse release event
1286
mode = mousePosition( QPoint( x, y ));
1288
moveOffset = QPoint( x, y );
1289
invertedMoveOffset = rect().bottomRight() - moveOffset;
1290
unrestrictedMoveResize = false;
1291
startDelayedMoveResize();
1294
performMouseCommand( com, QPoint( x_root, y_root ));
1297
// called from decoration
1298
void Client::processMousePressEvent( QMouseEvent* e )
1300
if( e->type() != QEvent::MouseButtonPress )
1302
kWarning() << "processMousePressEvent()" ;
1306
switch( e->button())
1308
case Qt::LeftButton:
1314
case Qt::RightButton:
1320
processDecorationButtonPress( button, e->buttons(), e->x(), e->y(), e->globalX(), e->globalY());
1323
// return value matters only when filtering events before decoration gets them
1324
bool Client::buttonReleaseEvent( Window w, int /*button*/, int state, int x, int y, int x_root, int y_root )
1326
if( w == decorationId() && !buttonDown)
1328
if( w == wrapperId())
1330
XAllowEvents(display(), SyncPointer, CurrentTime ); //xTime());
1333
if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
1335
x = this->x(); // translate from grab window to local coords
1337
if ( (state & ( Button1Mask & Button2Mask & Button3Mask )) == 0 )
1340
stopDelayedMoveResize();
1341
if ( moveResizeMode )
1343
finishMoveResize( false );
1344
// mouse position is still relative to old Client position, adjust it
1345
QPoint mousepos( x_root - x, y_root - y );
1346
mode = mousePosition( mousepos );
1353
static bool was_motion = false;
1354
static Time next_motion_time = CurrentTime;
1355
// Check whole incoming X queue for MotionNotify events
1356
// checking whole queue is done by always returning False in the predicate.
1357
// If there are more MotionNotify events in the queue, all until the last
1358
// one may be safely discarded (if a ButtonRelease event comes, a MotionNotify
1359
// will be faked from it, so there's no need to check other events).
1360
// This helps avoiding being overloaded by being flooded from many events
1361
// from the XServer.
1362
static Bool motion_predicate( Display*, XEvent* ev, XPointer )
1364
if( ev->type == MotionNotify )
1367
next_motion_time = ev->xmotion.time; // for setting time
1372
static bool waitingMotionEvent()
1374
// The queue doesn't need to be checked until the X timestamp
1375
// of processes events reaches the timestamp of the last suitable
1376
// MotionNotify event in the queue.
1377
if( next_motion_time != CurrentTime
1378
&& timestampCompare( xTime(), next_motion_time ) < 0 )
1381
XSync( display(), False ); // this helps to discard more MotionNotify events
1383
XCheckIfEvent( display(), &dummy, motion_predicate, NULL );
1387
// return value matters only when filtering events before decoration gets them
1388
bool Client::motionNotifyEvent( Window w, int /*state*/, int x, int y, int x_root, int y_root )
1390
if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
1391
return true; // care only about the whole frame
1394
Position newmode = mousePosition( QPoint( x, y ));
1395
if( newmode != mode )
1400
// reset the timestamp for the optimization, otherwise with long passivity
1401
// the option in waitingMotionEvent() may be always true
1402
next_motion_time = CurrentTime;
1405
if( w == moveResizeGrabWindow())
1407
x = this->x(); // translate from grab window to local coords
1410
if( !waitingMotionEvent())
1411
handleMoveResize( x, y, x_root, y_root );
1415
void Client::focusInEvent( XFocusInEvent* e )
1417
if( e->window != window())
1418
return; // only window gets focus
1419
if ( e->mode == NotifyUngrab )
1420
return; // we don't care
1421
if ( e->detail == NotifyPointer )
1422
return; // we don't care
1423
if( !isShown( false ) || !isOnCurrentDesktop()) // we unmapped it, but it got focus meanwhile ->
1424
return; // activateNextClient() already transferred focus elsewhere
1425
// check if this client is in should_get_focus list or if activation is allowed
1426
bool activate = workspace()->allowClientActivation( this, -1U, true );
1427
workspace()->gotFocusIn( this ); // remove from should_get_focus list
1432
workspace()->restoreFocus();
1437
// When a client loses focus, FocusOut events are usually immediatelly
1438
// followed by FocusIn events for another client that gains the focus
1439
// (unless the focus goes to another screen, or to the nofocus widget).
1440
// Without this check, the former focused client would have to be
1441
// deactivated, and after that, the new one would be activated, with
1442
// a short time when there would be no active client. This can cause
1443
// flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred
1444
// from it to its transient, the fullscreen would be kept in the Active layer
1445
// at the beginning and at the end, but not in the middle, when the active
1446
// client would be temporarily none (see Client::belongToLayer() ).
1447
// Therefore, the events queue is checked, whether it contains the matching
1448
// FocusIn event, and if yes, deactivation of the previous client will
1449
// be skipped, as activation of the new one will automatically deactivate
1450
// previously active client.
1451
static bool follows_focusin = false;
1452
static bool follows_focusin_failed = false;
1453
static Bool predicate_follows_focusin( Display*, XEvent* e, XPointer arg )
1455
if( follows_focusin || follows_focusin_failed )
1457
Client* c = ( Client* ) arg;
1458
if( e->type == FocusIn && c->workspace()->findClient( WindowMatchPredicate( e->xfocus.window )))
1460
follows_focusin = true;
1463
// events that may be in the queue before the FocusIn event that's being
1465
if( e->type == FocusIn || e->type == FocusOut || e->type == KeymapNotify )
1467
follows_focusin_failed = true; // a different event - stop search
1471
static bool check_follows_focusin( Client* c )
1473
follows_focusin = follows_focusin_failed = false;
1475
// XCheckIfEvent() is used to make the search non-blocking, the predicate
1476
// always returns False, so nothing is removed from the events queue.
1477
// XPeekIfEvent() would block.
1478
XCheckIfEvent( display(), &dummy, predicate_follows_focusin, (XPointer)c );
1479
return follows_focusin;
1483
void Client::focusOutEvent( XFocusOutEvent* e )
1485
if( e->window != window())
1486
return; // only window gets focus
1487
if ( e->mode == NotifyGrab )
1488
return; // we don't care
1490
return; // here neither
1491
if ( e->detail != NotifyNonlinear
1492
&& e->detail != NotifyNonlinearVirtual )
1493
// SELI check all this
1494
return; // hack for motif apps like netscape
1495
if ( QApplication::activePopupWidget() )
1497
if( !check_follows_focusin( this ))
1501
// performs _NET_WM_MOVERESIZE
1502
void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction )
1504
if( direction == NET::Move )
1505
performMouseCommand( Options::MouseMove, QPoint( x_root, y_root ));
1506
else if( moveResizeMode && direction == NET::MoveResizeCancel)
1508
finishMoveResize( true );
1512
else if( direction >= NET::TopLeft && direction <= NET::Left )
1514
static const Position convert[] =
1520
PositionBottomRight,
1525
if(!isResizable() || isShade())
1527
if( moveResizeMode )
1528
finishMoveResize( false );
1530
moveOffset = QPoint( x_root - x(), y_root - y()); // map from global
1531
invertedMoveOffset = rect().bottomRight() - moveOffset;
1532
unrestrictedMoveResize = false;
1533
mode = convert[ direction ];
1534
if( !startMoveResize())
1538
else if( direction == NET::KeyboardMove )
1539
{ // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm
1540
QCursor::setPos( geometry().center() );
1541
performMouseCommand( Options::MouseUnrestrictedMove, geometry().center());
1543
else if( direction == NET::KeyboardSize )
1544
{ // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm
1545
QCursor::setPos( geometry().bottomRight());
1546
performMouseCommand( Options::MouseUnrestrictedResize, geometry().bottomRight());
1550
void Client::keyPressEvent( uint key_code )
1553
if ( !isMove() && !isResize() )
1555
bool is_control = key_code & Qt::CTRL;
1556
bool is_alt = key_code & Qt::ALT;
1557
key_code = key_code & ~Qt::KeyboardModifierMask;
1558
int delta = is_control?1:is_alt?32:8;
1559
QPoint pos = cursorPos();
1575
case Qt::Key_Return:
1577
finishMoveResize( false );
1581
case Qt::Key_Escape:
1582
finishMoveResize( true );
1589
QCursor::setPos( pos );
1593
void Client::syncEvent( XSyncAlarmNotifyEvent* e )
1595
if( e->alarm == sync_alarm && XSyncValueEqual( e->counter_value, sync_counter_value ))
1597
ready_for_painting = true;
1600
delete sync_timeout;
1601
sync_timeout = NULL;
1602
if( sync_resize_pending )
1603
performMoveResize();
1609
// ****************************************
1611
// ****************************************
1613
bool Unmanaged::windowEvent( XEvent* e )
1615
double old_opacity = opacity();
1616
unsigned long dirty[ 2 ];
1617
info->event( e, dirty, 2 ); // pass through the NET stuff
1618
if( dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2Opacity )
1623
scene->windowOpacityChanged( this );
1625
static_cast<EffectsHandlerImpl*>(effects)->windowOpacityChanged( effectWindow(), old_opacity );
1631
unmapNotifyEvent( &e->xunmap );
1634
mapNotifyEvent( &e->xmap );
1636
case ConfigureNotify:
1637
configureNotifyEvent( &e->xconfigure );
1639
case PropertyNotify:
1640
propertyNotifyEvent( &e->xproperty );
1643
if( e->type == Extensions::shapeNotifyEvent() )
1645
detectShape( window());
1648
scene->windowGeometryShapeChanged( this );
1649
if( effects != NULL )
1650
static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geometry());
1653
if( e->type == Extensions::damageNotifyEvent())
1654
damageNotifyEvent( reinterpret_cast< XDamageNotifyEvent* >( e ));
1659
return false; // don't eat events, even our own unmanaged widgets are tracked
1662
void Unmanaged::mapNotifyEvent( XMapEvent* )
1666
void Unmanaged::unmapNotifyEvent( XUnmapEvent* )
1671
void Unmanaged::configureNotifyEvent( XConfigureEvent* e )
1674
static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowStacking(); // keep them on top
1675
QRect newgeom( e->x, e->y, e->width, e->height );
1676
if( newgeom != geom )
1678
addWorkspaceRepaint( geometry()); // damage old area
1681
discardWindowPixmap();
1683
scene->windowGeometryShapeChanged( this );
1684
if( effects != NULL )
1685
static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), old );
1687
workspace()->restackUnmanaged( this, e->above );
1690
// ****************************************
1692
// ****************************************
1694
void Toplevel::propertyNotifyEvent( XPropertyEvent* e )
1696
if( e->window != window())
1697
return; // ignore frame/wrapper
1701
if (e->atom == atoms->wm_client_leader )
1702
getWmClientLeader();
1703
else if( e->atom == atoms->wm_window_role )
1709
// ****************************************
1711
// ****************************************
1713
bool Group::groupEvent( XEvent* e )
1715
unsigned long dirty[ 2 ];
1716
leader_info->event( e, dirty, 2 ); // pass through the NET stuff
1717
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
1719
if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )