You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/twin/events.cpp

1785 lines
64 KiB

/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
Copyright (C) 2003 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.
******************************************************************/
/*
This file contains things relevant to handling incoming events.
*/
#include "client.h"
#include "workspace.h"
#include "atoms.h"
#include "tabbox.h"
#include "group.h"
#include "rules.h"
#include <tqwhatsthis.h>
#include <kkeynative.h>
#include <tqapplication.h>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include <stdlib.h>
extern Atom tqt_window_role;
namespace KWinInternal
{
// ****************************************
// WinInfo
// ****************************************
WinInfo::WinInfo( Client * c, Display * display, Window window,
Window rwin, const unsigned long pr[], int pr_size )
: NETWinInfo( display, window, rwin, pr, pr_size, NET::WindowManager ), m_client( c )
{
}
void WinInfo::changeDesktop(int desktop)
{
m_client->workspace()->sendClientToDesktop( m_client, desktop, true );
}
void WinInfo::changeState( unsigned long state, unsigned long mask )
{
mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore
mask &= ~NET::Hidden; // clients are not allowed to change this directly
state &= mask; // for safety, clear all other bits
if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) == 0 )
m_client->setFullScreen( false, false );
if ( (mask & NET::Max) == NET::Max )
m_client->setMaximize( state & NET::MaxVert, state & NET::MaxHoriz );
else if ( mask & NET::MaxVert )
m_client->setMaximize( state & NET::MaxVert, m_client->maximizeMode() & Client::MaximizeHorizontal );
else if ( mask & NET::MaxHoriz )
m_client->setMaximize( m_client->maximizeMode() & Client::MaximizeVertical, state & NET::MaxHoriz );
if ( mask & NET::Shaded )
m_client->setShade( state & NET::Shaded ? ShadeNormal : ShadeNone );
if ( mask & NET::KeepAbove)
m_client->setKeepAbove( (state & NET::KeepAbove) != 0 );
if ( mask & NET::KeepBelow)
m_client->setKeepBelow( (state & NET::KeepBelow) != 0 );
if( mask & NET::SkipTaskbar )
m_client->setSkipTaskbar( ( state & NET::SkipTaskbar ) != 0, true );
if( mask & NET::SkipPager )
m_client->setSkipPager( ( state & NET::SkipPager ) != 0 );
if( mask & NET::DemandsAttention )
m_client->demandAttention(( state & NET::DemandsAttention ) != 0 );
if( mask & NET::Modal )
m_client->setModal( ( state & NET::Modal ) != 0 );
// unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() )
if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) != 0 )
m_client->setFullScreen( true, false );
}
// ****************************************
// RootInfo
// ****************************************
RootInfo::RootInfo( Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr[], int pr_num, int scr )
: NETRootInfo4( dpy, w, name, pr, pr_num, scr )
{
workspace = ws;
}
void RootInfo::changeNumberOfDesktops(int n)
{
workspace->setNumberOfDesktops( n );
}
void RootInfo::changeCurrentDesktop(int d)
{
workspace->setCurrentDesktop( d );
}
void RootInfo::changeActiveWindow( Window w, NET::RequestSource src, Time timestamp, Window active_window )
{
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
{
if( timestamp == CurrentTime )
timestamp = c->userTime();
if( src != NET::FromApplication && src != FromTool )
src = NET::FromTool;
if( src == NET::FromTool )
workspace->activateClient( c, true ); // force
else // NET::FromApplication
{
Client* c2;
if( workspace->allowClientActivation( c, timestamp ))
workspace->activateClient( c );
// if activation of the requestor's window would be allowed, allow activation too
else if( active_window != None
&& ( c2 = workspace->findClient( WindowMatchPredicate( active_window ))) != NULL
&& workspace->allowClientActivation( c2,
timestampCompare( timestamp, c2->userTime() > 0 ? timestamp : c2->userTime())))
workspace->activateClient( c );
else
c->demandAttention();
}
}
}
void RootInfo::restackWindow( Window w, RequestSource src, Window above, int detail, Time timestamp )
{
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
{
if( timestamp == CurrentTime )
timestamp = c->userTime();
if( src != NET::FromApplication && src != FromTool )
src = NET::FromTool;
c->restackWindow( above, detail, src, timestamp, true );
}
}
void RootInfo::gotTakeActivity( Window w, Time timestamp, long flags )
{
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
workspace->handleTakeActivity( c, timestamp, flags );
}
void RootInfo::closeWindow(Window w)
{
Client* c = workspace->findClient( WindowMatchPredicate( w ));
if ( c )
c->closeWindow();
}
void RootInfo::moveResize(Window w, int x_root, int y_root, unsigned long direction)
{
Client* c = workspace->findClient( WindowMatchPredicate( w ));
if ( c )
{
updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp
c->NETMoveResize( x_root, y_root, (Direction)direction);
}
}
void RootInfo::moveResizeWindow(Window w, int flags, int x, int y, int width, int height )
{
Client* c = workspace->findClient( WindowMatchPredicate( w ));
if ( c )
c->NETMoveResizeWindow( flags, x, y, width, height );
}
void RootInfo::gotPing( Window w, Time timestamp )
{
if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
c->gotPing( timestamp );
}
void RootInfo::changeShowingDesktop( bool showing )
{
workspace->setShowingDesktop( showing );
}
// ****************************************
// Workspace
// ****************************************
/*!
Handles workspace specific XEvents
*/
bool Workspace::workspaceEvent( XEvent * e )
{
if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) )
{
mouse_emulation = FALSE;
XUngrabKeyboard( tqt_xdisplay(), GET_QT_X_TIME() );
}
if( e->type == PropertyNotify || e->type == ClientMessage )
{
unsigned long dirty[ NETRootInfo::PROPERTIES_SIZE ];
rootInfo->event( e, dirty, NETRootInfo::PROPERTIES_SIZE );
if( dirty[ NETRootInfo::PROTOCOLS ] & NET::DesktopNames )
saveDesktopSettings();
if( dirty[ NETRootInfo::PROTOCOLS2 ] & NET::WM2DesktopLayout )
updateDesktopLayout();
}
// events that should be handled before Clients can get them
switch (e->type)
{
case ButtonPress:
case ButtonRelease:
was_user_interaction = true;
// fallthrough
case MotionNotify:
if ( tab_grab || control_grab )
{
tab_box->handleMouseEvent( e );
return TRUE;
}
break;
case KeyPress:
{
was_user_interaction = true;
KKeyNative keyX( (XEvent*)e );
uint keyQt = keyX.keyCodeQt();
kdDebug(125) << "Workspace::keyPress( " << keyX.key().toString() << " )" << endl;
if (movingClient)
{
movingClient->keyPressEvent(keyQt);
return true;
}
if( tab_grab || control_grab )
{
tabBoxKeyPress( keyX );
return true;
}
break;
}
case KeyRelease:
was_user_interaction = true;
if( tab_grab || control_grab )
{
tabBoxKeyRelease( e->xkey );
return true;
}
break;
};
if( Client* c = findClient( WindowMatchPredicate( e->xany.window )))
{
if( c->windowEvent( e ))
return true;
}
else if( Client* c = findClient( WrapperIdMatchPredicate( e->xany.window )))
{
if( c->windowEvent( e ))
return true;
}
else if( Client* c = findClient( FrameIdMatchPredicate( e->xany.window )))
{
if( c->windowEvent( e ))
return true;
}
else
{
Window special = findSpecialEventWindow( e );
if( special != None )
if( Client* c = findClient( WindowMatchPredicate( special )))
{
if( c->windowEvent( e ))
return true;
}
}
if( movingClient != NULL && movingClient->moveResizeGrabWindow() == e->xany.window
&& ( e->type == MotionNotify || e->type == ButtonPress || e->type == ButtonRelease ))
{
if( movingClient->windowEvent( e ))
return true;
}
switch (e->type)
{
case CreateNotify:
if ( e->xcreatewindow.parent == root &&
!TQWidget::find( e->xcreatewindow.window) &&
!e->xcreatewindow.override_redirect )
{
// see comments for allowClientActivation()
Time my_qtx_time = GET_QT_X_TIME();
XChangeProperty(tqt_xdisplay(), e->xcreatewindow.window,
atoms->kde_net_wm_user_creation_time, XA_CARDINAL,
32, PropModeReplace, (unsigned char *)&my_qtx_time, 1);
SET_QT_X_TIME(my_qtx_time);
}
break;
case UnmapNotify:
{
// check for system tray windows
if ( removeSystemTrayWin( e->xunmap.window, true ) )
{
// If the system tray gets destroyed, the system tray
// icons automatically get unmapped, reparented and mapped
// again to the closest non-client ancestor due to
// QXEmbed's SaveSet feature. Unfortunatly with kicker
// this closest ancestor is not the root window, but our
// decoration, so we reparent explicitely back to the root
// window.
XEvent ev;
WId w = e->xunmap.window;
if ( XCheckTypedWindowEvent (tqt_xdisplay(), w,
ReparentNotify, &ev) )
{
if ( ev.xreparent.parent != root )
{
XReparentWindow( tqt_xdisplay(), w, root, 0, 0 );
addSystemTrayWin( w );
}
}
return TRUE;
}
return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt
}
case MapNotify:
return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt
case ReparentNotify:
{
//do not confuse Qt with these events. After all, _we_ are the
//window manager who does the reparenting.
return TRUE;
}
case DestroyNotify:
{
if ( removeSystemTrayWin( e->xdestroywindow.window, false ) )
return TRUE;
return false;
}
case MapRequest:
{
updateXTime();
// e->xmaprequest.window is different from e->xany.window
// TODO this shouldn't be necessary now
Client* c = findClient( WindowMatchPredicate( e->xmaprequest.window ));
if ( !c )
{
// don't check for the parent being the root window, this breaks when some app unmaps
// a window, changes something and immediately maps it back, without giving KWin
// a chance to reparent it back to root
// since KWin can get MapRequest only for root window children and
// children of WindowWrapper (=clients), the check is AFAIK useless anyway
// Note: Now the save-set support in Client::mapRequestEvent() actually requires that
// this code doesn't check the parent to be root.
// if ( e->xmaprequest.parent == root ) { //###TODO store previously destroyed client ids
if ( addSystemTrayWin( e->xmaprequest.window ) )
return TRUE;
c = createClient( e->xmaprequest.window, false );
if ( c != NULL && root != tqt_xrootwin() )
{ // TODO what is this?
// TODO may use TQWidget::create
XReparentWindow( tqt_xdisplay(), c->frameId(), root, 0, 0 );
}
if( c == NULL ) // refused to manage, simply map it (most probably override redirect)
XMapRaised( tqt_xdisplay(), e->xmaprequest.window );
return true;
}
if( c )
{
c->windowEvent( e );
updateFocusChains( c, FocusChainUpdate );
return true;
}
break;
}
case EnterNotify:
{
if ( TQWhatsThis::inWhatsThisMode() )
{
TQWidget* w = TQWidget::find( e->xcrossing.window );
if ( w )
TQWhatsThis::leaveWhatsThisMode();
}
if( electricBorder(e))
return true;
break;
}
case LeaveNotify:
{
if ( !TQWhatsThis::inWhatsThisMode() )
break;
// TODO is this cliente ever found, given that client events are searched above?
Client* c = findClient( FrameIdMatchPredicate( e->xcrossing.window ));
if ( c && e->xcrossing.detail != NotifyInferior )
TQWhatsThis::leaveWhatsThisMode();
break;
}
case ConfigureRequest:
{
if ( e->xconfigurerequest.parent == root )
{
XWindowChanges wc;
wc.border_width = e->xconfigurerequest.border_width;
wc.x = e->xconfigurerequest.x;
wc.y = e->xconfigurerequest.y;
wc.width = e->xconfigurerequest.width;
wc.height = e->xconfigurerequest.height;
wc.sibling = None;
wc.stack_mode = Above;
unsigned int value_mask = e->xconfigurerequest.value_mask
& ( CWX | CWY | CWWidth | CWHeight | CWBorderWidth );
XConfigureWindow( tqt_xdisplay(), e->xconfigurerequest.window, value_mask, &wc );
return true;
}
break;
}
case KeyPress:
if ( mouse_emulation )
return keyPressMouseEmulation( e->xkey );
break;
case KeyRelease:
if ( mouse_emulation )
return FALSE;
break;
case FocusIn:
if( e->xfocus.window == rootWin() && TQCString( getenv("TDE_MULTIHEAD")).lower() != "true"
&& ( e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot ))
{
updateXTime(); // focusToNull() uses tqt_x_time, which is old now (FocusIn has no timestamp)
Window focus;
int revert;
XGetInputFocus( tqt_xdisplay(), &focus, &revert );
if( focus == None || focus == PointerRoot )
{
//kdWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" << endl;
Client *c = mostRecentlyActivatedClient();
if( c != NULL )
requestFocus( c, true );
else if( activateNextClient( NULL ))
; // ok, activated
else
focusToNull();
}
}
// fall through
case FocusOut:
return true; // always eat these, they would tell Qt that KWin is the active app
case ClientMessage:
if( electricBorder( e ))
return true;
break;
default:
break;
}
return FALSE;
}
// Some events don't have the actual window which caused the event
// as e->xany.window (e.g. ConfigureRequest), but as some other
// field in the XEvent structure.
Window Workspace::findSpecialEventWindow( XEvent* e )
{
switch( e->type )
{
case CreateNotify:
return e->xcreatewindow.window;
case DestroyNotify:
return e->xdestroywindow.window;
case UnmapNotify:
return e->xunmap.window;
case MapNotify:
return e->xmap.window;
case MapRequest:
return e->xmaprequest.window;
case ReparentNotify:
return e->xreparent.window;
case ConfigureNotify:
return e->xconfigure.window;
case GravityNotify:
return e->xgravity.window;
case ConfigureRequest:
return e->xconfigurerequest.window;
case CirculateNotify:
return e->xcirculate.window;
case CirculateRequest:
return e->xcirculaterequest.window;
default:
return None;
};
}
// ****************************************
// Client
// ****************************************
/*!
General handler for XEvents concerning the client window
*/
bool Client::windowEvent( XEvent* e )
{
if( e->xany.window == window()) // avoid doing stuff on frame or wrapper
{
unsigned long dirty[ 2 ];
info->event( e, dirty, 2 ); // pass through the NET stuff
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 )
fetchName();
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName ) != 0 )
fetchIconicName();
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut ) != 0
|| ( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut ) != 0 )
{
if( isTopMenu()) // the fallback mode of KMenuBar may alter the strut
checkWorkspacePosition(); // restore it
workspace()->updateClientArea();
}
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
getIcons();
// Note there's a difference between userTime() and info->userTime()
// info->userTime() is the value of the property, userTime() also includes
// updates of the time done by KWin (ButtonPress on windowrapper etc.).
if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime ) != 0 )
{
workspace()->setWasUserInteraction();
updateUserTime( info->userTime());
}
if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
startupIdChanged();
if( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry )
{
if( demandAttentionKNotifyTimer != NULL )
demandAttentionKNotify();
}
}
// TODO move all focus handling stuff to separate file?
switch (e->type)
{
case UnmapNotify:
unmapNotifyEvent( &e->xunmap );
break;
case DestroyNotify:
destroyNotifyEvent( &e->xdestroywindow );
break;
case MapRequest:
// this one may pass the event to workspace
return mapRequestEvent( &e->xmaprequest );
case ConfigureRequest:
configureRequestEvent( &e->xconfigurerequest );
break;
case PropertyNotify:
propertyNotifyEvent( &e->xproperty );
break;
case KeyPress:
updateUserTime();
workspace()->setWasUserInteraction();
break;
case ButtonPress:
updateUserTime();
workspace()->setWasUserInteraction();
buttonPressEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
break;
case KeyRelease:
// don't update user time on releases
// e.g. if the user presses Alt+F2, the Alt release
// would appear as user input to the currently active window
break;
case ButtonRelease:
// don't update user time on releases
// e.g. if the user presses Alt+F2, the Alt release
// would appear as user input to the currently active window
buttonReleaseEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
break;
case MotionNotify:
motionNotifyEvent( e->xmotion.window, e->xmotion.state,
e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root );
workspace()->updateFocusMousePosition( TQPoint( e->xmotion.x_root, e->xmotion.y_root ));
break;
case EnterNotify:
enterNotifyEvent( &e->xcrossing );
// MotionNotify is guaranteed to be generated only if the mouse
// move start and ends in the window; for cases when it only
// starts or only ends there, Enter/LeaveNotify are generated.
// Fake a MotionEvent in such cases to make handle of mouse
// events simpler (Qt does that too).
motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
workspace()->updateFocusMousePosition( TQPoint( e->xcrossing.x_root, e->xcrossing.y_root ));
break;
case LeaveNotify:
motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
leaveNotifyEvent( &e->xcrossing );
// not here, it'd break following enter notify handling
// workspace()->updateFocusMousePosition( TQPoint( e->xcrossing.x_root, e->xcrossing.y_root ));
break;
case FocusIn:
focusInEvent( &e->xfocus );
break;
case FocusOut:
focusOutEvent( &e->xfocus );
break;
case ReparentNotify:
break;
case ClientMessage:
clientMessageEvent( &e->xclient );
break;
case ColormapChangeMask:
if( e->xany.window == window())
{
cmap = e->xcolormap.colormap;
if ( isActive() )
workspace()->updateColormap();
}
break;
default:
if( e->xany.window == window())
{
if( e->type == Shape::shapeEvent() )
{
is_shape = Shape::hasShape( window()); // workaround for #19644
updateShape();
}
}
break;
}
return true; // eat all events
}
/*!
Handles map requests of the client window
*/
bool Client::mapRequestEvent( XMapRequestEvent* e )
{
if( e->window != window())
{
// Special support for the save-set feature, which is a bit broken.
// If there's a window from one client embedded in another one,
// e.g. using XEMBED, and the embedder suddenly looses its X connection,
// save-set will reparent the embedded window to its closest ancestor
// that will remains. Unfortunately, with reparenting window managers,
// this is not the root window, but the frame (or in KWin's case,
// it's the wrapper for the client window). In this case,
// the wrapper will get ReparentNotify for a window it won't know,
// which will be ignored, and then it gets MapRequest, as save-set
// always maps. Returning true here means that Workspace::workspaceEvent()
// will handle this MapRequest and manage this window (i.e. act as if
// it was reparented to root window).
if( e->parent == wrapperId())
return false;
return true; // no messing with frame etc.
}
if( isTopMenu() && workspace()->managingTopMenus())
return true; // twin controls these
switch ( mappingState() )
{
case WithdrawnState:
assert( false ); // WMs are not supposed to manage clients in Withdrawn state,
// manage(); // after initial mapping manage() is called from createClient()
break;
case IconicState:
// also copied in clientMessage()
if( isMinimized())
unminimize();
if( isShade())
setShade( ShadeNone );
if( !isOnCurrentDesktop())
{
if( workspace()->allowClientActivation( this ))
workspace()->activateClient( this );
else
demandAttention();
}
break;
case NormalState:
// TODO fake MapNotify?
break;
}
return true;
}
/*!
Handles unmap notify events of the client window
*/
void Client::unmapNotifyEvent( XUnmapEvent* e )
{
if( e->window != window())
return;
if( e->event != wrapperId())
{ // most probably event from root window when initially reparenting
bool ignore = true;
if( e->event == workspace()->rootWin() && e->send_event )
ignore = false; // XWithdrawWindow()
if( ignore )
return;
}
switch( mappingState())
{
case IconicState:
releaseWindow();
return;
case NormalState:
// maybe we will be destroyed soon. Check this first.
XEvent ev;
if( XCheckTypedWindowEvent (tqt_xdisplay(), window(),
DestroyNotify, &ev) ) // TODO I don't like this much
{
destroyClient(); // deletes this
return;
}
releaseWindow();
break;
default:
assert( false );
}
}
void Client::destroyNotifyEvent( XDestroyWindowEvent* e )
{
if( e->window != window())
return;
destroyClient();
}
bool blockAnimation = FALSE;
/*!
Handles client messages for the client window
*/
void Client::clientMessageEvent( XClientMessageEvent* e )
{
if( e->window != window())
return; // ignore frame/wrapper
// WM_STATE
if ( e->message_type == atoms->kde_wm_change_state )
{
if( isTopMenu() && workspace()->managingTopMenus())
return; // twin controls these
if( e->data.l[ 1 ] )
blockAnimation = true;
if( e->data.l[ 0 ] == IconicState )
minimize();
else if( e->data.l[ 0 ] == NormalState )
{ // copied from mapRequest()
if( isMinimized())
unminimize();
if( isShade())
setShade( ShadeNone );
if( !isOnCurrentDesktop())
{
if( workspace()->allowClientActivation( this ))
workspace()->activateClient( this );
else
demandAttention();
}
}
blockAnimation = false;
}
else if ( e->message_type == atoms->wm_change_state)
{
if( isTopMenu() && workspace()->managingTopMenus())
return; // twin controls these
if ( e->data.l[0] == IconicState )
minimize();
return;
}
}
/*!
Handles configure requests of the client window
*/
void Client::configureRequestEvent( XConfigureRequestEvent* e )
{
if( e->window != window())
return; // ignore frame/wrapper
if ( isResize() || isMove())
return; // we have better things to do right now
if( fullscreen_mode == FullScreenNormal ) // refuse resizing of fullscreen windows
{ // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode
sendSyntheticConfigureNotify();
return;
}
if( isSplash() // no manipulations with splashscreens either
|| isTopMenu()) // topmenus neither
{
sendSyntheticConfigureNotify();
return;
}
if ( e->value_mask & CWBorderWidth )
{
// first, get rid of a window border
XWindowChanges wc;
unsigned int value_mask = 0;
wc.border_width = 0;
value_mask = CWBorderWidth;
XConfigureWindow( tqt_xdisplay(), window(), value_mask, & wc );
}
if( e->value_mask & ( CWX | CWY | CWHeight | CWWidth ))
configureRequest( e->value_mask, e->x, e->y, e->width, e->height, 0, false );
if ( e->value_mask & CWStackMode )
restackWindow( e->above, e->detail, NET::FromApplication, userTime(), false );
// TODO sending a synthetic configure notify always is fine, even in cases where
// the ICCCM doesn't require this - it can be though of as 'the WM decided to move
// the window later'. The client should not cause that many configure request,
// so this should not have any significant impact. With user moving/resizing
// the it should be optimized though (see also Client::setGeometry()/plainResize()/move()).
sendSyntheticConfigureNotify();
// SELI TODO accept configure requests for isDesktop windows (because kdesktop
// may get XRANDR resize event before twin), but check it's still at the bottom?
}
/*!
Handles property changes of the client window
*/
void Client::propertyNotifyEvent( XPropertyEvent* e )
{
if( e->window != window())
return; // ignore frame/wrapper
switch ( e->atom )
{
case XA_WM_NORMAL_HINTS:
getWmNormalHints();
break;
case XA_WM_NAME:
fetchName();
break;
case XA_WM_ICON_NAME:
fetchIconicName();
break;
case XA_WM_TRANSIENT_FOR:
readTransient();
break;
case XA_WM_HINTS:
getWMHints();
getIcons(); // because KWin::icon() uses WMHints as fallback
break;
default:
if ( e->atom == atoms->wm_protocols )
getWindowProtocols();
else if (e->atom == atoms->wm_client_leader )
getWmClientLeader();
else if( e->atom == tqt_window_role )
window_role = staticWindowRole( window());
else if( e->atom == atoms->motif_wm_hints )
getMotifHints();
break;
}
}
void Client::enterNotifyEvent( XCrossingEvent* e )
{
if( e->window != frameId())
return; // care only about entering the whole frame
if( e->mode == NotifyNormal ||
( !options->focusPolicyIsReasonable() &&
e->mode == NotifyUngrab ) )
{
if (options->shadeHover && isShade())
{
delete shadeHoverTimer;
shadeHoverTimer = new TQTimer( this );
connect( shadeHoverTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( shadeHover() ));
shadeHoverTimer->start( options->shadeHoverInterval, TRUE );
}
if ( options->focusPolicy == Options::ClickToFocus )
return;
if ( options->autoRaise && !isDesktop() &&
!isDock() && !isTopMenu() && workspace()->focusChangeEnabled() &&
workspace()->topClientOnDesktop( workspace()->currentDesktop()) != this )
{
delete autoRaiseTimer;
autoRaiseTimer = new TQTimer( this );
connect( autoRaiseTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( autoRaise() ) );
autoRaiseTimer->start( options->autoRaiseInterval, TRUE );
}
TQPoint currentPos( e->x_root, e->y_root );
if ( options->focusPolicy != Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) )
return;
// for FocusFollowsMouse, change focus only if the mouse has actually been moved, not if the focus
// change came because of window changes (e.g. closing a window) - #92290
if( options->focusPolicy != Options::FocusFollowsMouse
|| currentPos != workspace()->focusMousePosition())
{
if ( options->delayFocus )
workspace()->requestDelayFocus( this );
else
workspace()->requestFocus( this );
}
return;
}
}
void Client::leaveNotifyEvent( XCrossingEvent* e )
{
if( e->window != frameId())
return; // care only about leaving the whole frame
if ( e->mode == NotifyNormal )
{
if ( !buttonDown )
{
mode = PositionCenter;
setCursor( tqarrowCursor );
}
bool lostMouse = !rect().contains( TQPoint( e->x, e->y ) );
// 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations
// (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event
// comes after leaving the rect) - so lets check if the pointer is really outside the window
// TODO this still sucks if a window appears above this one - it should lose the mouse
// if this window is another client, but not if it's a popup ... maybe after KDE3.1 :(
// (repeat after me 'AARGHL!')
if ( !lostMouse && e->detail != NotifyInferior )
{
int d1, d2, d3, d4;
unsigned int d5;
Window w, child;
if( XQueryPointer( tqt_xdisplay(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5 ) == False
|| child == None )
lostMouse = true; // really lost the mouse
}
if ( lostMouse )
{
cancelAutoRaise();
workspace()->cancelDelayFocus();
cancelShadeHover();
if ( shade_mode == ShadeHover && !moveResizeMode && !buttonDown )
setShade( ShadeNormal );
}
if ( options->focusPolicy == Options::FocusStrictlyUnderMouse )
if ( isActive() && lostMouse )
workspace()->requestFocus( 0 ) ;
return;
}
}
#define XCapL KKeyNative::modXLock()
#define XNumL KKeyNative::modXNumLock()
#define XScrL KKeyNative::modXScrollLock()
void Client::grabButton( int modifier )
{
unsigned int mods[ 8 ] =
{
0, XCapL, XNumL, XNumL | XCapL,
XScrL, XScrL | XCapL,
XScrL | XNumL, XScrL | XNumL | XCapL
};
for( int i = 0;
i < 8;
++i )
XGrabButton( tqt_xdisplay(), AnyButton,
modifier | mods[ i ],
wrapperId(), FALSE, ButtonPressMask,
GrabModeSync, GrabModeAsync, None, None );
}
void Client::ungrabButton( int modifier )
{
unsigned int mods[ 8 ] =
{
0, XCapL, XNumL, XNumL | XCapL,
XScrL, XScrL | XCapL,
XScrL | XNumL, XScrL | XNumL | XCapL
};
for( int i = 0;
i < 8;
++i )
XUngrabButton( tqt_xdisplay(), AnyButton,
modifier | mods[ i ], wrapperId());
}
#undef XCapL
#undef XNumL
#undef XScrL
/*
Releases the passive grab for some modifier combinations when a
window becomes active. This helps broken X programs that
missinterpret LeaveNotify events in grab mode to work properly
(Motif, AWT, Tk, ...)
*/
void Client::updateMouseGrab()
{
if( workspace()->globalShortcutsDisabled())
{
XUngrabButton( tqt_xdisplay(), AnyButton, AnyModifier, wrapperId());
// keep grab for the simple click without modifiers if needed (see below)
bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this;
if( !( !options->clickRaise || not_obscured ))
grabButton( None );
return;
}
if( isActive() && !workspace()->forcedGlobalMouseGrab()) // see Workspace::establishTabBoxGrab()
{
// first grab all modifier combinations
XGrabButton(tqt_xdisplay(), AnyButton, AnyModifier, wrapperId(), FALSE,
ButtonPressMask,
GrabModeSync, GrabModeAsync,
None, None );
// remove the grab for no modifiers only if the window
// is unobscured or if the user doesn't want click raise
// (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is
// the most recently raised window)
bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this;
if( !options->clickRaise || not_obscured )
ungrabButton( None );
else
grabButton( None );
ungrabButton( ShiftMask );
ungrabButton( ControlMask );
ungrabButton( ControlMask | ShiftMask );
}
else
{
XUngrabButton( tqt_xdisplay(), AnyButton, AnyModifier, wrapperId());
// simply grab all modifier combinations
XGrabButton(tqt_xdisplay(), AnyButton, AnyModifier, wrapperId(), FALSE,
ButtonPressMask,
GrabModeSync, GrabModeAsync,
None, None );
}
}
int qtToX11Button( TQt::ButtonState button )
{
if( button == Qt::LeftButton )
return Button1;
else if( button == Qt::MidButton )
return Button2;
else if( button == Qt::RightButton )
return Button3;
return AnyButton;
}
int qtToX11State( TQt::ButtonState state )
{
int ret = 0;
if( state & Qt::LeftButton )
ret |= Button1Mask;
if( state & Qt::MidButton )
ret |= Button2Mask;
if( state & Qt::RightButton )
ret |= Button3Mask;
if( state & TQt::ShiftButton )
ret |= ShiftMask;
if( state & TQt::ControlButton )
ret |= ControlMask;
if( state & TQt::AltButton )
ret |= KKeyNative::modX(KKey::ALT);
if( state & TQt::MetaButton )
ret |= KKeyNative::modX(KKey::WIN);
return ret;
}
// Qt propagates mouse events up the widget hierachy, which means events
// for the decoration window cannot be (easily) intercepted as X11 events
bool Client::eventFilter( TQObject* o, TQEvent* e )
{
if (TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(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 = tqt_xrootwin();
XQueryPointer(tqt_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(tqt_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(tqt_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 = tqt_xdisplay();
xe.root = tqt_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(tqt_xdisplay(), inner_window, True, ButtonPressMask,
(XEvent *)&xe);
}
xe.window = pointer_window;
XSendEvent(tqt_xdisplay(), pointer_window, True, ButtonPressMask,
(XEvent *)&xe);
// simulate a mouse button release
xe.type = ButtonRelease;
xe.display = tqt_xdisplay();
xe.root = tqt_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(tqt_xdisplay(), inner_window, True, ButtonReleaseMask,
(XEvent *)&xe);
}
xe.window = pointer_window;
XSendEvent(tqt_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 = tqt_xrootwin();
XQueryPointer(tqt_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(tqt_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(tqt_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 = tqt_xdisplay();
xe.root = tqt_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(tqt_xdisplay(), inner_window, True, ButtonPressMask,
(XEvent *)&xe);
}
xe.button = buttonPressed;
xe.window = pointer_window;
XSendEvent(tqt_xdisplay(), pointer_window, True, ButtonPressMask,
(XEvent *)&xe);
// simulate a mouse button release
xe.type = ButtonRelease;
xe.display = tqt_xdisplay();
xe.root = tqt_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(tqt_xdisplay(), inner_window, True, ButtonReleaseMask,
(XEvent *)&xe);
}
xe.state = buttonMask;
xe.button = buttonPressed;
xe.window = pointer_window;
XSendEvent(tqt_xdisplay(), pointer_window, True, ButtonReleaseMask,
(XEvent *)&xe);
drawDelayedShadow();
return true;
}
}
if( decoration == NULL
|| TQT_BASE_OBJECT(o) != TQT_BASE_OBJECT(decoration->widget()))
return false;
if( e->type() == TQEvent::MouseButtonPress )
{
TQMouseEvent* ev = TQT_TQMOUSEEVENT( e );
return buttonPressEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
}
if( e->type() == TQEvent::MouseButtonRelease )
{
TQMouseEvent* ev = TQT_TQMOUSEEVENT( e );
return buttonReleaseEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
}
if( e->type() == TQEvent::MouseMove ) // FRAME i fake z enter/leave?
{
TQMouseEvent* ev = TQT_TQMOUSEEVENT( e );
return motionNotifyEvent( decorationId(), qtToX11State( ev->state()),
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
}
if( e->type() == TQEvent::Wheel )
{
TQWheelEvent* ev = TQT_TQWHEELEVENT( e );
bool r = buttonPressEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()),
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
r = r || buttonReleaseEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()),
ev->x(), ev->y(), ev->globalX(), ev->globalY() );
return r;
}
if( e->type() == TQEvent::Resize )
{
TQResizeEvent* ev = TQT_TQRESIZEEVENT( e );
// Filter out resize events that inform about size different than frame size.
// This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync.
// These events only seem to be delayed events from initial resizing before show() was called
// on the decoration widget.
if( ev->size() != size())
return true;
}
return false;
}
// return value matters only when filtering events before decoration gets them
bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root )
{
if (buttonDown)
{
if( w == wrapperId())
XAllowEvents(tqt_xdisplay(), SyncPointer, CurrentTime ); //tqt_x_time);
return true;
}
if( w == wrapperId() || w == frameId() || w == decorationId())
{ // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
// FRAME something out of this would be processed before it gets decorations
updateUserTime();
workspace()->setWasUserInteraction();
uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ?
KKeyNative::modX(KKey::WIN) :
KKeyNative::modX(KKey::ALT);
bool bModKeyHeld = keyModX != 0 && ( state & KKeyNative::accelModMaskX()) == keyModX;
if( isSplash()
&& button == Button1 && !bModKeyHeld )
{ // hide splashwindow if the user clicks on it
hideClient( true );
if( w == wrapperId())
XAllowEvents(tqt_xdisplay(), SyncPointer, CurrentTime ); //tqt_x_time);
return true;
}
Options::MouseCommand com = Options::MouseNothing;
bool was_action = false;
bool perform_handled = false;
if ( bModKeyHeld )
{
was_action = true;
switch (button)
{
case Button1:
com = options->commandAll1();
break;
case Button2:
com = options->commandAll2();
break;
case Button3:
com = options->commandAll3();
break;
case Button4:
case Button5:
com = options->operationWindowMouseWheel( button == Button4 ? 120 : -120 );
break;
}
}
else
{ // inactive inner window
if( !isActive() && w == wrapperId())
{
was_action = true;
perform_handled = true;
switch (button)
{
case Button1:
com = options->commandWindow1();
break;
case Button2:
com = options->commandWindow2();
break;
case Button3:
com = options->commandWindow3();
break;
default:
com = Options::MouseActivateAndPassClick;
}
}
// active inner window
if( isActive() && w == wrapperId()
&& options->clickRaise && button < 4 ) // exclude wheel
{
com = Options::MouseActivateRaiseAndPassClick;
was_action = true;
perform_handled = true;
}
}
if( was_action )
{
bool replay = performMouseCommand( com, TQPoint( x_root, y_root), perform_handled );
if ( isSpecialWindow())
replay = TRUE;
if( w == wrapperId()) // these can come only from a grab
XAllowEvents(tqt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //tqt_x_time);
return true;
}
}
if( w == wrapperId()) // these can come only from a grab
{
XAllowEvents(tqt_xdisplay(), ReplayPointer, CurrentTime ); //tqt_x_time);
return true;
}
if( w == decorationId())
return false; // don't eat decoration events
if( w == frameId())
processDecorationButtonPress( button, state, x, y, x_root, y_root );
return true;
}
// this function processes button press events only after decoration decides not to handle them,
// unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them
void Client::processDecorationButtonPress( int button, int /*state*/, int x, int y, int x_root, int y_root )
{
Options::MouseCommand com = Options::MouseNothing;
bool active = isActive();
if ( !wantsInput() ) // we cannot be active, use it anyway
active = TRUE;
if ( button == Button1 )
com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1();
else if ( button == Button2 )
com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2();
else if ( button == Button3 )
com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
if( button == Button1
&& com != Options::MouseOperationsMenu // actions where it's not possible to get the matching
&& com != Options::MouseMinimize ) // mouse release event
{
mode = mousePosition( TQPoint( x, y ));
buttonDown = TRUE;
moveOffset = TQPoint( x, y );
invertedMoveOffset = rect().bottomRight() - moveOffset;
unrestrictedMoveResize = false;
setCursor( mode ); // update to sizeAllCursor if about to move
}
performMouseCommand( com, TQPoint( x_root, y_root ));
}
// called from decoration
void Client::processMousePressEvent( TQMouseEvent* e )
{
if( e->type() != TQEvent::MouseButtonPress )
{
kdWarning() << "processMousePressEvent()" << endl;
return;
}
int button;
switch( e->button())
{
case Qt::LeftButton:
button = Button1;
break;
case Qt::MidButton:
button = Button2;
break;
case Qt::RightButton:
button = Button3;
break;
default:
return;
}
processDecorationButtonPress( button, e->state(), e->x(), e->y(), e->globalX(), e->globalY());
}
// return value matters only when filtering events before decoration gets them
bool Client::buttonReleaseEvent( Window w, int /*button*/, int state, int x, int y, int x_root, int y_root )
{
if( w == decorationId() && !buttonDown)
return false;
if( w == wrapperId())
{
XAllowEvents(tqt_xdisplay(), SyncPointer, CurrentTime ); //tqt_x_time);
return true;
}
if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
return true;
x = this->x(); // translate from grab window to local coords
y = this->y();
if ( (state & ( Button1Mask & Button2Mask & Button3Mask )) == 0 )
{
buttonDown = FALSE;
if ( moveResizeMode )
{
finishMoveResize( false );
// mouse position is still relative to old Client position, adjust it
TQPoint mousepos( x_root - x, y_root - y );
mode = mousePosition( mousepos );
}
setCursor( mode );
}
return true;
}
static bool was_motion = false;
static Time next_motion_time = CurrentTime;
// Check whole incoming X queue for MotionNotify events
// checking whole queue is done by always returning False in the predicate.
// If there are more MotionNotify events in the queue, all until the last
// one may be safely discarded (if a ButtonRelease event comes, a MotionNotify
// will be faked from it, so there's no need to check other events).
// This helps avoiding being overloaded by being flooded from many events
// from the XServer.
static Bool motion_predicate( Display*, XEvent* ev, XPointer )
{
if( ev->type == MotionNotify )
{
was_motion = true;
next_motion_time = ev->xmotion.time; // for setting time
}
return False;
}
static bool waitingMotionEvent()
{
// The queue doesn't need to be checked until the X timestamp
// of processes events reaches the timestamp of the last suitable
// MotionNotify event in the queue.
if( next_motion_time != CurrentTime
&& timestampCompare( GET_QT_X_TIME(), next_motion_time ) < 0 )
return true;
was_motion = false;
XSync( tqt_xdisplay(), False ); // this helps to discard more MotionNotify events
XEvent dummy;
XCheckIfEvent( tqt_xdisplay(), &dummy, motion_predicate, NULL );
return was_motion;
}
// return value matters only when filtering events before decoration gets them
bool Client::motionNotifyEvent( Window w, int /*state*/, int x, int y, int x_root, int y_root )
{
if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
return true; // care only about the whole frame
if ( !buttonDown )
{
Position newmode = mousePosition( TQPoint( x, y ));
if( newmode != mode )
setCursor( newmode );
mode = newmode;
// reset the timestamp for the optimization, otherwise with long passivity
// the option in waitingMotionEvent() may be always true
next_motion_time = CurrentTime;
return false;
}
if( w == moveResizeGrabWindow())
{
x = this->x(); // translate from grab window to local coords
y = this->y();
}
if( !waitingMotionEvent())
handleMoveResize( x, y, x_root, y_root );
return true;
}
void Client::focusInEvent( XFocusInEvent* e )
{
if( e->window != window())
return; // only window gets focus
if ( e->mode == NotifyUngrab )
return; // we don't care
if ( e->detail == NotifyPointer )
return; // we don't care
if( !isShown( false ) || !isOnCurrentDesktop()) // we unmapped it, but it got focus meanwhile ->
return; // activateNextClient() already transferred focus elsewhere
// check if this client is in should_get_focus list or if activation is allowed
bool activate = workspace()->allowClientActivation( this, -1U, true );
workspace()->gotFocusIn( this ); // remove from should_get_focus list
if( activate )
setActive( TRUE );
else
{
workspace()->restoreFocus();
demandAttention();
}
}
// When a client loses focus, FocusOut events are usually immediatelly
// followed by FocusIn events for another client that gains the focus
// (unless the focus goes to another screen, or to the nofocus widget).
// Without this check, the former focused client would have to be
// deactivated, and after that, the new one would be activated, with
// a short time when there would be no active client. This can cause
// flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred
// from it to its transient, the fullscreen would be kept in the Active layer
// at the beginning and at the end, but not in the middle, when the active
// client would be temporarily none (see Client::belongToLayer() ).
// Therefore, the events queue is checked, whether it contains the matching
// FocusIn event, and if yes, deactivation of the previous client will
// be skipped, as activation of the new one will automatically deactivate
// previously active client.
static bool follows_focusin = false;
static bool follows_focusin_failed = false;
static Bool predicate_follows_focusin( Display*, XEvent* e, XPointer arg )
{
if( follows_focusin || follows_focusin_failed )
return False;
Client* c = ( Client* ) arg;
if( e->type == FocusIn && c->workspace()->findClient( WindowMatchPredicate( e->xfocus.window )))
{ // found FocusIn
follows_focusin = true;
return False;
}
// events that may be in the queue before the FocusIn event that's being
// searched for
if( e->type == FocusIn || e->type == FocusOut || e->type == KeymapNotify )
return False;
follows_focusin_failed = true; // a different event - stop search
return False;
}
static bool check_follows_focusin( Client* c )
{
follows_focusin = follows_focusin_failed = false;
XEvent dummy;
// XCheckIfEvent() is used to make the search non-blocking, the predicate
// always returns False, so nothing is removed from the events queue.
// XPeekIfEvent() would block.
XCheckIfEvent( tqt_xdisplay(), &dummy, predicate_follows_focusin, (XPointer)c );
return follows_focusin;
}
void Client::focusOutEvent( XFocusOutEvent* e )
{
if( e->window != window())
return; // only window gets focus
if ( e->mode == NotifyGrab )
return; // we don't care
if ( isShade() )
return; // here neither
if ( e->detail != NotifyNonlinear
&& e->detail != NotifyNonlinearVirtual )
// SELI check all this
return; // hack for motif apps like netscape
if ( TQApplication::activePopupWidget() )
return;
if( !check_follows_focusin( this ))
setActive( FALSE );
}
// performs _NET_WM_MOVERESIZE
void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction )
{
if( direction == NET::Move )
performMouseCommand( Options::MouseMove, TQPoint( x_root, y_root ));
else if( moveResizeMode && direction == NET::MoveResizeCancel )
{
finishMoveResize( true );
buttonDown = FALSE;
setCursor( mode );
}
else if( direction >= NET::TopLeft && direction <= NET::Left )
{
static const Position convert[] =
{
PositionTopLeft,
PositionTop,
PositionTopRight,
PositionRight,
PositionBottomRight,
PositionBottom,
PositionBottomLeft,
PositionLeft
};
if(!isResizable() || isShade())
return;
if( moveResizeMode )
finishMoveResize( false );
buttonDown = TRUE;
moveOffset = TQPoint( x_root - x(), y_root - y()); // map from global
invertedMoveOffset = rect().bottomRight() - moveOffset;
unrestrictedMoveResize = false;
mode = convert[ direction ];
setCursor( mode );
if( !startMoveResize())
{
buttonDown = false;
setCursor( mode );
}
}
else if( direction == NET::KeyboardMove )
{ // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm
TQCursor::setPos( geometry().center() );
performMouseCommand( Options::MouseUnrestrictedMove, geometry().center());
}
else if( direction == NET::KeyboardSize )
{ // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm
TQCursor::setPos( geometry().bottomRight());
performMouseCommand( Options::MouseUnrestrictedResize, geometry().bottomRight());
}
}
void Client::keyPressEvent( uint key_code )
{
updateUserTime();
if ( !isMove() && !isResize() )
return;
bool is_control = key_code & Qt::CTRL;
bool is_alt = key_code & Qt::ALT;
key_code = key_code & 0xffff;
int delta = is_control?1:is_alt?32:8;
TQPoint pos = TQCursor::pos();
switch ( key_code )
{
case Key_Left:
pos.rx() -= delta;
break;
case Key_Right:
pos.rx() += delta;
break;
case Key_Up:
pos.ry() -= delta;
break;
case Key_Down:
pos.ry() += delta;
break;
case Key_Space:
case Key_Return:
case Key_Enter:
finishMoveResize( false );
buttonDown = FALSE;
setCursor( mode );
break;
case Key_Escape:
finishMoveResize( true );
buttonDown = FALSE;
setCursor( mode );
break;
default:
return;
}
TQCursor::setPos( pos );
}
// ****************************************
// Group
// ****************************************
bool Group::groupEvent( XEvent* e )
{
unsigned long dirty[ 2 ];
leader_info->event( e, dirty, 2 ); // pass through the NET stuff
if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
getIcons();
if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
startupIdChanged();
return false;
}
} // namespace