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                 if ( !c->wantsTabFocus())
00374                     focus_chain.remove( c );  // TODO move focus_chain changes to functions
00375                 return true;
00376                 }
00377             break;
00378             }
00379         case EnterNotify:
00380             {
00381             if ( QWhatsThis::inWhatsThisMode() )
00382                 {
00383                 QWidget* w = QWidget::find( e->xcrossing.window );
00384                 if ( w )
00385                     QWhatsThis::leaveWhatsThisMode();
00386                 }
00387             if( electricBorder(e))
00388                 return true;
00389             break;
00390             }
00391         case LeaveNotify:
00392             {
00393             if ( !QWhatsThis::inWhatsThisMode() )
00394                 break;
00395             // TODO is this cliente ever found, given that client events are searched above?
00396             Client* c = findClient( FrameIdMatchPredicate( e->xcrossing.window ));
00397             if ( c && e->xcrossing.detail != NotifyInferior )
00398                 QWhatsThis::leaveWhatsThisMode();
00399             break;
00400             }
00401         case ConfigureRequest:
00402             {
00403             if ( e->xconfigurerequest.parent == root ) 
00404                 {
00405                 XWindowChanges wc;
00406                 unsigned int value_mask = 0;
00407                 wc.border_width = 0;
00408                 wc.x = e->xconfigurerequest.x;
00409                 wc.y = e->xconfigurerequest.y;
00410                 wc.width = e->xconfigurerequest.width;
00411                 wc.height = e->xconfigurerequest.height;
00412                 wc.sibling = None;
00413                 wc.stack_mode = Above;
00414                 value_mask = e->xconfigurerequest.value_mask | CWBorderWidth;
00415                 XConfigureWindow( qt_xdisplay(), e->xconfigurerequest.window, value_mask, &wc );
00416                 return true;
00417                 }
00418             break;
00419             }
00420         case KeyPress:
00421             if ( mouse_emulation )
00422                 return keyPressMouseEmulation( e->xkey );
00423             break;
00424         case KeyRelease:
00425             if ( mouse_emulation )
00426                 return FALSE;
00427             break;
00428         case FocusIn:
00429             if( e->xfocus.window == rootWin()
00430                 && ( e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot ))
00431                 {
00432                 updateXTime(); // focusToNull() uses qt_x_time, which is old now (FocusIn has no timestamp)
00433                 Window focus;
00434                 int revert;
00435                 XGetInputFocus( qt_xdisplay(), &focus, &revert );
00436                 if( focus == None || focus == PointerRoot )
00437                     {
00438                     //kdWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" << endl;
00439                     Client *c = mostRecentlyActivatedClient();
00440                     if( c != NULL )
00441                         requestFocus( c, true );
00442                     else if( activateNextClient( NULL ))
00443                         ; // ok, activated
00444                     else
00445                         focusToNull();
00446                     }
00447                 }
00448             // fall through
00449         case FocusOut:
00450             return true; // always eat these, they would tell Qt that KWin is the active app
00451         case ClientMessage:
00452             if( electricBorder( e ))
00453                 return true;
00454             break;
00455         case MappingNotify:
00456             XRefreshKeyboardMapping( &e->xmapping );
00457             tab_box->updateKeyMapping();
00458             break;
00459         default:
00460             break;
00461         }
00462     return FALSE;
00463     }
00464 
00465 // Some events don't have the actual window which caused the event
00466 // as e->xany.window (e.g. ConfigureRequest), but as some other
00467 // field in the XEvent structure.
00468 Window Workspace::findSpecialEventWindow( XEvent* e )
00469     {
00470     switch( e->type )
00471         {
00472         case CreateNotify:
00473             return e->xcreatewindow.window;
00474         case DestroyNotify:
00475             return e->xdestroywindow.window;
00476         case UnmapNotify:
00477             return e->xunmap.window;
00478         case MapNotify:
00479             return e->xmap.window;
00480         case MapRequest:
00481             return e->xmaprequest.window;
00482         case ReparentNotify:
00483             return e->xreparent.window;
00484         case ConfigureNotify:
00485             return e->xconfigure.window;
00486         case GravityNotify:
00487             return e->xgravity.window;
00488         case ConfigureRequest:
00489             return e->xconfigurerequest.window;
00490         case CirculateNotify:
00491             return e->xcirculate.window;
00492         case CirculateRequest:
00493             return e->xcirculaterequest.window;
00494         default:
00495             return None;
00496         };
00497     }
00498 
00502 bool Workspace::netCheck( XEvent* e )
00503     {
00504     unsigned int dirty = rootInfo->event( e );
00505 
00506     if ( dirty & NET::DesktopNames )
00507         saveDesktopSettings();
00508 
00509     return dirty != 0;
00510     }
00511 
00512 
00513 // ****************************************
00514 // Client
00515 // ****************************************
00516 
00520 bool Client::windowEvent( XEvent* e )
00521     {
00522     if( e->xany.window == window()) // avoid doing stuff on frame or wrapper
00523         {
00524         unsigned long dirty[ 2 ];
00525         info->event( e, dirty, 2 ); // pass through the NET stuff
00526 
00527         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 )
00528             fetchName();
00529         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName ) != 0 )
00530             fetchIconicName();
00531         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut ) != 0
00532             || ( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut ) != 0 )
00533             {
00534             if( isTopMenu())  // the fallback mode of KMenuBar may alter the strut
00535                 checkWorkspacePosition();  // restore it
00536             workspace()->updateClientArea();
00537             }
00538         if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
00539             getIcons();
00540         // Note there's a difference between userTime() and info->userTime()
00541         // info->userTime() is the value of the property, userTime() also includes
00542         // updates of the time done by KWin (ButtonPress on windowrapper etc.).
00543         if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime ) != 0 )
00544             {
00545             workspace()->setWasUserInteraction();
00546             updateUserTime( info->userTime());
00547             }
00548         if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
00549             startupIdChanged();
00550         if( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry )
00551             {
00552             if( demandAttentionKNotifyTimer != NULL )
00553                 demandAttentionKNotify();
00554             }
00555         }
00556 
00557 // TODO move all focus handling stuff to separate file?
00558     switch (e->type) 
00559         {
00560         case UnmapNotify:
00561             unmapNotifyEvent( &e->xunmap );
00562             break;
00563         case DestroyNotify:
00564             destroyNotifyEvent( &e->xdestroywindow );
00565             break;
00566         case MapRequest:
00567             // this one may pass the event to workspace
00568             return mapRequestEvent( &e->xmaprequest );
00569         case ConfigureRequest:
00570             configureRequestEvent( &e->xconfigurerequest );
00571             break;
00572         case PropertyNotify:
00573             propertyNotifyEvent( &e->xproperty );
00574             break;
00575         case KeyPress:
00576             updateUserTime();
00577             workspace()->setWasUserInteraction();
00578             break;
00579         case ButtonPress:
00580             updateUserTime();
00581             workspace()->setWasUserInteraction();
00582             buttonPressEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
00583                 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
00584             break;
00585         case KeyRelease:
00586     // don't update user time on releases
00587     // e.g. if the user presses Alt+F2, the Alt release
00588     // would appear as user input to the currently active window
00589             break;
00590         case ButtonRelease:
00591     // don't update user time on releases
00592     // e.g. if the user presses Alt+F2, the Alt release
00593     // would appear as user input to the currently active window
00594             buttonReleaseEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
00595                 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
00596             break;
00597         case MotionNotify:
00598             motionNotifyEvent( e->xmotion.window, e->xmotion.state,
00599                 e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root );
00600             break;
00601         case EnterNotify:
00602             enterNotifyEvent( &e->xcrossing );
00603             // MotionNotify is guaranteed to be generated only if the mouse
00604             // move start and ends in the window; for cases when it only
00605             // starts or only ends there, Enter/LeaveNotify are generated.
00606             // Fake a MotionEvent in such cases to make handle of mouse
00607             // events simpler (Qt does that too).
00608             motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
00609                 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
00610             break;
00611         case LeaveNotify:
00612             motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
00613                 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
00614             leaveNotifyEvent( &e->xcrossing );
00615             break;
00616         case FocusIn:
00617             focusInEvent( &e->xfocus );
00618             break;
00619         case FocusOut:
00620             focusOutEvent( &e->xfocus );
00621             break;
00622         case ReparentNotify:
00623             break;
00624         case ClientMessage:
00625             clientMessageEvent( &e->xclient );
00626             break;
00627         case ColormapChangeMask:
00628             if( e->xany.window == window())
00629             {
00630             cmap = e->xcolormap.colormap;
00631             if ( isActive() )
00632                 workspace()->updateColormap();
00633             }
00634             break;
00635         case VisibilityNotify:
00636             visibilityNotifyEvent( &e->xvisibility );
00637             break;
00638         default:
00639             if( e->xany.window == window())
00640             {
00641             if( e->type == Shape::shapeEvent() )
00642                 {
00643                 is_shape = Shape::hasShape( window()); // workaround for #19644
00644                 updateShape();
00645                 }
00646             }
00647             break;
00648         }
00649     return true; // eat all events
00650     }
00651 
00655 bool Client::mapRequestEvent( XMapRequestEvent* e )
00656     {
00657     if( e->window != window())
00658         {
00659         // Special support for the save-set feature, which is a bit broken.
00660         // If there's a window from one client embedded in another one,
00661         // e.g. using XEMBED, and the embedder suddenly looses its X connection,
00662         // save-set will reparent the embedded window to its closest ancestor
00663         // that will remains. Unfortunately, with reparenting window managers,
00664         // this is not the root window, but the frame (or in KWin's case,
00665         // it's the wrapper for the client window). In this case,
00666         // the wrapper will get ReparentNotify for a window it won't know,
00667         // which will be ignored, and then it gets MapRequest, as save-set
00668         // always maps. Returning true here means that Workspace::workspaceEvent()
00669         // will handle this MapRequest and manage this window (i.e. act as if
00670         // it was reparented to root window).
00671         if( e->parent == wrapperId())
00672             return false;
00673         return true; // no messing with frame etc.
00674         }
00675     if( isTopMenu() && workspace()->managingTopMenus())
00676         return true; // kwin controls these
00677     switch ( mappingState() )
00678         {
00679         case WithdrawnState:
00680             assert( false ); // WMs are not supposed to manage clients in Withdrawn state,
00681 //        manage();      // after initial mapping manage() is called from createClient()
00682             break;
00683         case IconicState:
00684     // also copied in clientMessage()
00685             if( isMinimized())
00686                 unminimize();
00687             if( isShade())
00688                 setShade( ShadeNone );
00689             if( !isOnCurrentDesktop())
00690                 {
00691                 if( workspace()->allowClientActivation( this ))
00692                     workspace()->activateClient( this );
00693                 else
00694                     demandAttention();
00695                 }
00696             break;
00697         case NormalState:
00698         // TODO fake MapNotify?
00699             break;
00700         }
00701     return true;
00702     }
00703 
00707 void Client::unmapNotifyEvent( XUnmapEvent* e )
00708     {
00709     if( e->window != window())
00710         return;
00711     if( e->event != wrapperId())
00712         { // most probably event from root window when initially reparenting
00713         bool ignore = true;
00714         if( e->event == workspace()->rootWin() && e->send_event )
00715             ignore = false; // XWithdrawWindow()
00716         if( ignore )
00717             return;
00718         }
00719     switch( mappingState())
00720         {
00721         case IconicState:
00722             releaseWindow();
00723           return;
00724         case NormalState:
00725             // maybe we will be destroyed soon. Check this first.
00726             XEvent ev;
00727             if( XCheckTypedWindowEvent (qt_xdisplay(), window(),
00728                 DestroyNotify, &ev) ) // TODO I don't like this much
00729                 {
00730                 destroyClient(); // deletes this
00731                 return;
00732                 }
00733             releaseWindow();
00734           break;
00735     default:
00736         assert( false );
00737         }
00738     }
00739 
00740 void Client::destroyNotifyEvent( XDestroyWindowEvent* e )
00741     {
00742     if( e->window != window())
00743         return;
00744     destroyClient();
00745     }
00746     
00747     
00748 bool         blockAnimation = FALSE;
00749 
00753 void Client::clientMessageEvent( XClientMessageEvent* e )
00754     {
00755     if( e->window != window())
00756         return; // ignore frame/wrapper
00757     // WM_STATE
00758     if ( e->message_type == atoms->kde_wm_change_state )
00759         {
00760         if( isTopMenu() && workspace()->managingTopMenus())
00761             return; // kwin controls these
00762         if( e->data.l[ 1 ] )
00763             blockAnimation = true;
00764         if( e->data.l[ 0 ] == IconicState )
00765             minimize();
00766         else if( e->data.l[ 0 ] == NormalState )
00767             { // copied from mapRequest()
00768             if( isMinimized())
00769                 unminimize();
00770             if( isShade())
00771                 setShade( ShadeNone );
00772             if( !isOnCurrentDesktop())
00773                 {
00774                 if( workspace()->allowClientActivation( this ))
00775                     workspace()->activateClient( this );
00776                 else
00777                     demandAttention();
00778                 }
00779             }
00780         blockAnimation = false;
00781         }
00782     else if ( e->message_type == atoms->wm_change_state)
00783         {
00784         if( isTopMenu() && workspace()->managingTopMenus())
00785             return; // kwin controls these
00786         if ( e->data.l[0] == IconicState )
00787             minimize();
00788         return;
00789         }
00790     }
00791 
00792 
00796 void Client::configureRequestEvent( XConfigureRequestEvent* e )
00797     {
00798     if( e->window != window())
00799         return; // ignore frame/wrapper
00800     if ( isResize() || isMove())
00801         return; // we have better things to do right now
00802 
00803     if( fullscreen_mode == FullScreenNormal ) // refuse resizing of fullscreen windows
00804         { // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode
00805         sendSyntheticConfigureNotify();
00806         return;
00807         }
00808     if( isSplash() // no manipulations with splashscreens either
00809         || isTopMenu()) // topmenus neither
00810         {
00811         sendSyntheticConfigureNotify();
00812         return;
00813         }
00814 
00815     if ( e->value_mask & CWBorderWidth ) 
00816         {
00817         // first, get rid of a window border
00818         XWindowChanges wc;
00819         unsigned int value_mask = 0;
00820 
00821         wc.border_width = 0;
00822         value_mask = CWBorderWidth;
00823         XConfigureWindow( qt_xdisplay(), window(), value_mask, & wc );
00824         }
00825 
00826     if( e->value_mask & ( CWX | CWY | CWHeight | CWWidth ))
00827         configureRequest( e->value_mask, e->x, e->y, e->width, e->height, 0, false );
00828 
00829     if ( e->value_mask & CWStackMode )
00830         restackWindow( e->above, e->detail, NET::FromApplication, userTime(), false );
00831 
00832     // TODO sending a synthetic configure notify always is fine, even in cases where
00833     // the ICCCM doesn't require this - it can be though of as 'the WM decided to move
00834     // the window later'. The client should not cause that many configure request,
00835     // so this should not have any significant impact. With user moving/resizing
00836     // the it should be optimized though (see also Client::setGeometry()/plainResize()/move()).
00837     sendSyntheticConfigureNotify();
00838 
00839     // SELI TODO accept configure requests for isDesktop windows (because kdesktop
00840     // may get XRANDR resize event before kwin), but check it's still at the bottom?
00841     }
00842 
00843 
00847 void Client::propertyNotifyEvent( XPropertyEvent* e )
00848     {
00849     if( e->window != window())
00850         return; // ignore frame/wrapper
00851     switch ( e->atom ) 
00852         {
00853         case XA_WM_NORMAL_HINTS:
00854             getWmNormalHints();
00855             break;
00856         case XA_WM_NAME:
00857             fetchName();
00858             break;
00859         case XA_WM_ICON_NAME:
00860             fetchIconicName();
00861             break;
00862         case XA_WM_TRANSIENT_FOR:
00863             readTransient();
00864             break;
00865         case XA_WM_HINTS:
00866             getWMHints();
00867             getIcons(); // because KWin::icon() uses WMHints as fallback
00868             break;
00869         default:
00870             if ( e->atom == atoms->wm_protocols )
00871                 getWindowProtocols();
00872             else if (e->atom == atoms->wm_client_leader )
00873                 getWmClientLeader();
00874             else if( e->atom == qt_window_role )
00875                 window_role = staticWindowRole( window());
00876             else if( e->atom == atoms->motif_wm_hints )
00877                 getMotifHints();
00878             break;
00879         }
00880     }
00881 
00882 
00883 void Client::enterNotifyEvent( XCrossingEvent* e )
00884     {
00885     if( e->window != frameId())
00886         return; // care only about entering the whole frame
00887     if( e->mode == NotifyNormal ||
00888          ( !options->focusPolicyIsReasonable() &&
00889              e->mode == NotifyUngrab ) ) 
00890         {
00891 
00892         if (options->shadeHover && isShade()) 
00893             {
00894             delete shadeHoverTimer;
00895             shadeHoverTimer = new QTimer( this );
00896             connect( shadeHoverTimer, SIGNAL( timeout() ), this, SLOT( shadeHover() ));
00897             shadeHoverTimer->start( options->shadeHoverInterval, TRUE );
00898             }
00899 
00900         if ( options->focusPolicy == Options::ClickToFocus )
00901             return;
00902 
00903         if ( options->autoRaise && !isDesktop() &&
00904              !isDock() && !isTopMenu() && workspace()->focusChangeEnabled() &&
00905              workspace()->topClientOnDesktop( workspace()->currentDesktop()) != this ) 
00906             {
00907             delete autoRaiseTimer;
00908             autoRaiseTimer = new QTimer( this );
00909             connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) );
00910             autoRaiseTimer->start( options->autoRaiseInterval, TRUE  );
00911             }
00912 
00913         if ( options->focusPolicy !=  Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) )
00914             return;
00915         if ( options->delayFocus )
00916             workspace()->requestDelayFocus( this );
00917         else
00918             workspace()->requestFocus( this );
00919 
00920         return;
00921         }
00922     }
00923 
00924 void Client::leaveNotifyEvent( XCrossingEvent* e )
00925     {
00926     if( e->window != frameId())
00927         return; // care only about leaving the whole frame
00928     if ( e->mode == NotifyNormal ) 
00929         {
00930         if ( !buttonDown ) 
00931             {
00932             mode = PositionCenter;
00933             setCursor( arrowCursor );
00934             }
00935         bool lostMouse = !rect().contains( QPoint( e->x, e->y ) );
00936         // 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations
00937         // (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event
00938         // comes after leaving the rect) - so lets check if the pointer is really outside the window
00939 
00940         // TODO this still sucks if a window appears above this one - it should lose the mouse
00941         // if this window is another client, but not if it's a popup ... maybe after KDE3.1 :(
00942         // (repeat after me 'AARGHL!')
00943         if ( !lostMouse && e->detail != NotifyInferior ) 
00944             {
00945             int d1, d2, d3, d4;
00946             unsigned int d5;
00947             Window w, child;
00948             if( XQueryPointer( qt_xdisplay(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5 ) == False
00949                 || child == None )
00950                 lostMouse = true; // really lost the mouse
00951             }
00952         if ( lostMouse ) 
00953             {
00954             cancelAutoRaise();
00955             workspace()->cancelDelayFocus();
00956             cancelShadeHover();
00957             if ( shade_mode == ShadeHover && !moveResizeMode && !buttonDown )
00958                setShade( ShadeNormal );
00959             }
00960         if ( options->focusPolicy == Options::FocusStrictlyUnderMouse )
00961             if ( isActive() && lostMouse )
00962                 workspace()->requestFocus( 0 ) ;
00963         return;
00964         }
00965     }
00966 
00967 #define XCapL KKeyNative::modXLock()
00968 #define XNumL KKeyNative::modXNumLock()
00969 #define XScrL KKeyNative::modXScrollLock()
00970 void Client::grabButton( int modifier )
00971     {
00972     unsigned int mods[ 8 ] = 
00973         {
00974         0, XCapL, XNumL, XNumL | XCapL,
00975         XScrL, XScrL | XCapL,
00976         XScrL | XNumL, XScrL | XNumL | XCapL
00977         };
00978     for( int i = 0;
00979          i < 8;
00980          ++i )
00981         XGrabButton( qt_xdisplay(), AnyButton,
00982             modifier | mods[ i ],
00983             wrapperId(),  FALSE, ButtonPressMask,
00984             GrabModeSync, GrabModeAsync, None, None );
00985     }
00986 
00987 void Client::ungrabButton( int modifier )
00988     {
00989     unsigned int mods[ 8 ] = 
00990         {
00991         0, XCapL, XNumL, XNumL | XCapL,
00992         XScrL, XScrL | XCapL,
00993         XScrL | XNumL, XScrL | XNumL | XCapL
00994         };
00995     for( int i = 0;
00996          i < 8;
00997          ++i )
00998         XUngrabButton( qt_xdisplay(), AnyButton,
00999             modifier | mods[ i ], wrapperId());
01000     }
01001 #undef XCapL
01002 #undef XNumL
01003 #undef XScrL
01004 
01005 /*
01006   Releases the passive grab for some modifier combinations when a
01007   window becomes active. This helps broken X programs that
01008   missinterpret LeaveNotify events in grab mode to work properly
01009   (Motif, AWT, Tk, ...)
01010  */
01011 void Client::updateMouseGrab()
01012     {
01013     if( workspace()->globalShortcutsDisabled())
01014         {
01015         XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, wrapperId());
01016         // keep grab for the simple click without modifiers if needed
01017         if( !( !options->clickRaise || not_obscured ))
01018             grabButton( None );
01019         return;
01020         }
01021     if( isActive() && !workspace()->forcedGlobalMouseGrab()) // see Workspace::establishTabBoxGrab()
01022         {
01023         // remove the grab for no modifiers only if the window
01024         // is unobscured or if the user doesn't want click raise
01025         if( !options->clickRaise || not_obscured )
01026             ungrabButton( None );
01027         else
01028             grabButton( None );
01029         ungrabButton( ShiftMask );
01030         ungrabButton( ControlMask );
01031         ungrabButton( ControlMask | ShiftMask );
01032         }
01033     else
01034         {
01035         XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, wrapperId());
01036         // simply grab all modifier combinations
01037         XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, wrapperId(), FALSE,
01038             ButtonPressMask,
01039             GrabModeSync, GrabModeAsync,
01040             None, None );
01041         }
01042     }
01043 
01044 int qtToX11Button( Qt::ButtonState button )
01045     {
01046     if( button == Qt::LeftButton )
01047         return Button1;
01048     else if( button == Qt::MidButton )
01049         return Button2;
01050     else if( button == Qt::RightButton )
01051         return Button3;
01052     return AnyButton;
01053     }
01054     
01055 int qtToX11State( Qt::ButtonState state )
01056     {
01057     int ret = 0;
01058     if( state & Qt::LeftButton )
01059         ret |= Button1Mask;
01060     if( state & Qt::MidButton )
01061         ret |= Button2Mask;
01062     if( state & Qt::RightButton )
01063         ret |= Button3Mask;
01064     if( state & Qt::ShiftButton )
01065         ret |= ShiftMask;
01066     if( state & Qt::ControlButton )
01067         ret |= ControlMask;
01068     if( state & Qt::AltButton )
01069         ret |= KKeyNative::modX(KKey::ALT);
01070     if( state & Qt::MetaButton )
01071         ret |= KKeyNative::modX(KKey::WIN);
01072     return ret;
01073     }
01074 
01075 // Qt propagates mouse events up the widget hierachy, which means events
01076 // for the decoration window cannot be (easily) intercepted as X11 events
01077 bool Client::eventFilter( QObject* o, QEvent* e )
01078     {
01079     if( decoration == NULL
01080         || o != decoration->widget())
01081         return false;
01082     if( e->type() == QEvent::MouseButtonPress )
01083         {
01084         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01085         return buttonPressEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
01086             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01087         }
01088     if( e->type() == QEvent::MouseButtonRelease )
01089         {
01090         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01091         return buttonReleaseEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()),
01092             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01093         }
01094     if( e->type() == QEvent::MouseMove ) // FRAME i fake z enter/leave?
01095         {
01096         QMouseEvent* ev = static_cast< QMouseEvent* >( e );
01097         return motionNotifyEvent( decorationId(), qtToX11State( ev->state()),
01098             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01099         }
01100     if( e->type() == QEvent::Wheel )
01101         {
01102         QWheelEvent* ev = static_cast< QWheelEvent* >( e );
01103         bool r = buttonPressEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()),
01104             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01105         r = r || buttonReleaseEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()),
01106             ev->x(), ev->y(), ev->globalX(), ev->globalY() );
01107         return r;
01108         }
01109     if( e->type() == QEvent::Resize )
01110         {
01111         QResizeEvent* ev = static_cast< QResizeEvent* >( e );
01112         // Filter out resize events that inform about size different than frame size.
01113         // This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync.
01114         // These events only seem to be delayed events from initial resizing before show() was called
01115         // on the decoration widget.
01116         if( ev->size() != size())
01117             return true;
01118         }
01119     return false;
01120     }
01121 
01122 // return value matters only when filtering events before decoration gets them
01123 bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root )
01124     {
01125     if (buttonDown)
01126         {
01127         if( w == wrapperId())
01128             XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01129         return true;
01130         }
01131 
01132     if( w == wrapperId() || w == frameId() || w == decorationId())
01133         { // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
01134         updateUserTime();
01135         workspace()->setWasUserInteraction();
01136         uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ?
01137             KKeyNative::modX(KKey::WIN) :
01138             KKeyNative::modX(KKey::ALT);
01139         bool bModKeyHeld = keyModX != 0 && ( state & KKeyNative::accelModMaskX()) == keyModX;
01140 
01141         if( isSplash()
01142             && button == Button1 && !bModKeyHeld )
01143             { // hide splashwindow if the user clicks on it
01144             hideClient( true );
01145             if( w == wrapperId())
01146                     XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01147             return true;
01148             }
01149 
01150         Options::MouseCommand com = Options::MouseNothing;
01151         bool was_action = false;
01152         bool perform_handled = false;
01153         if ( bModKeyHeld )
01154             {
01155             was_action = true;
01156             switch (button) 
01157                 {
01158                 case Button1:
01159                     com = options->commandAll1();
01160                     break;
01161                 case Button2:
01162                     com = options->commandAll2();
01163                     break;
01164                 case Button3:
01165                     com = options->commandAll3();
01166                     break;
01167                 case Button4:
01168                 case Button5:
01169                     com = options->operationWindowMouseWheel( button == Button4 ? 120 : -120 );
01170                     break;
01171                 }
01172             }
01173         else
01174             { // inactive inner window
01175             if( !isActive() && w == wrapperId())
01176                 {
01177                 was_action = true;
01178                 perform_handled = true;
01179                 switch (button) 
01180                     {
01181                     case Button1:
01182                         com = options->commandWindow1();
01183                         break;
01184                     case Button2:
01185                         com = options->commandWindow2();
01186                         break;
01187                     case Button3:
01188                         com = options->commandWindow3();
01189                         break;
01190                     default:
01191                         com = Options::MouseActivateAndPassClick;
01192                     }
01193                 }
01194             // active inner window
01195             if( isActive() && w == wrapperId()
01196                 && options->clickRaise && button < 4 ) // exclude wheel
01197                 {
01198                 com = Options::MouseActivateRaiseAndPassClick;
01199                 was_action = true;
01200                 perform_handled = true;
01201                 }
01202             }
01203         if( was_action )
01204             {
01205             bool replay = performMouseCommand( com, QPoint( x_root, y_root), perform_handled );
01206 
01207             if ( isSpecialWindow())
01208                 replay = TRUE;
01209 
01210             if( w == wrapperId()) // these can come only from a grab
01211                 XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //qt_x_time);
01212             return true;
01213             }
01214         }
01215 
01216     if( w == wrapperId()) // these can come only from a grab
01217         {
01218         XAllowEvents(qt_xdisplay(), ReplayPointer, CurrentTime ); //qt_x_time);
01219         return true;
01220         }
01221     if( w == decorationId())
01222         return false; // don't eat decoration events
01223     if( w == frameId())
01224         processDecorationButtonPress( button, state, x, y, x_root, y_root );
01225     return true;
01226     }
01227 
01228 
01229 // this function processes button press events only after decoration decides not to handle them,
01230 // unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them
01231 void Client::processDecorationButtonPress( int button, int /*state*/, int x, int y, int x_root, int y_root )
01232     {
01233     Options::MouseCommand com = Options::MouseNothing;
01234     bool active = isActive();
01235     if ( !wantsInput() ) // we cannot be active, use it anyway
01236         active = TRUE;
01237 
01238     if ( button == Button1 )
01239         com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1();
01240     else if ( button == Button2 )
01241         com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2();
01242     else if ( button == Button3 )
01243         com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
01244     if( button == Button1
01245         && com != Options::MouseOperationsMenu // actions where it's not possible to get the matching
01246         && com != Options::MouseMinimize )  // mouse release event
01247         {
01248         mode = mousePosition( QPoint( x, y ));
01249         buttonDown = TRUE;
01250         moveOffset = QPoint( x, y );
01251         invertedMoveOffset = rect().bottomRight() - moveOffset;
01252         unrestrictedMoveResize = false;
01253         setCursor( mode ); // update to sizeAllCursor if about to move
01254         }
01255     performMouseCommand( com, QPoint( x_root, y_root ));
01256     }
01257 
01258 // called from decoration
01259 void Client::processMousePressEvent( QMouseEvent* e )
01260     {
01261     if( e->type() != QEvent::MouseButtonPress )
01262         {
01263         kdWarning() << "processMousePressEvent()" << endl;
01264         return;
01265         }
01266     int button;
01267     switch( e->button())
01268         {
01269         case LeftButton:
01270             button = Button1;
01271           break;
01272         case MidButton:
01273             button = Button2;
01274           break;
01275         case RightButton:
01276             button = Button3;
01277           break;
01278         default:
01279             return;
01280         }
01281     processDecorationButtonPress( button, e->state(), e->x(), e->y(), e->globalX(), e->globalY());
01282     }
01283 
01284 // return value matters only when filtering events before decoration gets them
01285 bool Client::buttonReleaseEvent( Window w, int /*button*/, int state, int x, int y, int x_root, int y_root )
01286     {
01287     if( w == decorationId() && !buttonDown)
01288         return false;
01289     if( w == wrapperId())
01290         {
01291         XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time);
01292         return true;
01293         }
01294     if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
01295         return true;
01296     x = this->x(); // translate from grab window to local coords
01297     y = this->y();
01298     if ( (state & ( Button1Mask & Button2Mask & Button3Mask )) == 0 )
01299         {
01300         buttonDown = FALSE;
01301         if ( moveResizeMode ) 
01302             {
01303             finishMoveResize( false );
01304             // mouse position is still relative to old Client position, adjust it
01305             QPoint mousepos( x_root - x, y_root - y );
01306             mode = mousePosition( mousepos );
01307             }
01308         setCursor( mode );
01309         }
01310     return true;
01311     }
01312 
01313 static bool was_motion = false;
01314 static Time next_motion_time = CurrentTime;
01315 // Check whole incoming X queue for MotionNotify events
01316 // checking whole queue is done by always returning False in the predicate.
01317 // If there are more MotionNotify events in the queue, all until the last
01318 // one may be safely discarded (if a ButtonRelease event comes, a MotionNotify
01319 // will be faked from it, so there's no need to check other events).
01320 // This helps avoiding being overloaded by being flooded from many events
01321 // from the XServer.
01322 static Bool motion_predicate( Display*, XEvent* ev, XPointer )
01323 {
01324     if( ev->type == MotionNotify )
01325         {
01326     was_motion = true;
01327         next_motion_time = ev->xmotion.time;  // for setting time
01328         }
01329     return False;
01330 }
01331 
01332 static bool waitingMotionEvent()
01333     {
01334 // The queue doesn't need to be checked until the X timestamp
01335 // of processes events reaches the timestamp of the last suitable
01336 // MotionNotify event in the queue.
01337     if( next_motion_time != CurrentTime
01338         && timestampCompare( qt_x_time, next_motion_time ) < 0 )
01339         return true;
01340     was_motion = false;
01341     XSync( qt_xdisplay(), False ); // this helps to discard more MotionNotify events
01342     XEvent dummy;
01343     XCheckIfEvent( qt_xdisplay(), &dummy, motion_predicate, NULL );
01344     return was_motion;
01345     }
01346 
01347 // return value matters only when filtering events before decoration gets them
01348 bool Client::motionNotifyEvent( Window w, int /*state*/, int x, int y, int x_root, int y_root )
01349     {
01350     if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
01351         return true; // care only about the whole frame
01352     if ( !buttonDown ) 
01353         {
01354         Position newmode = mousePosition( QPoint( x, y ));
01355         if( newmode != mode )
01356             setCursor( newmode );
01357         mode = newmode;
01358         // reset the timestamp for the optimization, otherwise with long passivity
01359         // the option in waitingMotionEvent() may be always true
01360         next_motion_time = CurrentTime;
01361         return false;
01362         }
01363     if( w == moveResizeGrabWindow())
01364         {
01365         x = this->x(); // translate from grab window to local coords
01366         y = this->y();
01367         }
01368     if( !waitingMotionEvent())
01369         handleMoveResize( x, y, x_root, y_root );
01370     return true;
01371     }
01372     
01373 void Client::focusInEvent( XFocusInEvent* e )
01374     {
01375     if( e->window != window())
01376         return; // only window gets focus
01377     if ( e->mode == NotifyUngrab )
01378         return; // we don't care
01379     if ( e->detail == NotifyPointer )
01380         return;  // we don't care
01381     if( !isShown( false ) || !isOnCurrentDesktop()) // we unmapped it, but it got focus meanwhile ->
01382         return;            // activateNextClient() already transferred focus elsewhere
01383     // check if this client is in should_get_focus list or if activation is allowed
01384     bool activate =  workspace()->allowClientActivation( this, -1U, true );
01385     workspace()->gotFocusIn( this ); // remove from should_get_focus list
01386     if( activate )
01387         setActive( TRUE );
01388     else
01389         {
01390         workspace()->restoreFocus();
01391         demandAttention();
01392         }
01393     }
01394 
01395 // When a client loses focus, FocusOut events are usually immediatelly
01396 // followed by FocusIn events for another client that gains the focus
01397 // (unless the focus goes to another screen, or to the nofocus widget).
01398 // Without this check, the former focused client would have to be
01399 // deactivated, and after that, the new one would be activated, with
01400 // a short time when there would be no active client. This can cause
01401 // flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred
01402 // from it to its transient, the fullscreen would be kept in the Active layer
01403 // at the beginning and at the end, but not in the middle, when the active
01404 // client would be temporarily none (see Client::belongToLayer() ).
01405 // Therefore, the events queue is checked, whether it contains the matching
01406 // FocusIn event, and if yes, deactivation of the previous client will
01407 // be skipped, as activation of the new one will automatically deactivate
01408 // previously active client.
01409 static bool follows_focusin = false;
01410 static bool follows_focusin_failed = false;
01411 static Bool predicate_follows_focusin( Display*, XEvent* e, XPointer arg )
01412     {
01413     if( follows_focusin || follows_focusin_failed )
01414         return False;
01415     Client* c = ( Client* ) arg;
01416     if( e->type == FocusIn && c->workspace()->findClient( WindowMatchPredicate( e->xfocus.window )))
01417         { // found FocusIn
01418         follows_focusin = true;
01419         return False;
01420         }
01421     // events that may be in the queue before the FocusIn event that's being
01422     // searched for
01423     if( e->type == FocusIn || e->type == FocusOut || e->type == KeymapNotify )
01424         return False;
01425     follows_focusin_failed = true; // a different event - stop search
01426     return False;
01427     }
01428 
01429 static bool check_follows_focusin( Client* c )
01430     {
01431     follows_focusin = follows_focusin_failed = false;
01432     XEvent dummy;
01433     // XCheckIfEvent() is used to make the search non-blocking, the predicate
01434     // always returns False, so nothing is removed from the events queue.
01435     // XPeekIfEvent() would block.
01436     XCheckIfEvent( qt_xdisplay(), &dummy, predicate_follows_focusin, (XPointer)c );
01437     return follows_focusin;
01438     }
01439 
01440 
01441 void Client::focusOutEvent( XFocusOutEvent* e )
01442     {
01443     if( e->window != window())
01444         return; // only window gets focus
01445     if ( e->mode == NotifyGrab )
01446         return; // we don't care
01447     if ( isShade() )
01448         return; // here neither
01449     if ( e->detail != NotifyNonlinear
01450         && e->detail != NotifyNonlinearVirtual )
01451         // SELI check all this
01452         return; // hack for motif apps like netscape
01453     if ( QApplication::activePopupWidget() )
01454         return;
01455     if( !check_follows_focusin( this ))
01456         setActive( FALSE );
01457     }
01458 
01459 void Client::visibilityNotifyEvent( XVisibilityEvent * e)
01460     {
01461     if( e->window != frameId())
01462         return; // care only about the whole frame
01463     bool new_not_obscured = e->state == VisibilityUnobscured;
01464     if( not_obscured == new_not_obscured )
01465         return;
01466     not_obscured = new_not_obscured;
01467     updateMouseGrab();
01468     }
01469 
01470 // performs _NET_WM_MOVERESIZE
01471 void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction )
01472     {
01473     if( direction == NET::Move )
01474         performMouseCommand( Options::MouseMove, QPoint( x_root, y_root ));
01475     else if( direction >= NET::TopLeft && direction <= NET::Left ) 
01476         {
01477         static const Position convert[] =
01478             {
01479             PositionTopLeft,
01480             PositionTop,
01481             PositionTopRight,
01482             PositionRight,
01483             PositionBottomRight,
01484             PositionBottom,
01485             PositionBottomLeft,
01486             PositionLeft
01487             };
01488         if(!isResizable() || isShade())
01489             return;
01490         if( moveResizeMode )
01491             finishMoveResize( false );
01492         buttonDown = TRUE;
01493         moveOffset = QPoint( x_root - x(), y_root - y()); // map from global
01494         invertedMoveOffset = rect().bottomRight() - moveOffset;
01495         unrestrictedMoveResize = false;
01496         mode = convert[ direction ];
01497         setCursor( mode );
01498         if( !startMoveResize())
01499             {
01500             buttonDown = false;
01501             setCursor( mode );
01502             }
01503         }
01504     else if( direction == NET::KeyboardMove )
01505         { // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm
01506         QCursor::setPos( geometry().center() );
01507         performMouseCommand( Options::MouseUnrestrictedMove, geometry().center());
01508         }
01509     else if( direction == NET::KeyboardSize )
01510         { // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm
01511         QCursor::setPos( geometry().bottomRight());
01512         performMouseCommand( Options::MouseUnrestrictedResize, geometry().bottomRight());
01513         }
01514     }
01515 
01516 void Client::keyPressEvent( uint key_code )
01517     {
01518     updateUserTime();
01519     if ( !isMove() && !isResize() )
01520         return;
01521     bool is_control = key_code & Qt::CTRL;
01522     bool is_alt = key_code & Qt::ALT;
01523     key_code = key_code & 0xffff;
01524     int delta = is_control?1:is_alt?32:8;
01525     QPoint pos = QCursor::pos();
01526     switch ( key_code ) 
01527         {
01528         case Key_Left:
01529             pos.rx() -= delta;
01530             break;
01531         case Key_Right:
01532             pos.rx() += delta;
01533             break;
01534         case Key_Up:
01535             pos.ry() -= delta;
01536             break;
01537         case Key_Down:
01538             pos.ry() += delta;
01539             break;
01540         case Key_Space:
01541         case Key_Return:
01542         case Key_Enter:
01543             finishMoveResize( false );
01544             buttonDown = FALSE;
01545             setCursor( mode );
01546             break;
01547         case Key_Escape:
01548             finishMoveResize( true );
01549             buttonDown = FALSE;
01550             setCursor( mode );
01551             break;
01552         default:
01553             return;
01554         }
01555     QCursor::setPos( pos );
01556     }
01557 
01558 // ****************************************
01559 // Group
01560 // ****************************************
01561 
01562 bool Group::groupEvent( XEvent* e )
01563     {
01564     unsigned long dirty[ 2 ];
01565     leader_info->event( e, dirty, 2 ); // pass through the NET stuff
01566     if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
01567         getIcons();
01568     if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
01569         startupIdChanged();
01570     return false;
01571     }
01572 
01573 
01574 } // namespace
KDE Home | KDE Accessibility Home | Description of Access Keys