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