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