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     : NETRootInfo4( 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 void RootInfo::changeShowingDesktop( bool showing )
00184     {
00185     workspace->setShowingDesktop( showing );
00186     }
00187 
00188 // ****************************************
00189 // Workspace
00190 // ****************************************
00191 
00195 bool Workspace::workspaceEvent( XEvent * e )
00196     {
00197     if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) ) 
00198         {
00199         mouse_emulation = FALSE;
00200         XUngrabKeyboard( qt_xdisplay(), qt_x_time );
00201         }
00202 
00203     if ( e->type == PropertyNotify || e->type == ClientMessage ) 
00204         {
00205         if ( netCheck( e ) )
00206             return TRUE;
00207         }
00208 
00209     // events that should be handled before Clients can get them
00210     switch (e->type) 
00211         {
00212         case ButtonPress:
00213         case ButtonRelease:
00214             was_user_interaction = true;
00215         // fallthrough
00216         case MotionNotify:
00217             if ( tab_grab || control_grab )
00218                 {
00219                 tab_box->handleMouseEvent( e );
00220                 return TRUE;
00221                 }
00222             break;
00223         case KeyPress:
00224             {
00225             was_user_interaction = true;
00226             KKeyNative keyX( (XEvent*)e );
00227             uint keyQt = keyX.keyCodeQt();
00228             kdDebug(125) << "Workspace::keyPress( " << keyX.key().toString() << " )" << endl;
00229             if (movingClient)
00230                 {
00231                 movingClient->keyPressEvent(keyQt);
00232                 return true;
00233                 }
00234             if( tab_grab || control_grab )
00235                 {
00236                 tabBoxKeyPress( keyX );
00237                 return true;
00238                 }
00239             break;
00240             }
00241         case KeyRelease:
00242             was_user_interaction = true;
00243             if( tab_grab || control_grab )
00244                 {
00245                 tabBoxKeyRelease( e->xkey );
00246                 return true;
00247                 }
00248             break;
00249         };
00250 
00251     if( Client* c = findClient( WindowMatchPredicate( e->xany.window )))
00252         {
00253         if( c->windowEvent( e ))
00254             return true;
00255         }
00256     else if( Client* c = findClient( WrapperIdMatchPredicate( e->xany.window )))
00257         {
00258         if( c->windowEvent( e ))
00259             return true;
00260         }
00261     else if( Client* c = findClient( FrameIdMatchPredicate( e->xany.window )))
00262         {
00263         if( c->windowEvent( e ))
00264             return true;
00265         }
00266     else
00267         {
00268         Window special = findSpecialEventWindow( e );
00269         if( special != None )
00270             if( Client* c = findClient( WindowMatchPredicate( special )))
00271                 {
00272                 if( c->windowEvent( e ))
00273                     return true;
00274                 }
00275         }
00276     if( movingClient != NULL && movingClient->moveResizeGrabWindow() == e->xany.window
00277         && ( e->type == MotionNotify || e->type == ButtonPress || e->type == ButtonRelease ))
00278         {
00279         if( movingClient->windowEvent( e ))
00280             return true;
00281         }
00282 
00283     switch (e->type) 
00284         {
00285         case CreateNotify:
00286             if ( e->xcreatewindow.parent == root &&
00287                  !QWidget::find( e->xcreatewindow.window) &&
00288                  !e->xcreatewindow.override_redirect )
00289             {
00290         // see comments for allowClientActivation()
00291             XChangeProperty(qt_xdisplay(), e->xcreatewindow.window,
00292                             atoms->kde_net_wm_user_creation_time, XA_CARDINAL,
00293                             32, PropModeReplace, (unsigned char *)&qt_x_time, 1);
00294             }
00295         break;
00296 
00297     case UnmapNotify:
00298             {
00299         // check for system tray windows
00300             if ( removeSystemTrayWin( e->xunmap.window, true ) ) 
00301                 {
00302         // If the system tray gets destroyed, the system tray
00303         // icons automatically get unmapped, reparented and mapped
00304         // again to the closest non-client ancestor due to
00305         // QXEmbed's SaveSet feature. Unfortunatly with kicker
00306         // this closest ancestor is not the root window, but our
00307         // decoration, so we reparent explicitely back to the root
00308         // window.
00309                 XEvent ev;
00310                 WId w = e->xunmap.window;
00311                 if ( XCheckTypedWindowEvent (qt_xdisplay(), w,
00312                                              ReparentNotify, &ev) )
00313                     {
00314                     if ( ev.xreparent.parent != root ) 
00315                         {
00316                         XReparentWindow( qt_xdisplay(), w, root, 0, 0 );
00317                         addSystemTrayWin( w );
00318                         }
00319                     }
00320                 return TRUE;
00321                 }
00322 
00323             return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt
00324             }
00325         case MapNotify:
00326 
00327             return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt
00328 
00329         case ReparentNotify:
00330             {
00331         //do not confuse Qt with these events. After all, _we_ are the
00332         //window manager who does the reparenting.
00333             return TRUE;
00334             }
00335         case DestroyNotify:
00336             {
00337             if ( removeSystemTrayWin( e->xdestroywindow.window, false ) )
00338                 return TRUE;
00339             return false;
00340             }
00341         case MapRequest:
00342             {
00343             updateXTime();
00344 
00345             // e->xmaprequest.window is different from e->xany.window
00346             // TODO this shouldn't be necessary now
00347             Client* c = findClient( WindowMatchPredicate( e->xmaprequest.window ));
00348             if ( !c ) 
00349                 {
00350 // don't check for the parent being the root window, this breaks when some app unmaps
00351 // a window, changes something and immediately maps it back, without giving KWin
00352 // a chance to reparent it back to root
00353 // since KWin can get MapRequest only for root window children and
00354 // children of WindowWrapper (=clients), the check is AFAIK useless anyway
00355 // Note: Now the save-set support in Client::mapRequestEvent() actually requires that
00356 // this code doesn't check the parent to be root.
00357 //            if ( e->xmaprequest.parent == root ) { //###TODO store previously destroyed client ids
00358                 if ( addSystemTrayWin( e->xmaprequest.window ) )
00359                     return TRUE;
00360                 c = createClient( e->xmaprequest.window, false );
00361                 if ( c != NULL && root != qt_xrootwin() ) 
00362                     { // TODO what is this?
00363                     // TODO may use QWidget::create
00364                     XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 );
00365                     }
00366                 if( c == NULL ) // refused to manage, simply map it (most probably override redirect)
00367                     XMapRaised( qt_xdisplay(), e->xmaprequest.window );
00368                 return true;
00369                 }
00370             if( c )
00371                 {
00372                 c->windowEvent( e );
00373                 updateFocusChains( c, FocusChainUpdate );
00374                 return true;
00375                 }
00376             break;
00377             }
00378         case EnterNotify:
00379             {
00380             if ( QWhatsThis::inWhatsThisMode() )
00381                 {
00382                 QWidget* w = QWidget::find( e->xcrossing.window );
00383                 if ( w )
00384                     QWhatsThis::leaveWhatsThisMode();
00385                 }
00386             if( electricBorder(e))
00387                 return true;
00388             break;
00389             }
00390         case LeaveNotify:
00391             {
00392             if ( !QWhatsThis::inWhatsThisMode() )
00393                 break;
00394             // TODO is this cliente ever found, given that client events are searched above?
00395             Client* c = findClient( FrameIdMatchPredicate( e->xcrossing.window ));
00396             if ( c && e->xcrossing.detail != NotifyInferior )
00397                 QWhatsThis::leaveWhatsThisMode();
00398             break;
00399             }
00400         case ConfigureRequest:
00401             {
00402             if ( e->xconfigurerequest.parent == root ) 
00403                 {
00404                 XWindowChanges wc;
00405                 wc.border_width = e->xconfigurerequest.border_width;
00406                 wc.x = e->xconfigurerequest.x;
00407                 wc.y = e->xconfigurerequest.y;
00408                 wc.width = e->xconfigurerequest.width;
00409                 wc.height = e->xconfigurerequest.height;
00410                 wc.sibling = None;
00411                 wc.stack_mode = Above;
00412                 unsigned int value_mask = e->xconfigurerequest.value_mask
00413                     & ( CWX | CWY | CWWidth | CWHeight | CWBorderWidth );
00414                 XConfigureWindow( qt_xdisplay(), e->xconfigurerequest.window, value_mask, &wc );
00415                 return true;
00416                 }
00417             break;
00418             }
00419         case KeyPress:
00420             if ( mouse_emulation )
00421                 return keyPressMouseEmulation( e->xkey );
00422             break;
00423         case KeyRelease:
00424             if ( mouse_emulation )
00425                 return FALSE;
00426             break;
00427         case FocusIn:
00428             if( e->xfocus.window == rootWin()
00429                 && ( e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot ))
00430                 {
00431                 updateXTime(); // focusToNull() uses qt_x_time, which is old now (FocusIn has no timestamp)
00432                 Window focus;
00433                 int revert;
00434                 XGetInputFocus( qt_xdisplay(), &focus, &revert );
00435                 if( focus == None || focus == PointerRoot )
00436                     {
00437                     //kdWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" << endl;
00438                     Client *c = mostRecentlyActivatedClient();
00439                     if( c != NULL )
00440                         requestFocus( c, true );
00441                     else if( activateNextClient( NULL ))
00442                         ; // ok, activated
00443                     else
00444                         focusToNull();
00445                     }
00446                 }
00447             // fall through
00448         case FocusOut:
00449             return true; // always eat these, they would tell Qt that KWin is the active app
00450         case ClientMessage:
00451             if( electricBorder( e ))
00452                 return true;
00453             break;
00454         case MappingNotify:
00455             XRefreshKeyboardMapping( &e->xmapping );
00456             tab_box->updateKeyMapping();
00457             break;
00458         default:
00459             break;
00460         }
00461     return FALSE;
00462     }
00463 
00464 // Some events don't have the actual window which caused the event
00465 // as e->xany.window (e.g. ConfigureRequest), but as some other
00466 // field in the XEvent structure.
00467 Window Workspace::findSpecialEventWindow( XEvent* e )
00468     {
00469     switch( e->type )
00470         {
00471         case CreateNotify:
00472             return e->xcreatewindow.window;
00473         case DestroyNotify:
00474             return e->xdestroywindow.window;
00475         case UnmapNotify:
00476             return e->xunmap.window;
00477         case MapNotify:
00478             return e->xmap.window;
00479         case MapRequest:
00480             return e->xmaprequest.window;
00481         case ReparentNotify:
00482             return e->xreparent.window;
00483         case ConfigureNotify:
00484             return e->xconfigure.window;
00485         case GravityNotify:
00486             return e->xgravity.window;
00487         case ConfigureRequest:
00488             return e->xconfigurerequest.window;
00489         case CirculateNotify:
00490             return e->xcirculate.window;
00491         case CirculateRequest:
00492             return e->xcirculaterequest.window;
00493         default:
00494             return None;
00495         };
00496     }
00497 
00501 bool Workspace::netCheck( XEvent* e )
00502     {
00503     unsigned int dirty = rootInfo->event( e );
00504 
00505     if ( dirty & NET::DesktopNames )
00506         saveDesktopSettings();
00507 
00508     return dirty != 0;
00509     }
00510 
00511 
00512 // ****************************************
00513 // Client
00514 // ****************************************
00515 
00519 bool Client::windowEvent( XEvent* e )
00520     {
00521     if( e->xany.window == window()) // avoid doing stuff on frame or wrapper
00522         {
00523         unsigned long dirty[ 2 ];
00524         info->event( e, dirty, 2 ); // pass through the NET stuff
00525 
00526         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 )
00527             fetchName();
00528         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName ) != 0 )
00529             fetchIconicName();
00530         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut ) != 0
00531             || ( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut ) != 0 )
00532             {
00533             if( isTopMenu())  // the fallback mode of KMenuBar may alter the strut
00534                 checkWorkspacePosition();  // restore it
00535             workspace()->updateClientArea();
00536             }
00537         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
00538             getIcons();
00539         // Note there's a difference between userTime() and info->userTime()
00540         // info->userTime() is the value of the property, userTime() also includes
00541         // updates of the time done by KWin (ButtonPress on windowrapper etc.).
00542         if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime ) != 0 )
00543             {
00544             workspace()->setWasUserInteraction();
00545             updateUserTime( info->userTime());
00546             }
00547         if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
00548             startupIdChanged();
00549         if( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry )
00550             {
00551             if( demandAttentionKNotifyTimer != NULL )
00552                 demandAttentionKNotify();
00553             }
00554         }
00555 
00556 // TODO move all focus handling stuff to separate file?
00557     switch (e->type) 
00558         {
00559         case UnmapNotify:
00560             unmapNotifyEvent( &e->xunmap );
00561             break;
00562         case DestroyNotify:
00563             destroyNotifyEvent( &e->xdestroywindow );
00564             break;
00565         case MapRequest:
00566             // this one may pass the event to workspace
00567             return mapRequestEvent( &e->xmaprequest );
00568         case ConfigureRequest:
00569             configureRequestEvent( &e->xconfigurerequest );
00570             break;
00571         case PropertyNotify:
00572             propertyNotifyEvent( &e->xproperty );
00573             break;
00574         case KeyPress:
00575             updateUserTime();
00576             workspace()->setWasUserInteraction();
00577             break;
00578         case ButtonPress:
00579             updateUserTime();
00580             workspace()->setWasUserInteraction();
00581             buttonPressEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
00582                 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
00583             break;
00584         case KeyRelease:
00585     // don't update user time on releases
00586     // e.g. if the user presses Alt+F2, the Alt release
00587     // would appear as user input to the currently active window
00588             break;
00589         case ButtonRelease:
00590     // don't update user time on releases
00591     // e.g. if the user presses Alt+F2, the Alt release
00592     // would appear as user input to the currently active window
00593             buttonReleaseEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
00594                 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
00595             break;
00596         case MotionNotify:
00597             motionNotifyEvent( e->xmotion.window, e->xmotion.state,
00598                 e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root );
00599             break;
00600         case EnterNotify:
00601             enterNotifyEvent( &e->xcrossing );
00602             // MotionNotify is guaranteed to be generated only if the mouse
00603             // move start and ends in the window; for cases when it only
00604             // starts or only ends there, Enter/LeaveNotify are generated.
00605             // Fake a MotionEvent in such cases to make handle of mouse
00606             // events simpler (Qt does that too).
00607             motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
00608                 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
00609             break;
00610         case LeaveNotify:
00611             motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
00612                 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
00613             leaveNotifyEvent( &e->xcrossing );
00614             break;
00615         case FocusIn:
00616             focusInEvent( &e->xfocus );
00617             break;
00618         case FocusOut:
00619             focusOutEvent( &e->xfocus );
00620             break;
00621         case ReparentNotify:
00622             break;
00623         case ClientMessage:
00624             clientMessageEvent( &e->xclient );
00625             break;
00626         case ColormapChangeMask:
00627             if( e->xany.window == window())
00628             {
00629             cmap = e->xcolormap.colormap;
00630             if ( isActive() )
00631                 workspace()->updateColormap();
00632             }
00633             break;
00634         default:
00635             if( e->xany.window == window())
00636             {
00637             if( e->type == Shape::shapeEvent() )
00638                 {
00639                 is_shape = Shape::hasShape( window()); // workaround for #19644
00640                 updateShape();
00641                 }
00642             }
00643             break;
00644         }
00645     return true; // eat all events
00646     }
00647 
00651 bool Client::mapRequestEvent( XMapRequestEvent* e )
00652     {
00653     if( e->window != window())
00654         {
00655         // Special support for the save-set feature, which is a bit broken.
00656         // If there's a window from one client embedded in another one,
00657         // e.g. using XEMBED, and the embedder suddenly looses its X connection,
00658         // save-set will reparent the embedded window to its closest ancestor
00659         // that will remains. Unfortunately, with reparenting window managers,
00660         // this is not the root window, but the frame (or in KWin's case,
00661         // it's the wrapper for the client window). In this case,
00662         // the wrapper will get ReparentNotify for a window it won't know,
00663         // which will be ignored, and then it gets MapRequest, as save-set
00664         // always maps. Returning true here means that Workspace::workspaceEvent()
00665         // will handle this MapRequest and manage this window (i.e. act as if
00666         // it was reparented to root window).
00667         if( e->parent == wrapperId())
00668             return false;
00669         return true; // no messing with frame etc.
00670         }
00671     if( isTopMenu() && workspace()->managingTopMenus())
00672         return true; // kwin controls these
00673     switch ( mappingState() )
00674         {
00675         case WithdrawnState:
00676             assert( false ); // WMs are not supposed to manage clients in Withdrawn state,
00677 //        manage();      // after initial mapping manage() is called from createClient()
00678             break;
00679         case IconicState:
00680     // also copied in clientMessage()
00681             if( isMinimized())
00682                 unminimize();
00683             if( isShade())
00684                 setShade( ShadeNone );
00685             if( !isOnCurrentDesktop())
00686                 {
00687                 if( workspace()->allowClientActivation( this ))
00688                     workspace()->activateClient( this );
00689                 else
00690                     demandAttention();
00691                 }
00692             break;
00693         case NormalState:
00694         // TODO fake MapNotify?
00695             break;
00696         }
00697     return true;
00698     }
00699 
00703 void Client::unmapNotifyEvent( XUnmapEvent* e )
00704     {
00705     if( e->window != window())
00706         return;
00707     if( e->event != wrapperId())
00708         { // most probably event from root window when initially reparenting
00709         bool ignore = true;
00710         if( e->event == workspace()->rootWin() && e->send_event )
00711             ignore = false; // XWithdrawWindow()
00712         if( ignore )
00713             return;
00714         }
00715     switch( mappingState())
00716         {
00717         case IconicState:
00718             releaseWindow();
00719           return;
00720         case NormalState:
00721             // maybe we will be destroyed soon. Check this first.
00722             XEvent ev;
00723             if( XCheckTypedWindowEvent (qt_xdisplay(), window(),
00724                 DestroyNotify, &ev) ) // TODO I don't like this much
00725                 {
00726                 destroyClient(); // deletes this
00727                 return;
00728                 }
00729             releaseWindow();
00730           break;
00731     default:
00732         assert( false );
00733         }
00734     }
00735 
00736 void Client::destroyNotifyEvent( XDestroyWindowEvent* e )
00737     {
00738     if( e->window != window())
00739         return;
00740     destroyClient();
00741     }
00742     
00743     
00744 bool         blockAnimation = FALSE;
00745 
00749 void Client::clientMessageEvent( XClientMessageEvent* e )
00750     {
00751     if( e->window != window())
00752         return; // ignore frame/wrapper
00753     // WM_STATE
00754     if ( e->message_type == atoms->kde_wm_change_state )
00755         {
00756         if( isTopMenu() && workspace()->managingTopMenus())
00757             return; // kwin controls these
00758         if( e->data.l[ 1 ] )
00759             blockAnimation = true;
00760         if( e->data.l[ 0 ] == IconicState )
00761             minimize();
00762         else if( e->data.l[ 0 ] == NormalState )
00763             { // copied from mapRequest()
00764             if( isMinimized())
00765                 unminimize();
00766             if( isShade())
00767                 setShade( ShadeNone );
00768             if( !isOnCurrentDesktop())
00769                 {
00770                 if( workspace()->allowClientActivation( this ))
00771                     workspace()->activateClient( this );
00772                 else
00773                     demandAttention();
00774                 }
00775             }
00776         blockAnimation = false;
00777         }
00778     else if ( e->message_type == atoms->wm_change_state)
00779         {
00780         if( isTopMenu() && workspace()->managingTopMenus())
00781             return; // kwin controls these
00782         if ( e->data.l[0] == IconicState )
00783             minimize();
00784         return;
00785         }
00786     }
00787 
00788 
00792 void Client::configureRequestEvent( XConfigureRequestEvent* e )
00793     {
00794     if( e->window != window())
00795         return; // ignore frame/wrapper
00796     if ( isResize() || isMove())
00797         return; // we have better things to do right now
00798 
00799     if( fullscreen_mode == FullScreenNormal ) // refuse resizing of fullscreen windows
00800         { // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode
00801         sendSyntheticConfigureNotify();
00802         return;
00803         }
00804     if( isSplash() // no manipulations with splashscreens either
00805         || isTopMenu()) // topmenus neither
00806         {
00807         sendSyntheticConfigureNotify();
00808         return;
00809         }
00810 
00811     if ( e->value_mask & CWBorderWidth ) 
00812         {
00813         // first, get rid of a window border
00814         XWindowChanges wc;
00815         unsigned int value_mask = 0;
00816 
00817         wc.border_width = 0;
00818         value_mask = CWBorderWidth;
00819         XConfigureWindow( qt_xdisplay(), window(), value_mask, & wc );
00820         }
00821 
00822     if( e->value_mask & ( CWX | CWY | CWHeight | CWWidth ))
00823         configureRequest( e->value_mask, e->x, e->y, e->width, e->height, 0, false );
00824 
00825     if ( e->value_mask & CWStackMode )
00826         restackWindow( e->above, e->detail, NET::FromApplication, userTime(), false );
00827 
00828     // TODO sending a synthetic configure notify always is fine, even in cases where
00829     // the ICCCM doesn't require this - it can be though of as 'the WM decided to move
00830     // the window later'. The client should not cause that many configure request,
00831     // so this should not have any significant impact. With user moving/resizing
00832     // the it should be optimized though (see also Client::setGeometry()/plainResize()/move()).
00833     sendSyntheticConfigureNotify();
00834 
00835     // SELI TODO accept configure requests for isDesktop windows (because kdesktop
00836     // may get XRANDR resize event before kwin), but check it's still at the bottom?
00837     }
00838 
00839 
00843 void Client::propertyNotifyEvent( XPropertyEvent* e )
00844     {
00845     if( e->window != window())
00846         return; // ignore frame/wrapper
00847     switch ( e->atom ) 
00848         {
00849         case XA_WM_NORMAL_HINTS:
00850             getWmNormalHints();
00851             break;
00852         case XA_WM_NAME:
00853             fetchName();
00854             break;
00855         case XA_WM_ICON_NAME:
00856             fetchIconicName();
00857             break;
00858         case XA_WM_TRANSIENT_FOR:
00859             readTransient();
00860             break;
00861         case XA_WM_HINTS:
00862             getWMHints();
00863             getIcons(); // because KWin::icon() uses WMHints as fallback
00864             break;
00865         default:
00866             if ( e->atom == atoms->wm_protocols )
00867                 getWindowProtocols();
00868             else if (e->atom == atoms->wm_client_leader )
00869                 getWmClientLeader();
00870             else if( e->atom == qt_window_role )
00871                 window_role = staticWindowRole( window());
00872             else if( e->atom == atoms->motif_wm_hints )
00873                 getMotifHints();
00874             break;
00875         }
00876     }
00877 
00878 
00879 void Client::enterNotifyEvent( XCrossingEvent* e )
00880     {
00881     if( e->window != frameId())
00882         return; // care only about entering the whole frame
00883     if( e->mode == NotifyNormal ||
00884          ( !options->focusPolicyIsReasonable() &&
00885              e->mode == NotifyUngrab ) ) 
00886         {
00887 
00888         if (options->shadeHover && isShade()) 
00889             {
00890             delete shadeHoverTimer;
00891             shadeHoverTimer = new QTimer( this );
00892             connect( shadeHoverTimer, SIGNAL( timeout() ), this, SLOT( shadeHover() ));
00893             shadeHoverTimer->start( options->shadeHoverInterval, TRUE );
00894             }
00895 
00896         if ( options->focusPolicy == Options::ClickToFocus )
00897             return;
00898 
00899         if ( options->autoRaise && !isDesktop() &&
00900              !isDock() && !isTopMenu() && workspace()->focusChangeEnabled() &&
00901              workspace()->topClientOnDesktop( workspace()->currentDesktop()) != this ) 
00902             {
00903             delete autoRaiseTimer;
00904             autoRaiseTimer = new QTimer( this );
00905             connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) );
00906             autoRaiseTimer->start( options->autoRaiseInterval, TRUE  );
00907             }
00908 
00909         if ( options->focusPolicy !=  Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) )
00910             return;
00911         if ( options->delayFocus )
00912             workspace()->requestDelayFocus( this );
00913         else
00914             workspace()->requestFocus( this );
00915 
00916         return;
00917         }
00918     }
00919 
00920 void Client::leaveNotifyEvent( XCrossingEvent* e )
00921     {
00922     if( e->window != frameId())
00923         return; // care only about leaving the whole frame
00924     if ( e->mode == NotifyNormal ) 
00925         {
00926         if ( !buttonDown ) 
00927             {
00928             mode = PositionCenter;
00929             setCursor( arrowCursor );
00930             }
00931         bool lostMouse = !rect().contains( QPoint( e->x, e->y ) );
00932         // 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations
00933         // (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event
00934         // comes after leaving the rect) - so lets check if the pointer is really outside the window
00935 
00936         // TODO this still sucks if a window appears above this one - it should lose the mouse
00937         // if this window is another client, but not if it's a popup ... maybe after KDE3.1 :(
00938         // (repeat after me 'AARGHL!')
00939         if ( !lostMouse && e->detail != NotifyInferior ) 
00940             {
00941             int d1, d2, d3, d4;
00942             unsigned int d5;
00943             Window w, child;
00944             if( XQueryPointer( qt_xdisplay(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5 ) == False
00945                 || child == None )
00946                 lostMouse = true; // really lost the mouse
00947             }
00948         if ( lostMouse ) 
00949             {
00950             cancelAutoRaise();
00951             workspace()->cancelDelayFocus();
00952             cancelShadeHover();
00953             if ( shade_mode == ShadeHover && !moveResizeMode && !buttonDown )
00954                setShade( ShadeNormal );
00955             }
00956         if ( options->focusPolicy == Options::FocusStrictlyUnderMouse )
00957             if ( isActive() && lostMouse )
00958                 workspace()->requestFocus( 0 ) ;
00959         return;
00960         }
00961     }
00962 
00963 #define XCapL KKeyNative::modXLock()
00964 #define XNumL KKeyNative::modXNumLock()
00965 #define XScrL KKeyNative::modXScrollLock()
00966 void Client::grabButton( int modifier )
00967     {
00968     unsigned int mods[ 8 ] = 
00969         {
00970         0, XCapL, XNumL, XNumL | XCapL,
00971         XScrL, XScrL | XCapL,
00972         XScrL | XNumL, XScrL | XNumL | XCapL
00973         };
00974     for( int i = 0;
00975          i < 8;
00976          ++i )
00977         XGrabButton( qt_xdisplay(), AnyButton,
00978             modifier | mods[ i ],
00979             wrapperId(),  FALSE, ButtonPressMask,
00980             GrabModeSync, GrabModeAsync, None, None );
00981     }
00982 
00983 void Client::ungrabButton( int modifier )
00984     {
00985     unsigned int mods[ 8 ] = 
00986         {
00987         0, XCapL, XNumL, XNumL | XCapL,
00988         XScrL, XScrL | XCapL,
00989         XScrL | XNumL, XScrL | XNumL | XCapL
00990         };
00991     for( int i = 0;
00992          i < 8;
00993          ++i )
00994         XUngrabButton( qt_xdisplay(), AnyButton,
00995             modifier | mods[ i ], wrapperId());
00996     }
00997 #undef XCapL
00998 #undef XNumL
00999 #undef XScrL
01000 
01001 /*
01002   Releases the passive grab for some modifier combinations when a
01003   window becomes active. This helps broken X programs that
01004   missinterpret LeaveNotify events in grab mode to work properly
01005   (Motif, AWT, Tk, ...)
01006  */
01007 void Client::updateMouseGrab()
01008     {
01009     if( isActive() && !workspace()->forcedGlobalMouseGrab()) // see Workspace::establishTabBoxGrab()
01010         {
01011         // remove the grab for no modifiers only if the window
01012         // is unobscured or if the user doesn't want click raise
01013         // (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is
01014         // the most recently raised window)
01015         bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true ) == this;
01016         if( !options->clickRaise || not_obscured )
01017             ungrabButton( None );
01018         else
01019             grabButton( None );
01020         ungrabButton( ShiftMask );
01021         ungrabButton( ControlMask );
01022         ungrabButton( ControlMask | ShiftMask );
01023         }
01024     else
01025         {
01026         XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, wrapperId());
01027         // simply grab all modifier combinations
01028         XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, wrapperId(), FALSE,
01029             ButtonPressMask,
01030             GrabModeSync, GrabModeAsync,
01031             None, None );
01032         }
01033     }
01034 
01035 int qtToX11Button( Qt::ButtonState button )
01036     {
01037     if( button == Qt::LeftButton )
01038         return Button1;
01039     else if( button == Qt::MidButton )
01040         return Button2;
01041     else if( button == Qt::RightButton )
01042         return Button3;
01043     return AnyButton;
01044     }
01045     
01046 int qtToX11State( Qt::ButtonState state )
01047     {
01048     int ret = 0;
01049     if( state & Qt::LeftButton )
01050         ret |= Button1Mask;
01051     if( state & Qt::MidButton )
01052         ret |= Button2Mask;
01053     if( state & Qt::RightButton )
01054         ret |= Button3Mask;
01055     if( state & Qt::ShiftButton )
01056         ret |= ShiftMask;
01057     if( state & Qt::ControlButton )
01058         ret |= ControlMask;
01059     if( state & Qt::AltButton )
01060         ret |= KKeyNative::modX(KKey::ALT);
01061     if( state & Qt::MetaButton )
01062         ret |= KKeyNative::modX(KKey::WIN);
01063     return ret;
01064     }
01065 
01066 // Qt propagates mouse events up the widget hierachy, which means events
01067 // for the decoration window cannot be (easily) intercepted as X11 events
01068 bool Client::eventFilter( QObject* o, QEvent* e )
01069     {
01070     if( decoration == NULL
01071         || o != decoration->widget())
01072         return false;
01073     if( e->type() == QEvent::MouseButtonPress )
01074         {
01075         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01076         return buttonPressEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
01077             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01078         }
01079     if( e->type() == QEvent::MouseButtonRelease )
01080         {
01081         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01082         return buttonReleaseEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
01083             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01084         }
01085     if( e->type() == QEvent::MouseMove ) // FRAME i fake z enter/leave?
01086         {
01087         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01088         return motionNotifyEvent( decorationId(), qtToX11State( ev->state()),
01089             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01090         }
01091     if( e->type() == QEvent::Wheel )
01092         {
01093         QWheelEvent* ev = static_cast< QWheelEvent* >( e );
01094         bool r = buttonPressEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()),
01095             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01096         r = r || buttonReleaseEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()),
01097             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01098         return r;
01099         }
01100     if( e->type() == QEvent::Resize )
01101         {
01102         QResizeEvent* ev = static_cast< QResizeEvent* >( e );
01103         // Filter out resize events that inform about size different than frame size.
01104         // This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync.
01105         // These events only seem to be delayed events from initial resizing before show() was called
01106         // on the decoration widget.
01107         if( ev->size() != size())
01108             return true;
01109         }
01110     return false;
01111     }
01112 
01113 // return value matters only when filtering events before decoration gets them
01114 bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root )
01115     {
01116     if (buttonDown)
01117         {
01118         if( w == wrapperId())
01119             XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01120         return true;
01121         }
01122 
01123     if( w == wrapperId() || w == frameId() || w == decorationId())
01124         { // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
01125         updateUserTime();
01126         workspace()->setWasUserInteraction();
01127         uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ?
01128             KKeyNative::modX(KKey::WIN) :
01129             KKeyNative::modX(KKey::ALT);
01130         bool bModKeyHeld = keyModX != 0 && ( state & KKeyNative::accelModMaskX()) == keyModX;
01131 
01132         if( isSplash()
01133             && button == Button1 && !bModKeyHeld )
01134             { // hide splashwindow if the user clicks on it
01135             hideClient( true );
01136             if( w == wrapperId())
01137                     XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01138             return true;
01139             }
01140 
01141         Options::MouseCommand com = Options::MouseNothing;
01142         bool was_action = false;
01143         bool perform_handled = false;
01144         if ( bModKeyHeld )
01145             {
01146             was_action = true;
01147             switch (button) 
01148                 {
01149                 case Button1:
01150                     com = options->commandAll1();
01151                     break;
01152                 case Button2:
01153                     com = options->commandAll2();
01154                     break;
01155                 case Button3:
01156                     com = options->commandAll3();
01157                     break;
01158                 case Button4:
01159                 case Button5:
01160                     com = options->operationWindowMouseWheel( button == Button4 ? 120 : -120 );
01161                     break;
01162                 }
01163             }
01164         else
01165             { // inactive inner window
01166             if( !isActive() && w == wrapperId())
01167                 {
01168                 was_action = true;
01169                 perform_handled = true;
01170                 switch (button) 
01171                     {
01172                     case Button1:
01173                         com = options->commandWindow1();
01174                         break;
01175                     case Button2:
01176                         com = options->commandWindow2();
01177                         break;
01178                     case Button3:
01179                         com = options->commandWindow3();
01180                         break;
01181                     default:
01182                         com = Options::MouseActivateAndPassClick;
01183                     }
01184                 }
01185             // active inner window
01186             if( isActive() && w == wrapperId()
01187                 && options->clickRaise && button < 4 ) // exclude wheel
01188                 {
01189                 com = Options::MouseActivateRaiseAndPassClick;
01190                 was_action = true;
01191                 perform_handled = true;
01192                 }
01193             }
01194         if( was_action )
01195             {
01196             bool replay = performMouseCommand( com, QPoint( x_root, y_root), perform_handled );
01197 
01198             if ( isSpecialWindow())
01199                 replay = TRUE;
01200 
01201             if( w == wrapperId()) // these can come only from a grab
01202                 XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //qt_x_time);
01203             return true;
01204             }
01205         }
01206 
01207     if( w == wrapperId()) // these can come only from a grab
01208         {
01209         XAllowEvents(qt_xdisplay(), ReplayPointer, CurrentTime ); //qt_x_time);
01210         return true;
01211         }
01212     if( w == decorationId())
01213         return false; // don't eat decoration events
01214     if( w == frameId())
01215         processDecorationButtonPress( button, state, x, y, x_root, y_root );
01216     return true;
01217     }
01218 
01219 
01220 // this function processes button press events only after decoration decides not to handle them,
01221 // unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them
01222 void Client::processDecorationButtonPress( int button, int /*state*/, int x, int y, int x_root, int y_root )
01223     {
01224     Options::MouseCommand com = Options::MouseNothing;
01225     bool active = isActive();
01226     if ( !