kwin Library API Documentation

events.cpp

00001 /*****************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 You can Freely distribute this program under the GNU General Public
00009 License. See the file "COPYING" for the exact licensing terms.
00010 ******************************************************************/
00011 
00012 /*
00013 
00014  This file contains things relevant to handling incoming events.
00015 
00016 */
00017 
00018 #include "client.h"
00019 #include "workspace.h"
00020 #include "atoms.h"
00021 #include "tabbox.h"
00022 #include "group.h"
00023 #include "rules.h"
00024 
00025 #include <qwhatsthis.h>
00026 #include <kkeynative.h>
00027 #include <qapplication.h>
00028 
00029 #include <X11/extensions/shape.h>
00030 #include <X11/Xatom.h>
00031 
00032 extern Time qt_x_time;
00033 extern Atom qt_window_role;
00034 
00035 namespace KWinInternal
00036 {
00037 
00038 // ****************************************
00039 // WinInfo
00040 // ****************************************
00041 
00042 WinInfo::WinInfo( Client * c, Display * display, Window window,
00043     Window rwin, const unsigned long pr[], int pr_size )
00044     : NETWinInfo( display, window, rwin, pr, pr_size, NET::WindowManager ), m_client( c )
00045     {
00046     }
00047 
00048 void WinInfo::changeDesktop(int desktop)
00049     {
00050     m_client->workspace()->sendClientToDesktop( m_client, desktop, true );
00051     }
00052 
00053 void WinInfo::changeState( unsigned long state, unsigned long mask )
00054     {
00055     mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore
00056     mask &= ~NET::Hidden; // clients are not allowed to change this directly
00057     state &= mask; // for safety, clear all other bits
00058 
00059     if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) == 0 )
00060         m_client->setFullScreen( false, false );
00061     if ( (mask & NET::Max) == NET::Max )
00062         m_client->setMaximize( state & NET::MaxVert, state & NET::MaxHoriz );
00063     else if ( mask & NET::MaxVert )
00064         m_client->setMaximize( state & NET::MaxVert, m_client->maximizeMode() & Client::MaximizeHorizontal );
00065     else if ( mask & NET::MaxHoriz )
00066         m_client->setMaximize( m_client->maximizeMode() & Client::MaximizeVertical, state & NET::MaxHoriz );
00067 
00068     if ( mask & NET::Shaded )
00069         m_client->setShade( state & NET::Shaded ? ShadeNormal : ShadeNone );
00070     if ( mask & NET::KeepAbove)
00071         m_client->setKeepAbove( (state & NET::KeepAbove) != 0 );
00072     if ( mask & NET::KeepBelow)
00073         m_client->setKeepBelow( (state & NET::KeepBelow) != 0 );
00074     if( mask & NET::SkipTaskbar )
00075         m_client->setSkipTaskbar( ( state & NET::SkipTaskbar ) != 0, true );
00076     if( mask & NET::SkipPager )
00077         m_client->setSkipPager( ( state & NET::SkipPager ) != 0 );
00078     if( mask & NET::DemandsAttention )
00079         m_client->demandAttention(( state & NET::DemandsAttention ) != 0 );
00080     if( mask & NET::Modal )
00081         m_client->setModal( ( state & NET::Modal ) != 0 );
00082     // unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() )
00083     if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) != 0 )
00084         m_client->setFullScreen( true, false );
00085     }
00086 
00087 
00088 // ****************************************
00089 // RootInfo
00090 // ****************************************
00091 
00092 RootInfo::RootInfo( Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr[], int pr_num, int scr )
00093     : NETRootInfo3( dpy, w, name, pr, pr_num, scr )
00094     {
00095     workspace = ws;
00096     }
00097 
00098 void RootInfo::changeNumberOfDesktops(int n)
00099     {
00100     workspace->setNumberOfDesktops( n );
00101     }
00102 
00103 void RootInfo::changeCurrentDesktop(int d)
00104     {
00105     workspace->setCurrentDesktop( d );
00106     }
00107 
00108 void RootInfo::changeActiveWindow( Window w, NET::RequestSource src, Time timestamp, Window active_window )
00109     {
00110     if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
00111         {
00112         if( timestamp == CurrentTime )
00113             timestamp = c->userTime();
00114         if( src != NET::FromApplication && src != FromTool )
00115             src = NET::FromTool;
00116         if( src == NET::FromTool )
00117             workspace->activateClient( c, true ); // force
00118         else // NET::FromApplication
00119             {
00120             Client* c2;
00121             if( workspace->allowClientActivation( c, timestamp ))
00122                 workspace->activateClient( c );
00123             // if activation of the requestor's window would be allowed, allow activation too
00124             else if( active_window != None
00125                 && ( c2 = workspace->findClient( WindowMatchPredicate( active_window ))) != NULL
00126                 && workspace->allowClientActivation( c2,
00127                     timestampCompare( timestamp, c2->userTime() > 0 ? timestamp : c2->userTime())))
00128                 workspace->activateClient( c );
00129             else
00130                 c->demandAttention();
00131             }
00132         }
00133     }
00134 
00135 void RootInfo::restackWindow( Window w, RequestSource src, Window above, int detail, Time timestamp )
00136     {
00137     if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
00138         {
00139         if( timestamp == CurrentTime )
00140             timestamp = c->userTime();
00141         if( src != NET::FromApplication && src != FromTool )
00142             src = NET::FromTool;
00143         c->restackWindow( above, detail, src, timestamp, true );
00144         }
00145     }
00146 
00147 void RootInfo::gotTakeActivity( Window w, Time timestamp, long flags )
00148     {
00149     if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
00150         workspace->handleTakeActivity( c, timestamp, flags );
00151     }
00152 
00153 void RootInfo::closeWindow(Window w)
00154     {
00155     Client* c = workspace->findClient( WindowMatchPredicate( w ));
00156     if ( c )
00157         c->closeWindow();
00158     }
00159 
00160 void RootInfo::moveResize(Window w, int x_root, int y_root, unsigned long direction)
00161     {
00162     Client* c = workspace->findClient( WindowMatchPredicate( w ));
00163     if ( c )
00164         {
00165         updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp
00166         c->NETMoveResize( x_root, y_root, (Direction)direction);
00167         }
00168     }
00169 
00170 void RootInfo::moveResizeWindow(Window w, int flags, int x, int y, int width, int height )
00171     {
00172     Client* c = workspace->findClient( WindowMatchPredicate( w ));
00173     if ( c )
00174         c->NETMoveResizeWindow( flags, x, y, width, height );
00175     }
00176 
00177 void RootInfo::gotPing( Window w, Time timestamp )
00178     {
00179     if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
00180         c->gotPing( timestamp );
00181     }
00182 
00183 // ****************************************
00184 // Workspace
00185 // ****************************************
00186 
00190 bool Workspace::workspaceEvent( XEvent * e )
00191     {
00192     if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) ) 
00193         {
00194         mouse_emulation = FALSE;
00195         XUngrabKeyboard( qt_xdisplay(), qt_x_time );
00196         }
00197 
00198     if ( e->type == PropertyNotify || e->type == ClientMessage ) 
00199         {
00200         if ( netCheck( e ) )
00201             return TRUE;
00202         }
00203 
00204     // events that should be handled before Clients can get them
00205     switch (e->type) 
00206         {
00207         case ButtonPress:
00208         case ButtonRelease:
00209             was_user_interaction = true;
00210         // fallthrough
00211         case MotionNotify:
00212             if ( tab_grab || control_grab )
00213                 {
00214                 tab_box->handleMouseEvent( e );
00215                 return TRUE;
00216                 }
00217             break;
00218         case KeyPress:
00219             {
00220             was_user_interaction = true;
00221             KKeyNative keyX( (XEvent*)e );
00222             uint keyQt = keyX.keyCodeQt();
00223             kdDebug(125) << "Workspace::keyPress( " << keyX.key().toString() << " )" << endl;
00224             if (movingClient)
00225                 {
00226                 movingClient->keyPressEvent(keyQt);
00227                 return true;
00228                 }
00229             if( tab_grab || control_grab )
00230                 {
00231                 tabBoxKeyPress( keyX );
00232                 return true;
00233                 }
00234             break;
00235             }
00236         case KeyRelease:
00237             was_user_interaction = true;
00238             if( tab_grab || control_grab )
00239                 {
00240                 tabBoxKeyRelease( e->xkey );
00241                 return true;
00242                 }
00243             break;
00244         };
00245 
00246     if( Client* c = findClient( WindowMatchPredicate( e->xany.window )))
00247         {
00248         if( c->windowEvent( e ))
00249             return true;
00250         }
00251     else if( Client* c = findClient( WrapperIdMatchPredicate( e->xany.window )))
00252         {
00253         if( c->windowEvent( e ))
00254             return true;
00255         }
00256     else if( Client* c = findClient( FrameIdMatchPredicate( e->xany.window )))
00257         {
00258         if( c->windowEvent( e ))
00259             return true;
00260         }
00261     else
00262         {
00263         Window special = findSpecialEventWindow( e );
00264         if( special != None )
00265             if( Client* c = findClient( WindowMatchPredicate( special )))
00266                 {
00267                 if( c->windowEvent( e ))
00268                     return true;
00269                 }
00270         }
00271     if( movingClient != NULL && movingClient->moveResizeGrabWindow() == e->xany.window
00272         && ( e->type == MotionNotify || e->type == ButtonPress || e->type == ButtonRelease ))
00273         {
00274         if( movingClient->windowEvent( e ))
00275             return true;
00276         }
00277 
00278     switch (e->type) 
00279         {
00280         case CreateNotify:
00281             if ( e->xcreatewindow.parent == root &&
00282                  !QWidget::find( e->xcreatewindow.window) &&
00283                  !e->xcreatewindow.override_redirect )
00284             {
00285         // see comments for allowClientActivation()
00286             XChangeProperty(qt_xdisplay(), e->xcreatewindow.window,
00287                             atoms->kde_net_wm_user_creation_time, XA_CARDINAL,
00288                             32, PropModeReplace, (unsigned char *)&qt_x_time, 1);
00289             }
00290         break;
00291 
00292     case UnmapNotify:
00293             {
00294         // check for system tray windows
00295             if ( removeSystemTrayWin( e->xunmap.window, true ) ) 
00296                 {
00297         // If the system tray gets destroyed, the system tray
00298         // icons automatically get unmapped, reparented and mapped
00299         // again to the closest non-client ancestor due to
00300         // QXEmbed's SaveSet feature. Unfortunatly with kicker
00301         // this closest ancestor is not the root window, but our
00302         // decoration, so we reparent explicitely back to the root
00303         // window.
00304                 XEvent ev;
00305                 WId w = e->xunmap.window;
00306                 if ( XCheckTypedWindowEvent (qt_xdisplay(), w,
00307                                              ReparentNotify, &ev) )
00308                     {
00309                     if ( ev.xreparent.parent != root ) 
00310                         {
00311                         XReparentWindow( qt_xdisplay(), w, root, 0, 0 );
00312                         addSystemTrayWin( w );
00313                         }
00314                     }
00315                 return TRUE;
00316                 }
00317 
00318             return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt
00319             }
00320         case MapNotify:
00321 
00322             return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt
00323 
00324         case ReparentNotify:
00325             {
00326         //do not confuse Qt with these events. After all, _we_ are the
00327         //window manager who does the reparenting.
00328             return TRUE;
00329             }
00330         case DestroyNotify:
00331             {
00332             if ( removeSystemTrayWin( e->xdestroywindow.window, false ) )
00333                 return TRUE;
00334             return false;
00335             }
00336         case MapRequest:
00337             {
00338             updateXTime();
00339 
00340             // e->xmaprequest.window is different from e->xany.window
00341             // TODO this shouldn't be necessary now
00342             Client* c = findClient( WindowMatchPredicate( e->xmaprequest.window ));
00343             if ( !c ) 
00344                 {
00345 // don't check for the parent being the root window, this breaks when some app unmaps
00346 // a window, changes something and immediately maps it back, without giving KWin
00347 // a chance to reparent it back to root
00348 // since KWin can get MapRequest only for root window children and
00349 // children of WindowWrapper (=clients), the check is AFAIK useless anyway
00350 // Note: Now the save-set support in Client::mapRequestEvent() actually requires that
00351 // this code doesn't check the parent to be root.
00352 //            if ( e->xmaprequest.parent == root ) { //###TODO store previously destroyed client ids
00353                 if ( addSystemTrayWin( e->xmaprequest.window ) )
00354                     return TRUE;
00355                 c = createClient( e->xmaprequest.window, false );
00356                 if ( c != NULL && root != qt_xrootwin() ) 
00357                     { // TODO what is this?
00358                     // TODO may use QWidget::create
00359                     XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 );
00360                     }
00361                 if( c == NULL ) // refused to manage, simply map it (most probably override redirect)
00362                     XMapRaised( qt_xdisplay(), e->xmaprequest.window );
00363                 return true;
00364                 }
00365             if ( c ) 
00366                 {
00367                 c->windowEvent( e );
00368                 if ( !c->wantsTabFocus())
00369                     focus_chain.remove( c );  // TODO move focus_chain changes to functions
00370                 return true;
00371                 }
00372             break;
00373             }
00374         case EnterNotify:
00375             {
00376             if ( QWhatsThis::inWhatsThisMode() )
00377                 {
00378                 QWidget* w = QWidget::find( e->xcrossing.window );
00379                 if ( w )
00380                     QWhatsThis::leaveWhatsThisMode();
00381                 }
00382             if( electricBorder(e))
00383                 return true;
00384             break;
00385             }
00386         case LeaveNotify:
00387             {
00388             if ( !QWhatsThis::inWhatsThisMode() )
00389                 break;
00390             // TODO is this cliente ever found, given that client events are searched above?
00391             Client* c = findClient( FrameIdMatchPredicate( e->xcrossing.window ));
00392             if ( c && e->xcrossing.detail != NotifyInferior )
00393                 QWhatsThis::leaveWhatsThisMode();
00394             break;
00395             }
00396         case ConfigureRequest:
00397             {
00398             if ( e->xconfigurerequest.parent == root ) 
00399                 {
00400                 XWindowChanges wc;
00401                 unsigned int value_mask = 0;
00402                 wc.border_width = 0;
00403                 wc.x = e->xconfigurerequest.x;
00404                 wc.y = e->xconfigurerequest.y;
00405                 wc.width = e->xconfigurerequest.width;
00406                 wc.height = e->xconfigurerequest.height;
00407                 wc.sibling = None;
00408                 wc.stack_mode = Above;
00409                 value_mask = e->xconfigurerequest.value_mask | CWBorderWidth;
00410                 XConfigureWindow( qt_xdisplay(), e->xconfigurerequest.window, value_mask, &wc );
00411                 return true;
00412                 }
00413             break;
00414             }
00415         case KeyPress:
00416             if ( mouse_emulation )
00417                 return keyPressMouseEmulation( e->xkey );
00418             break;
00419         case KeyRelease:
00420             if ( mouse_emulation )
00421                 return FALSE;
00422             break;
00423         case FocusIn:
00424             if( e->xfocus.window == rootWin()
00425                 && ( e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot ))
00426                 {
00427                 updateXTime(); // focusToNull() uses qt_x_time, which is old now (FocusIn has no timestamp)
00428                 Window focus;
00429                 int revert;
00430                 XGetInputFocus( qt_xdisplay(), &focus, &revert );
00431                 if( focus == None || focus == PointerRoot )
00432                     {
00433                     //kdWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" << endl;
00434                     Client *c = mostRecentlyActivatedClient();
00435                     if( c != NULL )
00436                         requestFocus( c, true );
00437                     else if( activateNextClient( NULL ))
00438                         ; // ok, activated
00439                     else
00440                         focusToNull();
00441                     }
00442                 }
00443             // fall through
00444         case FocusOut:
00445             return true; // always eat these, they would tell Qt that KWin is the active app
00446         case ClientMessage:
00447             if( electricBorder( e ))
00448                 return true;
00449             break;
00450         default:
00451             break;
00452         }
00453     return FALSE;
00454     }
00455 
00456 // Some events don't have the actual window which caused the event
00457 // as e->xany.window (e.g. ConfigureRequest), but as some other
00458 // field in the XEvent structure.
00459 Window Workspace::findSpecialEventWindow( XEvent* e )
00460     {
00461     switch( e->type )
00462         {
00463         case CreateNotify:
00464             return e->xcreatewindow.window;
00465         case DestroyNotify:
00466             return e->xdestroywindow.window;
00467         case UnmapNotify:
00468             return e->xunmap.window;
00469         case MapNotify:
00470             return e->xmap.window;
00471         case MapRequest:
00472             return e->xmaprequest.window;
00473         case ReparentNotify:
00474             return e->xreparent.window;
00475         case ConfigureNotify:
00476             return e->xconfigure.window;
00477         case GravityNotify:
00478             return e->xgravity.window;
00479         case ConfigureRequest:
00480             return e->xconfigurerequest.window;
00481         case CirculateNotify:
00482             return e->xcirculate.window;
00483         case CirculateRequest:
00484             return e->xcirculaterequest.window;
00485         default:
00486             return None;
00487         };
00488     }
00489 
00493 bool Workspace::netCheck( XEvent* e )
00494     {
00495     unsigned int dirty = rootInfo->event( e );
00496 
00497     if ( dirty & NET::DesktopNames )
00498         saveDesktopSettings();
00499 
00500     return dirty != 0;
00501     }
00502 
00503 
00504 // ****************************************
00505 // Client
00506 // ****************************************
00507 
00511 bool Client::windowEvent( XEvent* e )
00512     {
00513     if( e->xany.window == window()) // avoid doing stuff on frame or wrapper
00514         {
00515         unsigned long dirty[ 2 ];
00516         info->event( e, dirty, 2 ); // pass through the NET stuff
00517 
00518         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 )
00519             fetchName();
00520         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName ) != 0 )
00521             fetchIconicName();
00522         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut ) != 0
00523             || ( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut ) != 0 )
00524             {
00525             if( isTopMenu())  // the fallback mode of KMenuBar may alter the strut
00526                 checkWorkspacePosition();  // restore it
00527             workspace()->updateClientArea();
00528             }
00529         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
00530             getIcons();
00531         // Note there's a difference between userTime() and info->userTime()
00532         // info->userTime() is the value of the property, userTime() also includes
00533         // updates of the time done by KWin (ButtonPress on windowrapper etc.).
00534         if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime ) != 0 )
00535             {
00536             workspace()->setWasUserInteraction();
00537             updateUserTime( info->userTime());
00538             }
00539         if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
00540             startupIdChanged();
00541         }
00542 
00543 // TODO move all focus handling stuff to separate file?
00544     switch (e->type) 
00545         {
00546         case UnmapNotify:
00547             unmapNotifyEvent( &e->xunmap );
00548             break;
00549         case DestroyNotify:
00550             destroyNotifyEvent( &e->xdestroywindow );
00551             break;
00552         case MapRequest:
00553             // this one may pass the event to workspace
00554             return mapRequestEvent( &e->xmaprequest );
00555         case ConfigureRequest:
00556             configureRequestEvent( &e->xconfigurerequest );
00557             break;
00558         case PropertyNotify:
00559             propertyNotifyEvent( &e->xproperty );
00560             break;
00561         case KeyPress:
00562             updateUserTime();
00563             workspace()->setWasUserInteraction();
00564             break;
00565         case ButtonPress:
00566             updateUserTime();
00567             workspace()->setWasUserInteraction();
00568             buttonPressEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
00569                 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
00570             break;
00571         case KeyRelease:
00572     // don't update user time on releases
00573     // e.g. if the user presses Alt+F2, the Alt release
00574     // would appear as user input to the currently active window
00575             break;
00576         case ButtonRelease:
00577     // don't update user time on releases
00578     // e.g. if the user presses Alt+F2, the Alt release
00579     // would appear as user input to the currently active window
00580             buttonReleaseEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
00581                 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
00582             break;
00583         case MotionNotify:
00584             motionNotifyEvent( e->xmotion.window, e->xmotion.state,
00585                 e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root );
00586             break;
00587         case EnterNotify:
00588             enterNotifyEvent( &e->xcrossing );
00589             // MotionNotify is guaranteed to be generated only if the mouse
00590             // move start and ends in the window; for cases when it only
00591             // starts or only ends there, Enter/LeaveNotify are generated.
00592             // Fake a MotionEvent in such cases to make handle of mouse
00593             // events simpler (Qt does that too).
00594             motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
00595                 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
00596             break;
00597         case LeaveNotify:
00598             motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
00599                 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
00600             leaveNotifyEvent( &e->xcrossing );
00601             break;
00602         case FocusIn:
00603             focusInEvent( &e->xfocus );
00604             break;
00605         case FocusOut:
00606             focusOutEvent( &e->xfocus );
00607             break;
00608         case ReparentNotify:
00609             break;
00610         case ClientMessage:
00611             clientMessageEvent( &e->xclient );
00612             break;
00613         case ColormapChangeMask:
00614             if( e->xany.window == window())
00615             {
00616             cmap = e->xcolormap.colormap;
00617             if ( isActive() )
00618                 workspace()->updateColormap();
00619             }
00620             break;
00621         case VisibilityNotify:
00622             visibilityNotifyEvent( &e->xvisibility );
00623             break;
00624         default:
00625             if( e->xany.window == window())
00626             {
00627             if( e->type == Shape::shapeEvent() )
00628                 {
00629                 is_shape = Shape::hasShape( window()); // workaround for #19644
00630                 updateShape();
00631                 }
00632             }
00633             break;
00634         }
00635     return true; // eat all events
00636     }
00637 
00641 bool Client::mapRequestEvent( XMapRequestEvent* e )
00642     {
00643     if( e->window != window())
00644         {
00645         // Special support for the save-set feature, which is a bit broken.
00646         // If there's a window from one client embedded in another one,
00647         // e.g. using XEMBED, and the embedder suddenly looses its X connection,
00648         // save-set will reparent the embedded window to its closest ancestor
00649         // that will remains. Unfortunately, with reparenting window managers,
00650         // this is not the root window, but the frame (or in KWin's case,
00651         // it's the wrapper for the client window). In this case,
00652         // the wrapper will get ReparentNotify for a window it won't know,
00653         // which will be ignored, and then it gets MapRequest, as save-set
00654         // always maps. Returning true here means that Workspace::workspaceEvent()
00655         // will handle this MapRequest and manage this window (i.e. act as if
00656         // it was reparented to root window).
00657         if( e->parent == wrapperId())
00658             return false;
00659         return true; // no messing with frame etc.
00660         }
00661     if( isTopMenu() && workspace()->managingTopMenus())
00662         return true; // kwin controls these
00663     switch ( mappingState() )
00664         {
00665         case WithdrawnState:
00666             assert( false ); // WMs are not supposed to manage clients in Withdrawn state,
00667 //        manage();      // after initial mapping manage() is called from createClient()
00668             break;
00669         case IconicState:
00670     // also copied in clientMessage()
00671             if( isMinimized())
00672                 unminimize();
00673             if( isShade())
00674                 setShade( ShadeNone );
00675             if( !isOnCurrentDesktop())
00676                 {
00677                 if( workspace()->allowClientActivation( this ))
00678                     workspace()->activateClient( this );
00679                 else
00680                     demandAttention();
00681                 }
00682             break;
00683         case NormalState:
00684         // TODO fake MapNotify?
00685             break;
00686         }
00687     return true;
00688     }
00689 
00693 void Client::unmapNotifyEvent( XUnmapEvent* e )
00694     {
00695     if( e->window != window())
00696         return;
00697     if( e->event != wrapperId())
00698         { // most probably event from root window when initially reparenting
00699         bool ignore = true;
00700         if( e->event == workspace()->rootWin() && e->send_event )
00701             ignore = false; // XWithdrawWindow()
00702         if( ignore )
00703             return;
00704         }
00705     switch( mappingState())
00706         {
00707         case IconicState:
00708             releaseWindow();
00709           return;
00710         case NormalState:
00711             // maybe we will be destroyed soon. Check this first.
00712             XEvent ev;
00713             if( XCheckTypedWindowEvent (qt_xdisplay(), window(),
00714                 DestroyNotify, &ev) ) // TODO I don't like this much
00715                 {
00716                 destroyClient(); // deletes this
00717                 return;
00718                 }
00719             releaseWindow();
00720           break;
00721     default:
00722         assert( false );
00723         }
00724     }
00725 
00726 void Client::destroyNotifyEvent( XDestroyWindowEvent* e )
00727     {
00728     if( e->window != window())
00729         return;
00730     destroyClient();
00731     }
00732     
00733     
00734 bool         blockAnimation = FALSE;
00735 
00739 void Client::clientMessageEvent( XClientMessageEvent* e )
00740     {
00741     if( e->window != window())
00742         return; // ignore frame/wrapper
00743     // WM_STATE
00744     if ( e->message_type == atoms->kde_wm_change_state )
00745         {
00746         if( isTopMenu() && workspace()->managingTopMenus())
00747             return; // kwin controls these
00748         if( e->data.l[ 1 ] )
00749             blockAnimation = true;
00750         if( e->data.l[ 0 ] == IconicState )
00751             minimize();
00752         else if( e->data.l[ 0 ] == NormalState )
00753             { // copied from mapRequest()
00754             if( isMinimized())
00755                 unminimize();
00756             if( isShade())
00757                 setShade( ShadeNone );
00758             if( !isOnCurrentDesktop())
00759                 {
00760                 if( workspace()->allowClientActivation( this ))
00761                     workspace()->activateClient( this );
00762                 else
00763                     demandAttention();
00764                 }
00765             }
00766         blockAnimation = false;
00767         }
00768     else if ( e->message_type == atoms->wm_change_state)
00769         {
00770         if( isTopMenu() && workspace()->managingTopMenus())
00771             return; // kwin controls these
00772         if ( e->data.l[0] == IconicState )
00773             minimize();
00774         return;
00775         }
00776     }
00777 
00778 
00782 void Client::configureRequestEvent( XConfigureRequestEvent* e )
00783     {
00784     if( e->window != window())
00785         return; // ignore frame/wrapper
00786     if ( isResize() || isMove())
00787         return; // we have better things to do right now
00788 
00789     if( fullscreen_mode == FullScreenNormal ) // refuse resizing of fullscreen windows
00790         { // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode
00791         sendSyntheticConfigureNotify();
00792         return;
00793         }
00794     if( isSplash() // no manipulations with splashscreens either
00795         || isTopMenu()) // topmenus neither
00796         {
00797         sendSyntheticConfigureNotify();
00798         return;
00799         }
00800 
00801     if ( e->value_mask & CWBorderWidth ) 
00802         {
00803         // first, get rid of a window border
00804         XWindowChanges wc;
00805         unsigned int value_mask = 0;
00806 
00807         wc.border_width = 0;
00808         value_mask = CWBorderWidth;
00809         XConfigureWindow( qt_xdisplay(), window(), value_mask, & wc );
00810         }
00811 
00812     if( e->value_mask & ( CWX | CWY | CWHeight | CWWidth ))
00813         configureRequest( e->value_mask, e->x, e->y, e->width, e->height, 0, false );
00814 
00815     if ( e->value_mask & CWStackMode )
00816         restackWindow( e->above, e->detail, NET::FromApplication, userTime(), false );
00817 
00818     // TODO sending a synthetic configure notify always is fine, even in cases where
00819     // the ICCCM doesn't require this - it can be though of as 'the WM decided to move
00820     // the window later'. The client should not cause that many configure request,
00821     // so this should not have any significant impact. With user moving/resizing
00822     // the it should be optimized though (see also Client::setGeometry()/plainResize()/move()).
00823     sendSyntheticConfigureNotify();
00824 
00825     // SELI TODO accept configure requests for isDesktop windows (because kdesktop
00826     // may get XRANDR resize event before kwin), but check it's still at the bottom?
00827     }
00828 
00829 
00833 void Client::propertyNotifyEvent( XPropertyEvent* e )
00834     {
00835     if( e->window != window())
00836         return; // ignore frame/wrapper
00837     switch ( e->atom ) 
00838         {
00839         case XA_WM_NORMAL_HINTS:
00840             getWmNormalHints();
00841             break;
00842         case XA_WM_NAME:
00843             fetchName();
00844             break;
00845         case XA_WM_ICON_NAME:
00846             fetchIconicName();
00847             break;
00848         case XA_WM_TRANSIENT_FOR:
00849             readTransient();
00850             break;
00851         case XA_WM_HINTS:
00852             getWMHints();
00853             getIcons(); // because KWin::icon() uses WMHints as fallback
00854             break;
00855         default:
00856             if ( e->atom == atoms->wm_protocols )
00857                 getWindowProtocols();
00858             else if (e->atom == atoms->wm_client_leader )
00859                 getWmClientLeader();
00860             else if( e->atom == qt_window_role )
00861                 window_role = staticWindowRole( window());
00862             else if( e->atom == atoms->motif_wm_hints )
00863                 getMotifHints();
00864             break;
00865         }
00866     }
00867 
00868 
00869 void Client::enterNotifyEvent( XCrossingEvent* e )
00870     {
00871     if( e->window != frameId())
00872         return; // care only about entering the whole frame
00873     if( e->mode == NotifyNormal ||
00874          ( !options->focusPolicyIsReasonable() &&
00875              e->mode == NotifyUngrab ) ) 
00876         {
00877 
00878         if (options->shadeHover && isShade()) 
00879             {
00880             delete shadeHoverTimer;
00881             shadeHoverTimer = new QTimer( this );
00882             connect( shadeHoverTimer, SIGNAL( timeout() ), this, SLOT( shadeHover() ));
00883             shadeHoverTimer->start( options->shadeHoverInterval, TRUE );
00884             }
00885 
00886         if ( options->focusPolicy == Options::ClickToFocus )
00887             return;
00888 
00889         if ( options->autoRaise && !isDesktop() &&
00890              !isDock() && !isTopMenu() && workspace()->focusChangeEnabled() &&
00891              workspace()->topClientOnDesktop( workspace()->currentDesktop()) != this ) 
00892             {
00893             delete autoRaiseTimer;
00894             autoRaiseTimer = new QTimer( this );
00895             connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) );
00896             autoRaiseTimer->start( options->autoRaiseInterval, TRUE  );
00897             }
00898 
00899         if ( options->focusPolicy !=  Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) )
00900             return;
00901         if ( options->delayFocus )
00902             workspace()->requestDelayFocus( this );
00903         else
00904             workspace()->requestFocus( this );
00905 
00906         return;
00907         }
00908     }
00909 
00910 void Client::leaveNotifyEvent( XCrossingEvent* e )
00911     {
00912     if( e->window != frameId())
00913         return; // care only about leaving the whole frame
00914     if ( e->mode == NotifyNormal ) 
00915         {
00916         if ( !buttonDown ) 
00917             {
00918             mode = PositionCenter;
00919             setCursor( arrowCursor );
00920             }
00921         bool lostMouse = !rect().contains( QPoint( e->x, e->y ) );
00922         // 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations
00923         // (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event
00924         // comes after leaving the rect) - so lets check if the pointer is really outside the window
00925 
00926         // TODO this still sucks if a window appears above this one - it should lose the mouse
00927         // if this window is another client, but not if it's a popup ... maybe after KDE3.1 :(
00928         // (repeat after me 'AARGHL!')
00929         if ( !lostMouse && e->detail != NotifyInferior ) 
00930             {
00931             int d1, d2, d3, d4;
00932             unsigned int d5;
00933             Window w, child;
00934             if( XQueryPointer( qt_xdisplay(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5 ) == False
00935                 || child == None )
00936                 lostMouse = true; // really lost the mouse
00937             }
00938         if ( lostMouse ) 
00939             {
00940             cancelAutoRaise();
00941             workspace()->cancelDelayFocus();
00942             delete shadeHoverTimer;
00943             shadeHoverTimer = 0;
00944             if ( shade_mode == ShadeHover && !moveResizeMode && !buttonDown )
00945                setShade( ShadeNormal );
00946             }
00947         if ( options->focusPolicy == Options::FocusStrictlyUnderMouse )
00948             if ( isActive() && lostMouse )
00949                 workspace()->requestFocus( 0 ) ;
00950         return;
00951         }
00952     }
00953 
00954 #define XCapL KKeyNative::modXLock()
00955 #define XNumL KKeyNative::modXNumLock()
00956 #define XScrL KKeyNative::modXScrollLock()
00957 void Client::grabButton( int modifier )
00958     {
00959     unsigned int mods[ 8 ] = 
00960         {
00961         0, XCapL, XNumL, XNumL | XCapL,
00962         XScrL, XScrL | XCapL,
00963         XScrL | XNumL, XScrL | XNumL | XCapL
00964         };
00965     for( int i = 0;
00966          i < 8;
00967          ++i )
00968         XGrabButton( qt_xdisplay(), AnyButton,
00969             modifier | mods[ i ],
00970             wrapperId(),  FALSE, ButtonPressMask,
00971             GrabModeSync, GrabModeAsync, None, None );
00972     }
00973 
00974 void Client::ungrabButton( int modifier )
00975     {
00976     unsigned int mods[ 8 ] = 
00977         {
00978         0, XCapL, XNumL, XNumL | XCapL,
00979         XScrL, XScrL | XCapL,
00980         XScrL | XNumL, XScrL | XNumL | XCapL
00981         };
00982     for( int i = 0;
00983          i < 8;
00984          ++i )
00985         XUngrabButton( qt_xdisplay(), AnyButton,
00986             modifier | mods[ i ], wrapperId());
00987     }
00988 #undef XCapL
00989 #undef XNumL
00990 #undef XScrL
00991 
00992 /*
00993   Releases the passive grab for some modifier combinations when a
00994   window becomes active. This helps broken X programs that
00995   missinterpret LeaveNotify events in grab mode to work properly
00996   (Motif, AWT, Tk, ...)
00997  */
00998 void Client::updateMouseGrab()
00999     {                   // see Workspace::establishTabBoxGrab()
01000     if( isActive() && !workspace()->forcedGlobalMouseGrab())
01001         {
01002         // remove the grab for no modifiers only if the window
01003         // is unobscured or if the user doesn't want click raise
01004         if( !options->clickRaise || not_obscured )
01005             ungrabButton( None );
01006         else
01007             grabButton( None );
01008         ungrabButton( ShiftMask );
01009         ungrabButton( ControlMask );
01010         ungrabButton( ControlMask | ShiftMask );
01011         }
01012     else
01013         {
01014         XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, wrapperId());
01015         // simply grab all modifier combinations
01016         XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, wrapperId(), FALSE,
01017             ButtonPressMask,
01018             GrabModeSync, GrabModeAsync,
01019             None, None );
01020         }
01021     }
01022 
01023 int qtToX11Button( Qt::ButtonState button )
01024     {
01025     if( button == Qt::LeftButton )
01026         return Button1;
01027     else if( button == Qt::MidButton )
01028         return Button2;
01029     else if( button == Qt::RightButton )
01030         return Button3;
01031     return AnyButton;
01032     }
01033     
01034 int qtToX11State( Qt::ButtonState state )
01035     {
01036     int ret = 0;
01037     if( state & Qt::LeftButton )
01038         ret |= Button1Mask;
01039     if( state & Qt::MidButton )
01040         ret |= Button2Mask;
01041     if( state & Qt::RightButton )
01042         ret |= Button3Mask;
01043     if( state & Qt::ShiftButton )
01044         ret |= ShiftMask;
01045     if( state & Qt::ControlButton )
01046         ret |= ControlMask;
01047     if( state & Qt::AltButton )
01048         ret |= KKeyNative::modX(KKey::ALT);
01049     if( state & Qt::MetaButton )
01050         ret |= KKeyNative::modX(KKey::WIN);
01051     return ret;
01052     }
01053 
01054 // Qt propagates mouse events up the widget hierachy, which means events
01055 // for the decoration window cannot be (easily) intercepted as X11 events
01056 bool Client::eventFilter( QObject* o, QEvent* e )
01057     {
01058     if( decoration == NULL
01059         || o != decoration->widget())
01060         return false;
01061     if( e->type() == QEvent::MouseButtonPress )
01062         {
01063         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01064         return buttonPressEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
01065             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01066         }
01067     if( e->type() == QEvent::MouseButtonRelease )
01068         {
01069         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01070         return buttonReleaseEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
01071             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01072         }
01073     if( e->type() == QEvent::MouseMove ) // FRAME i fake z enter/leave?
01074         {
01075         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01076         return motionNotifyEvent( decorationId(), qtToX11State( ev->state()),
01077             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01078         }
01079     if( e->type() == QEvent::Resize )
01080         {
01081         QResizeEvent* ev = static_cast< QResizeEvent* >( e );
01082         // Filter out resize events that inform about size different than frame size.
01083         // This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync.
01084         // These events only seem to be delayed events from initial resizing before show() was called
01085         // on the decoration widget.
01086         if( ev->size() != size())
01087             return true;
01088         }
01089     return false;
01090     }
01091 
01092 // return value matters only when filtering events before decoration gets them
01093 bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root )
01094     {
01095     if (buttonDown)
01096         {
01097         if( w == wrapperId())
01098             XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01099         return true;
01100         }
01101 
01102     if( w == wrapperId() || w == frameId() || w == decorationId())
01103         { // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
01104         updateUserTime();
01105         workspace()->setWasUserInteraction();
01106         uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ?
01107             KKeyNative::modX(KKey::WIN) :
01108             KKeyNative::modX(KKey::ALT);
01109         bool bModKeyHeld = keyModX != 0 && ( state & KKeyNative::accelModMaskX()) == keyModX;
01110 
01111         if( isSplash()
01112             && button == Button1 && !bModKeyHeld )
01113             { // hide splashwindow if the user clicks on it
01114             hideClient( true );
01115             if( w == wrapperId())
01116                     XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01117             return true;
01118             }
01119 
01120         Options::MouseCommand com = Options::MouseNothing;
01121         bool was_action = false;
01122         bool perform_handled = false;
01123         if (keyModX != 0 && (state & keyModX) &&  (state & ControlMask))
01124             {
01125             switch (button)
01126                 {
01127                 case Button5:
01128                     if (opacity_ > 0)
01129                         {
01130                         setOpacity(TRUE, (opacity_ > 0xCCCCCCC) ? opacity_ - 0xCCCCCCC : 0);
01131                         custom_opacity = true;
01132                         }
01133                     XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime );
01134                     return true;
01135                 case Button4:
01136                     if (opacity_ < 0xFFFFFFFF)
01137                         {
01138                         if (opacity_ < 0xF3333333){
01139                             setOpacity(TRUE, opacity_ + 0xCCCCCCC);
01140                             custom_opacity = true;
01141                             }
01142                         else{
01143                             setOpacity(FALSE, 0xFFFFFFFF);
01144                             custom_opacity = false;
01145                             }
01146                         }
01147                     XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime );
01148                     return true;
01149                 }
01150             }
01151         else if ( bModKeyHeld )
01152             {
01153             was_action = true;
01154             switch (button) 
01155                 {
01156                 case Button1:
01157                     com = options->commandAll1();
01158                     break;
01159                 case Button2:
01160                     com = options->commandAll2();
01161                     break;
01162                 case Button3:
01163                     com = options->commandAll3();
01164                     break;
01165                 }
01166             }
01167         else
01168             { // inactive inner window
01169             if( !isActive() && w == wrapperId())
01170                 {
01171                 was_action = true;
01172                 perform_handled = true;
01173                 switch (button) 
01174                     {
01175                     case Button1:
01176                         com = options->commandWindow1();
01177                         break;
01178                     case Button2:
01179                         com = options->commandWindow2();
01180                         break;
01181                     case Button3:
01182                         com = options->commandWindow3();
01183                         break;
01184                     default:
01185                         com = Options::MouseActivateAndPassClick;
01186                     }
01187                 }
01188             // active inner window
01189             if( isActive() && w == wrapperId()
01190                 && options->clickRaise && button < 4 ) // exclude wheel
01191                 {
01192                 com = Options::MouseActivateRaiseAndPassClick;
01193                 was_action = true;
01194                 perform_handled = true;
01195                 }
01196             }
01197         if( was_action )
01198             {
01199             bool replay = performMouseCommand( com, QPoint( x_root, y_root), perform_handled );
01200 
01201             if ( isSpecialWindow() && !isOverride())
01202                 replay = TRUE;
01203 
01204                 if( w == wrapperId()) // these can come only from a grab
01205                     XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //qt_x_time);
01206             return true;
01207             }
01208         }
01209 
01210     if( w == wrapperId()) // these can come only from a grab
01211         {
01212         XAllowEvents(qt_xdisplay(), ReplayPointer, CurrentTime ); //qt_x_time);
01213         return true;
01214         }
01215     if( w == decorationId())
01216         return false; // don't eat decoration events
01217     if( w == frameId())
01218         processDecorationButtonPress( button, state, x, y, x_root, y_root );
01219     return true;
01220     }
01221 
01222 
01223 // this function processes button press events only after decoration decides not to handle them,
01224 // unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them
01225 void Client::processDecorationButtonPress( int button, int /*state*/, int x, int y, int x_root, int y_root )
01226     {
01227     Options::MouseCommand com = Options::MouseNothing;
01228     bool active = isActive();
01229     if ( !wantsInput() ) // we cannot be active, use it anyway
01230         active = TRUE;
01231 
01232     if ( button == Button1 )
01233         com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1();
01234     else if ( button == Button2 )
01235         com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2();
01236     else if ( button == Button3 )
01237         com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
01238     if( button == Button1
01239         && com != Options::MouseOperationsMenu // actions where it's not possible to get the matching
01240         && com != Options::MouseMinimize )  // mouse release event
01241         {
01242         mode = mousePosition( QPoint( x, y ));
01243         buttonDown = TRUE;
01244         moveOffset = QPoint( x, y );
01245         invertedMoveOffset = rect().bottomRight() - moveOffset;
01246         unrestrictedMoveResize = false;
01247         setCursor( mode ); // update to sizeAllCursor if about to move
01248         }
01249     performMouseCommand( com, QPoint( x_root, y_root ));
01250     }
01251 
01252 // called from decoration
01253 void Client::processMousePressEvent( QMouseEvent* e )
01254     {
01255     if( e->type() != QEvent::MouseButtonPress )
01256         {
01257         kdWarning() << "processMousePressEvent()" << endl;
01258         return;
01259         }
01260     int button;
01261     switch( e->button())
01262         {
01263         case LeftButton:
01264             button = Button1;
01265           break;
01266         case MidButton:
01267             button = Button2;
01268           break;
01269         case RightButton:
01270             button = Button3;
01271           break;
01272         default:
01273             return;
01274         }
01275     processDecorationButtonPress( button, e->state(), e->x(), e->y(), e->globalX(), e->globalY());
01276     }
01277 
01278 // return value matters only when filtering events before decoration gets them
01279 bool Client::buttonReleaseEvent( Window w, int /*button*/, int state, int x, int y, int x_root, int y_root )
01280     {
01281     if( w == decorationId() && !buttonDown)
01282         return false;
01283     if( w == wrapperId())
01284         {
01285         XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01286         return true;
01287         }
01288     if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
01289         return true;
01290     x = this->x(); // translate from grab window to local coords
01291     y = this->y();
01292     if ( (state & ( Button1Mask & Button2Mask & Button3Mask )) == 0 )
01293         {
01294         buttonDown = FALSE;
01295         if ( moveResizeMode ) 
01296             {
01297             finishMoveResize( false );
01298             // mouse position is still relative to old Client position, adjust it
01299             QPoint mousepos( x_root - x, y_root - y );
01300             mode = mousePosition( mousepos );
01301             }
01302         setCursor( mode );
01303         }
01304     return true;
01305     }
01306 
01307 static bool was_motion = false;
01308 static Time next_motion_time = CurrentTime;
01309 // Check whole incoming X queue for MotionNotify events
01310 // checking whole queue is done by always returning False in the predicate.
01311 // If there are more MotionNotify events in the queue, all until the last
01312 // one may be safely discarded (if a ButtonRelease event comes, a MotionNotify
01313 // will be faked from it, so there's no need to check other events).
01314 // This helps avoiding being overloaded by being flooded from many events
01315 // from the XServer.
01316 static Bool motion_predicate( Display*, XEvent* ev, XPointer )
01317 {
01318     if( ev->type == MotionNotify )
01319         {
01320     was_motion = true;
01321         next_motion_time = ev->xmotion.time;  // for setting time
01322         }
01323     return False;
01324 }
01325 
01326 static bool waitingMotionEvent()
01327     {
01328 // The queue doesn't need to be checked until the X timestamp
01329 // of processes events reaches the timestamp of the last suitable
01330 // MotionNotify event in the queue.
01331     if( next_motion_time != CurrentTime
01332         && timestampCompare( qt_x_time, next_motion_time ) < 0 )
01333         return true;
01334     was_motion = false;
01335     XSync( qt_xdisplay(), False ); // this helps to discard more MotionNotify events
01336     XEvent dummy;
01337     XCheckIfEvent( qt_xdisplay(), &dummy, motion_predicate, NULL );
01338     return was_motion;
01339     }
01340 
01341 // return value matters only when filtering events before decoration gets them
01342 bool Client::motionNotifyEvent( Window w, int /*state*/, int x, int y, int x_root, int y_root )
01343     {
01344     if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
01345         return true; // care only about the whole frame
01346     if ( !buttonDown ) 
01347         {
01348         Position newmode = mousePosition( QPoint( x, y ));
01349         if( newmode != mode )
01350             setCursor( newmode );
01351         mode = newmode;
01352         // reset the timestamp for the optimization, otherwise with long passivity
01353         // the option in waitingMotionEvent() may be always true
01354         next_motion_time = CurrentTime;
01355         return false;
01356         }
01357     if( w == moveResizeGrabWindow())
01358         {
01359         x = this->x(); // translate from grab window to local coords
01360         y = this->y();
01361         }
01362     if( !waitingMotionEvent())
01363         handleMoveResize( x, y, x_root, y_root );
01364     return true;
01365     }
01366     
01367 void Client::focusInEvent( XFocusInEvent* e )
01368     {
01369     if( e->window != window())
01370         return; // only window gets focus
01371     if ( e->mode == NotifyUngrab )
01372         return; // we don't care
01373     if ( e->detail == NotifyPointer )
01374         return;  // we don't care
01375     if( !isShown( false ) || !isOnCurrentDesktop()) // we unmapped it, but it got focus meanwhile ->
01376         return;            // activateNextClient() already transferred focus elsewhere
01377     // check if this client is in should_get_focus list or if activation is allowed
01378     bool activate =  workspace()->allowClientActivation( this, -1U, true );
01379     workspace()->gotFocusIn( this ); // remove from should_get_focus list
01380     if( activate )
01381         setActive( TRUE );
01382     else
01383         {
01384         workspace()->restoreFocus();
01385         demandAttention();
01386         }
01387     }
01388 
01389 // When a client loses focus, FocusOut events are usually immediatelly
01390 // followed by FocusIn events for another client that gains the focus
01391 // (unless the focus goes to another screen, or to the nofocus widget).
01392 // Without this check, the former focused client would have to be
01393 // deactivated, and after that, the new one would be activated, with
01394 // a short time when there would be no active client. This can cause
01395 // flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred
01396 // from it to its transient, the fullscreen would be kept in the Active layer
01397 // at the beginning and at the end, but not in the middle, when the active
01398 // client would be temporarily none (see Client::belongToLayer() ).
01399 // Therefore, the events queue is checked, whether it contains the matching
01400 // FocusIn event, and if yes, deactivation of the previous client will
01401 // be skipped, as activation of the new one will automatically deactivate
01402 // previously active client.
01403 static bool follows_focusin = false;
01404 static bool follows_focusin_failed = false;
01405 static Bool predicate_follows_focusin( Display*, XEvent* e, XPointer arg )
01406     {
01407     if( follows_focusin || follows_focusin_failed )
01408         return False;
01409     Client* c = ( Client* ) arg;
01410     if( e->type == FocusIn && c->workspace()->findClient( WindowMatchPredicate( e->xfocus.window )))
01411         { // found FocusIn
01412         follows_focusin = true;
01413         return False;
01414         }
01415     // events that may be in the queue before the FocusIn event that's being
01416     // searched for
01417     if( e->type == FocusIn || e->type == FocusOut || e->type == KeymapNotify )
01418         return False;
01419     follows_focusin_failed = true; // a different event - stop search
01420     return False;
01421     }
01422 
01423 static bool check_follows_focusin( Client* c )
01424     {
01425     follows_focusin = follows_focusin_failed = false;
01426     XEvent dummy;
01427     // XCheckIfEvent() is used to make the search non-blocking, the predicate
01428     // always returns False, so nothing is removed from the events queue.
01429     // XPeekIfEvent() would block.
01430     XCheckIfEvent( qt_xdisplay(), &dummy, predicate_follows_focusin, (XPointer)c );
01431     return follows_focusin;
01432     }
01433 
01434 
01435 void Client::focusOutEvent( XFocusOutEvent* e )
01436     {
01437     if( e->window != window())
01438         return; // only window gets focus
01439     if ( e->mode == NotifyGrab )
01440         return; // we don't care
01441     if ( isShade() )
01442         return; // here neither
01443     if ( e->detail != NotifyNonlinear
01444         && e->detail != NotifyNonlinearVirtual )
01445         // SELI check all this
01446         return; // hack for motif apps like netscape
01447     if ( QApplication::activePopupWidget() )
01448         return;
01449     if( !check_follows_focusin( this ))
01450         setActive( FALSE );
01451     }
01452 
01453 void Client::visibilityNotifyEvent( XVisibilityEvent * e)
01454     {
01455     if( e->window != frameId())
01456         return; // care only about the whole frame
01457     bool new_not_obscured = e->state == VisibilityUnobscured;
01458     if( not_obscured == new_not_obscured )
01459         return;
01460     not_obscured = new_not_obscured;
01461     updateMouseGrab();
01462     }
01463 
01464 // performs _NET_WM_MOVERESIZE
01465 void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction )
01466     {
01467     if( direction == NET::Move )
01468         performMouseCommand( Options::MouseMove, QPoint( x_root, y_root ));
01469     else if( direction >= NET::TopLeft && direction <= NET::Left ) 
01470         {
01471         static const Position convert[] =
01472             {
01473             PositionTopLeft,
01474             PositionTop,
01475             PositionTopRight,
01476             PositionRight,
01477             PositionBottomRight,
01478             PositionBottom,
01479             PositionBottomLeft,
01480             PositionLeft
01481             };
01482         if(!isResizable() || isShade())
01483             return;
01484         if( moveResizeMode )
01485             finishMoveResize( false );
01486         buttonDown = TRUE;
01487         moveOffset = QPoint( x_root - x(), y_root - y()); // map from global
01488         invertedMoveOffset = rect().bottomRight() - moveOffset;
01489         unrestrictedMoveResize = false;
01490         mode = convert[ direction ];
01491         setCursor( mode );
01492         if( !startMoveResize())
01493             {
01494             buttonDown = false;
01495             setCursor( mode );
01496             }
01497         }
01498     else if( direction == NET::KeyboardMove )
01499         { // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm
01500         QCursor::setPos( geometry().center() );
01501         performMouseCommand( Options::MouseUnrestrictedMove, geometry().center());
01502         }
01503     else if( direction == NET::KeyboardSize )
01504         { // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm
01505         QCursor::setPos( geometry().bottomRight());
01506         performMouseCommand( Options::MouseUnrestrictedResize, geometry().bottomRight());
01507         }
01508     }
01509 
01510 void Client::keyPressEvent( uint key_code )
01511     {
01512     updateUserTime();
01513     if ( !isMove() && !isResize() )
01514         return;
01515     bool is_control = key_code & Qt::CTRL;
01516     bool is_alt = key_code & Qt::ALT;
01517     key_code = key_code & 0xffff;
01518     int delta = is_control?1:is_alt?32:8;
01519     QPoint pos = QCursor::pos();
01520     switch ( key_code ) 
01521         {
01522         case Key_Left:
01523             pos.rx() -= delta;
01524             break;
01525         case Key_Right:
01526             pos.rx() += delta;
01527             break;
01528         case Key_Up:
01529             pos.ry() -= delta;
01530             break;
01531         case Key_Down:
01532             pos.ry() += delta;
01533             break;
01534         case Key_Space:
01535         case Key_Return:
01536         case Key_Enter:
01537             finishMoveResize( false );
01538             buttonDown = FALSE;
01539             setCursor( mode );
01540             break;
01541         case Key_Escape:
01542             finishMoveResize( true );
01543             buttonDown = FALSE;
01544             setCursor( mode );
01545             break;
01546         default:
01547             return;
01548         }
01549     QCursor::setPos( pos );
01550     }
01551 
01552 // ****************************************
01553 // Group
01554 // ****************************************
01555 
01556 bool Group::groupEvent( XEvent* e )
01557     {
01558     unsigned long dirty[ 2 ];
01559     leader_info->event( e, dirty, 2 ); // pass through the NET stuff
01560     if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
01561         getIcons();
01562     if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
01563         startupIdChanged();
01564     return false;
01565     }
01566 
01567 
01568 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Sep 30 18:40:42 2005 by doxygen 1.4.3 written by Dimitri van Heesch, © 1997-2003