kwin Library API Documentation

events.cpp

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