Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kwin / events.cpp
blob39d9d9973912b49d14fa1c8ce6069b96b3bd01b3
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
24 This file contains things relevant to handling incoming events.
28 #include <config-X11.h>
30 #include "client.h"
31 #include "workspace.h"
32 #include "atoms.h"
33 #include "tabbox.h"
34 #include "group.h"
35 #include "rules.h"
36 #include "unmanaged.h"
37 #include "scene.h"
38 #include "effects.h"
40 #include <QWhatsThis>
41 #include <QApplication>
43 #include <kkeyserver.h>
45 #include <X11/extensions/shape.h>
46 #include <X11/Xatom.h>
47 #include <QX11Info>
49 #ifdef HAVE_XRANDR
50 #include <X11/extensions/Xrandr.h>
51 #endif
53 namespace KWin
56 // ****************************************
57 // WinInfo
58 // ****************************************
60 WinInfo::WinInfo( Client * c, Display * display, Window window,
61 Window rwin, const unsigned long pr[], int pr_size )
62 : NETWinInfo( display, window, rwin, pr, pr_size, NET::WindowManager ), m_client( c )
66 void WinInfo::changeDesktop(int desktop)
68 m_client->workspace()->sendClientToDesktop( m_client, desktop, true );
71 void WinInfo::changeState( unsigned long state, unsigned long mask )
73 mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore
74 mask &= ~NET::Hidden; // clients are not allowed to change this directly
75 state &= mask; // for safety, clear all other bits
77 if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) == 0 )
78 m_client->setFullScreen( false, false );
79 if ( (mask & NET::Max) == NET::Max )
80 m_client->setMaximize( state & NET::MaxVert, state & NET::MaxHoriz );
81 else if ( mask & NET::MaxVert )
82 m_client->setMaximize( state & NET::MaxVert, m_client->maximizeMode() & Client::MaximizeHorizontal );
83 else if ( mask & NET::MaxHoriz )
84 m_client->setMaximize( m_client->maximizeMode() & Client::MaximizeVertical, state & NET::MaxHoriz );
86 if ( mask & NET::Shaded )
87 m_client->setShade( state & NET::Shaded ? ShadeNormal : ShadeNone );
88 if ( mask & NET::KeepAbove)
89 m_client->setKeepAbove( (state & NET::KeepAbove) != 0 );
90 if ( mask & NET::KeepBelow)
91 m_client->setKeepBelow( (state & NET::KeepBelow) != 0 );
92 if( mask & NET::SkipTaskbar )
93 m_client->setSkipTaskbar( ( state & NET::SkipTaskbar ) != 0, true );
94 if( mask & NET::SkipPager )
95 m_client->setSkipPager( ( state & NET::SkipPager ) != 0 );
96 if( mask & NET::DemandsAttention )
97 m_client->demandAttention(( state & NET::DemandsAttention ) != 0 );
98 if( mask & NET::Modal )
99 m_client->setModal( ( state & NET::Modal ) != 0 );
100 // unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() )
101 if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) != 0 )
102 m_client->setFullScreen( true, false );
105 void WinInfo::disable()
107 m_client = NULL; // only used when the object is passed to Deleted
110 // ****************************************
111 // RootInfo
112 // ****************************************
114 RootInfo::RootInfo( Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr[], int pr_num, int scr )
115 : NETRootInfo( dpy, w, name, pr, pr_num, scr )
117 workspace = ws;
120 void RootInfo::changeNumberOfDesktops(int n)
122 workspace->setNumberOfDesktops( n );
125 void RootInfo::changeCurrentDesktop(int d)
127 workspace->setCurrentDesktop( d );
130 void RootInfo::changeActiveWindow( Window w, NET::RequestSource src, Time timestamp, Window active_window )
132 if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
134 if( timestamp == CurrentTime )
135 timestamp = c->userTime();
136 if( src != NET::FromApplication && src != FromTool )
137 src = NET::FromTool;
138 if( src == NET::FromTool )
139 workspace->activateClient( c, true ); // force
140 else // NET::FromApplication
142 Client* c2;
143 if( workspace->allowClientActivation( c, timestamp, false, true ))
144 workspace->activateClient( c );
145 // if activation of the requestor's window would be allowed, allow activation too
146 else if( active_window != None
147 && ( c2 = workspace->findClient( WindowMatchPredicate( active_window ))) != NULL
148 && workspace->allowClientActivation( c2,
149 timestampCompare( timestamp, c2->userTime() > 0 ? timestamp : c2->userTime()), false, true ))
151 workspace->activateClient( c );
153 else
154 c->demandAttention();
159 void RootInfo::restackWindow( Window w, RequestSource src, Window above, int detail, Time timestamp )
161 if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
163 if( timestamp == CurrentTime )
164 timestamp = c->userTime();
165 if( src != NET::FromApplication && src != FromTool )
166 src = NET::FromTool;
167 c->restackWindow( above, detail, src, timestamp, true );
171 void RootInfo::gotTakeActivity( Window w, Time timestamp, long flags )
173 if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
174 workspace->handleTakeActivity( c, timestamp, flags );
177 void RootInfo::closeWindow(Window w)
179 Client* c = workspace->findClient( WindowMatchPredicate( w ));
180 if ( c )
181 c->closeWindow();
184 void RootInfo::moveResize(Window w, int x_root, int y_root, unsigned long direction)
186 Client* c = workspace->findClient( WindowMatchPredicate( w ));
187 if ( c )
189 updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp
190 c->NETMoveResize( x_root, y_root, (Direction)direction);
194 void RootInfo::moveResizeWindow(Window w, int flags, int x, int y, int width, int height )
196 Client* c = workspace->findClient( WindowMatchPredicate( w ));
197 if ( c )
198 c->NETMoveResizeWindow( flags, x, y, width, height );
201 void RootInfo::gotPing( Window w, Time timestamp )
203 if( Client* c = workspace->findClient( WindowMatchPredicate( w )))
204 c->gotPing( timestamp );
207 void RootInfo::changeShowingDesktop( bool showing )
209 workspace->setShowingDesktop( showing );
212 // ****************************************
213 // Workspace
214 // ****************************************
217 Handles workspace specific XEvents
219 bool Workspace::workspaceEvent( XEvent * e )
221 if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) )
223 mouse_emulation = false;
224 ungrabXKeyboard();
226 if( effects && static_cast< EffectsHandlerImpl* >( effects )->hasKeyboardGrab()
227 && ( e->type == KeyPress || e->type == KeyRelease ))
228 return false; // let Qt process it, it'll be intercepted again in eventFilter()
230 if( e->type == PropertyNotify || e->type == ClientMessage )
232 unsigned long dirty[ NETRootInfo::PROPERTIES_SIZE ];
233 rootInfo->event( e, dirty, NETRootInfo::PROPERTIES_SIZE );
234 if( dirty[ NETRootInfo::PROTOCOLS ] & NET::DesktopNames )
235 saveDesktopSettings();
236 if( dirty[ NETRootInfo::PROTOCOLS2 ] & NET::WM2DesktopLayout )
237 updateDesktopLayout();
240 // events that should be handled before Clients can get them
241 switch (e->type)
243 case ButtonPress:
244 case ButtonRelease:
245 was_user_interaction = true;
246 // fallthrough
247 case MotionNotify:
248 if ( tab_grab || control_grab )
250 tab_box->handleMouseEvent( e );
251 return true;
253 if( effects && static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent( e ))
254 return true;
255 break;
256 case KeyPress:
258 was_user_interaction = true;
259 int keyQt;
260 KKeyServer::xEventToQt(e, &keyQt);
261 // kDebug(125) << "Workspace::keyPress( " << keyQt << " )";
262 if (movingClient)
264 movingClient->keyPressEvent(keyQt);
265 return true;
267 if( tab_grab || control_grab )
269 tabBoxKeyPress( keyQt );
270 return true;
272 break;
274 case KeyRelease:
275 was_user_interaction = true;
276 if( tab_grab || control_grab )
278 tabBoxKeyRelease( e->xkey );
279 return true;
281 break;
284 if( Client* c = findClient( WindowMatchPredicate( e->xany.window )))
286 if( c->windowEvent( e ))
287 return true;
289 else if( Client* c = findClient( WrapperIdMatchPredicate( e->xany.window )))
291 if( c->windowEvent( e ))
292 return true;
294 else if( Client* c = findClient( FrameIdMatchPredicate( e->xany.window )))
296 if( c->windowEvent( e ))
297 return true;
299 else if( Unmanaged* c = findUnmanaged( WindowMatchPredicate( e->xany.window )))
301 if( c->windowEvent( e ))
302 return true;
304 else
306 Window special = findSpecialEventWindow( e );
307 if( special != None )
308 if( Client* c = findClient( WindowMatchPredicate( special )))
310 if( c->windowEvent( e ))
311 return true;
314 if( movingClient != NULL && movingClient->moveResizeGrabWindow() == e->xany.window
315 && ( e->type == MotionNotify || e->type == ButtonPress || e->type == ButtonRelease ))
317 if( movingClient->windowEvent( e ))
318 return true;
321 switch (e->type)
323 case CreateNotify:
324 if ( e->xcreatewindow.parent == rootWindow() &&
325 !QWidget::find( e->xcreatewindow.window) &&
326 !e->xcreatewindow.override_redirect )
328 // see comments for allowClientActivation()
329 Time t = xTime();
330 XChangeProperty(display(), e->xcreatewindow.window,
331 atoms->kde_net_wm_user_creation_time, XA_CARDINAL,
332 32, PropModeReplace, (unsigned char *)&t, 1);
334 break;
336 case UnmapNotify:
338 return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt
340 case ReparentNotify:
342 //do not confuse Qt with these events. After all, _we_ are the
343 //window manager who does the reparenting.
344 return true;
346 case DestroyNotify:
348 return false;
350 case MapRequest:
352 updateXTime();
354 // e->xmaprequest.window is different from e->xany.window
355 // TODO this shouldn't be necessary now
356 Client* c = findClient( WindowMatchPredicate( e->xmaprequest.window ));
357 if ( !c )
359 // don't check for the parent being the root window, this breaks when some app unmaps
360 // a window, changes something and immediately maps it back, without giving KWin
361 // a chance to reparent it back to root
362 // since KWin can get MapRequest only for root window children and
363 // children of WindowWrapper (=clients), the check is AFAIK useless anyway
364 // Note: Now the save-set support in Client::mapRequestEvent() actually requires that
365 // this code doesn't check the parent to be root.
366 // if ( e->xmaprequest.parent == root ) {
367 c = createClient( e->xmaprequest.window, false );
368 if( c == NULL ) // refused to manage, simply map it (most probably override redirect)
369 XMapRaised( display(), e->xmaprequest.window );
370 return true;
372 if( c )
374 c->windowEvent( e );
375 updateFocusChains( c, FocusChainUpdate );
376 return true;
378 break;
380 case MapNotify:
382 if( e->xmap.override_redirect )
384 Unmanaged* c = findUnmanaged( WindowMatchPredicate( e->xmap.window ));
385 if( c == NULL )
386 c = createUnmanaged( e->xmap.window );
387 if( c )
388 return c->windowEvent( e );
390 return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt
393 case EnterNotify:
395 if ( QWhatsThis::inWhatsThisMode() )
397 QWidget* w = QWidget::find( e->xcrossing.window );
398 if ( w )
399 QWhatsThis::leaveWhatsThisMode();
401 if( electricBorderEvent(e))
402 return true;
403 break;
405 case LeaveNotify:
407 if ( !QWhatsThis::inWhatsThisMode() )
408 break;
409 // TODO is this cliente ever found, given that client events are searched above?
410 Client* c = findClient( FrameIdMatchPredicate( e->xcrossing.window ));
411 if ( c && e->xcrossing.detail != NotifyInferior )
412 QWhatsThis::leaveWhatsThisMode();
413 break;
415 case ConfigureRequest:
417 if ( e->xconfigurerequest.parent == rootWindow())
419 XWindowChanges wc;
420 wc.border_width = e->xconfigurerequest.border_width;
421 wc.x = e->xconfigurerequest.x;
422 wc.y = e->xconfigurerequest.y;
423 wc.width = e->xconfigurerequest.width;
424 wc.height = e->xconfigurerequest.height;
425 wc.sibling = None;
426 wc.stack_mode = Above;
427 unsigned int value_mask = e->xconfigurerequest.value_mask
428 & ( CWX | CWY | CWWidth | CWHeight | CWBorderWidth );
429 XConfigureWindow( display(), e->xconfigurerequest.window, value_mask, &wc );
430 return true;
432 break;
434 case KeyPress:
435 if ( mouse_emulation )
436 return keyPressMouseEmulation( e->xkey );
437 break;
438 case KeyRelease:
439 if ( mouse_emulation )
440 return false;
441 break;
442 case FocusIn:
443 if( e->xfocus.window == rootWindow()
444 && ( e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot ))
446 updateXTime(); // focusToNull() uses xTime(), which is old now (FocusIn has no timestamp)
447 Window focus;
448 int revert;
449 XGetInputFocus( display(), &focus, &revert );
450 if( focus == None || focus == PointerRoot )
452 //kWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" ;
453 Client *c = mostRecentlyActivatedClient();
454 if( c != NULL )
455 requestFocus( c, true );
456 else if( activateNextClient( NULL ))
457 ; // ok, activated
458 else
459 focusToNull();
462 // fall through
463 case FocusOut:
464 return true; // always eat these, they would tell Qt that KWin is the active app
465 case ClientMessage:
466 if( electricBorderEvent( e ))
467 return true;
468 break;
469 case Expose:
470 if( compositing()
471 && ( e->xexpose.window == rootWindow() // root window needs repainting
472 || overlay != None && e->xexpose.window == overlay )) // overlay needs repainting
474 addRepaint( e->xexpose.x, e->xexpose.y, e->xexpose.width, e->xexpose.height );
476 break;
477 case VisibilityNotify:
478 if( compositing() && overlay != None && e->xvisibility.window == overlay )
480 overlay_visible = ( e->xvisibility.state != VisibilityFullyObscured );
481 addRepaintFull();
483 break;
484 default:
485 if( e->type == Extensions::randrNotifyEvent() && Extensions::randrAvailable() )
487 #ifdef HAVE_XRANDR
488 XRRUpdateConfiguration( e );
489 #endif
490 if( compositing() )
492 // desktopResized() should take care of when the size or
493 // shape of the desktop has changed, but we also want to
494 // catch refresh rate changes
495 finishCompositing();
496 QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
499 else if( e->type == Extensions::syncAlarmNotifyEvent() && Extensions::syncAvailable())
501 #ifdef HAVE_XSYNC
502 foreach( Client* c, clients )
503 c->syncEvent( reinterpret_cast< XSyncAlarmNotifyEvent* >( e ));
504 foreach( Client* c, desktops )
505 c->syncEvent( reinterpret_cast< XSyncAlarmNotifyEvent* >( e ));
506 #endif
508 break;
510 return false;
513 // Used only to filter events that need to be processed by Qt first
514 // (e.g. keyboard input to be composed), otherwise events are
515 // handle by the XEvent filter above
516 bool Workspace::workspaceEvent( QEvent* e )
518 if(( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease || e->type() == QEvent::ShortcutOverride )
519 && effects && static_cast< EffectsHandlerImpl* >( effects )->hasKeyboardGrab())
521 static_cast< EffectsHandlerImpl* >( effects )->grabbedKeyboardEvent( static_cast< QKeyEvent* >( e ));
522 return true;
524 return false;
527 // Some events don't have the actual window which caused the event
528 // as e->xany.window (e.g. ConfigureRequest), but as some other
529 // field in the XEvent structure.
530 Window Workspace::findSpecialEventWindow( XEvent* e )
532 switch( e->type )
534 case CreateNotify:
535 return e->xcreatewindow.window;
536 case DestroyNotify:
537 return e->xdestroywindow.window;
538 case UnmapNotify:
539 return e->xunmap.window;
540 case MapNotify:
541 return e->xmap.window;
542 case MapRequest:
543 return e->xmaprequest.window;
544 case ReparentNotify:
545 return e->xreparent.window;
546 case ConfigureNotify:
547 return e->xconfigure.window;
548 case GravityNotify:
549 return e->xgravity.window;
550 case ConfigureRequest:
551 return e->xconfigurerequest.window;
552 case CirculateNotify:
553 return e->xcirculate.window;
554 case CirculateRequest:
555 return e->xcirculaterequest.window;
556 default:
557 return None;
561 // ****************************************
562 // Client
563 // ****************************************
566 General handler for XEvents concerning the client window
568 bool Client::windowEvent( XEvent* e )
570 if( e->xany.window == window()) // avoid doing stuff on frame or wrapper
572 unsigned long dirty[ 2 ];
573 double old_opacity = opacity();
574 info->event( e, dirty, 2 ); // pass through the NET stuff
576 if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 )
577 fetchName();
578 if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName ) != 0 )
579 fetchIconicName();
580 if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut ) != 0
581 || ( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut ) != 0 )
583 if( isTopMenu()) // the fallback mode of KMenuBar may alter the strut
584 checkWorkspacePosition(); // restore it
585 workspace()->updateClientArea();
587 if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
588 getIcons();
589 // Note there's a difference between userTime() and info->userTime()
590 // info->userTime() is the value of the property, userTime() also includes
591 // updates of the time done by KWin (ButtonPress on windowrapper etc.).
592 if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime ) != 0 )
594 workspace()->setWasUserInteraction();
595 updateUserTime( info->userTime());
597 if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
598 startupIdChanged();
599 if( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry )
601 if( demandAttentionKNotifyTimer != NULL )
602 demandAttentionKNotify();
604 if( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2Opacity )
606 if( compositing())
608 addRepaintFull();
609 scene->windowOpacityChanged( this );
610 if( effects )
611 static_cast<EffectsHandlerImpl*>(effects)->windowOpacityChanged( effectWindow(), old_opacity );
613 else
614 { // forward to the frame if there's possibly another compositing manager running
615 NETWinInfo i( display(), frameId(), rootWindow(), 0 );
616 i.setOpacity( info->opacity());
621 switch (e->type)
623 case UnmapNotify:
624 unmapNotifyEvent( &e->xunmap );
625 break;
626 case DestroyNotify:
627 destroyNotifyEvent( &e->xdestroywindow );
628 break;
629 case MapRequest:
630 // this one may pass the event to workspace
631 return mapRequestEvent( &e->xmaprequest );
632 case ConfigureRequest:
633 configureRequestEvent( &e->xconfigurerequest );
634 break;
635 case PropertyNotify:
636 propertyNotifyEvent( &e->xproperty );
637 break;
638 case KeyPress:
639 updateUserTime();
640 workspace()->setWasUserInteraction();
641 break;
642 case ButtonPress:
643 updateUserTime();
644 workspace()->setWasUserInteraction();
645 buttonPressEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
646 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
647 break;
648 case KeyRelease:
649 // don't update user time on releases
650 // e.g. if the user presses Alt+F2, the Alt release
651 // would appear as user input to the currently active window
652 break;
653 case ButtonRelease:
654 // don't update user time on releases
655 // e.g. if the user presses Alt+F2, the Alt release
656 // would appear as user input to the currently active window
657 buttonReleaseEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state,
658 e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root );
659 break;
660 case MotionNotify:
661 motionNotifyEvent( e->xmotion.window, e->xmotion.state,
662 e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root );
663 workspace()->updateFocusMousePosition( QPoint( e->xmotion.x_root, e->xmotion.y_root ));
664 break;
665 case EnterNotify:
666 enterNotifyEvent( &e->xcrossing );
667 // MotionNotify is guaranteed to be generated only if the mouse
668 // move start and ends in the window; for cases when it only
669 // starts or only ends there, Enter/LeaveNotify are generated.
670 // Fake a MotionEvent in such cases to make handle of mouse
671 // events simpler (Qt does that too).
672 motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
673 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
674 workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ));
675 break;
676 case LeaveNotify:
677 motionNotifyEvent( e->xcrossing.window, e->xcrossing.state,
678 e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root );
679 leaveNotifyEvent( &e->xcrossing );
680 // not here, it'd break following enter notify handling
681 // workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ));
682 break;
683 case FocusIn:
684 focusInEvent( &e->xfocus );
685 break;
686 case FocusOut:
687 focusOutEvent( &e->xfocus );
688 break;
689 case ReparentNotify:
690 break;
691 case ClientMessage:
692 clientMessageEvent( &e->xclient );
693 break;
694 case ColormapChangeMask:
695 if( e->xany.window == window())
697 cmap = e->xcolormap.colormap;
698 if ( isActive() )
699 workspace()->updateColormap();
701 break;
702 default:
703 if( e->xany.window == window())
705 if( e->type == Extensions::shapeNotifyEvent() )
707 detectShape( window()); // workaround for #19644
708 updateShape();
711 if( e->xany.window == frameId())
713 #ifdef HAVE_XDAMAGE
714 if( e->type == Extensions::damageNotifyEvent())
715 damageNotifyEvent( reinterpret_cast< XDamageNotifyEvent* >( e ));
716 #endif
718 break;
720 return true; // eat all events
724 Handles map requests of the client window
726 bool Client::mapRequestEvent( XMapRequestEvent* e )
728 if( e->window != window())
730 // Special support for the save-set feature, which is a bit broken.
731 // If there's a window from one client embedded in another one,
732 // e.g. using XEMBED, and the embedder suddenly looses its X connection,
733 // save-set will reparent the embedded window to its closest ancestor
734 // that will remains. Unfortunately, with reparenting window managers,
735 // this is not the root window, but the frame (or in KWin's case,
736 // it's the wrapper for the client window). In this case,
737 // the wrapper will get ReparentNotify for a window it won't know,
738 // which will be ignored, and then it gets MapRequest, as save-set
739 // always maps. Returning true here means that Workspace::workspaceEvent()
740 // will handle this MapRequest and manage this window (i.e. act as if
741 // it was reparented to root window).
742 if( e->parent == wrapperId())
743 return false;
744 return true; // no messing with frame etc.
746 if( isTopMenu() && workspace()->managingTopMenus())
747 return true; // kwin controls these
748 switch ( mappingState() )
750 case WithdrawnState:
751 assert( false ); // WMs are not supposed to manage clients in Withdrawn state,
752 // manage(); // after initial mapping manage() is called from createClient()
753 break;
754 case IconicState:
755 // also copied in clientMessage()
756 if( isMinimized())
757 unminimize();
758 if( isShade())
759 setShade( ShadeNone );
760 if( !isOnCurrentDesktop())
762 if( workspace()->allowClientActivation( this ))
763 workspace()->activateClient( this );
764 else
765 demandAttention();
767 break;
768 case NormalState:
769 // TODO fake MapNotify?
770 break;
772 return true;
776 Handles unmap notify events of the client window
778 void Client::unmapNotifyEvent( XUnmapEvent* e )
780 if( e->window != window())
781 return;
782 if( e->event != wrapperId())
783 { // most probably event from root window when initially reparenting
784 bool ignore = true;
785 if( e->event == rootWindow() && e->send_event )
786 ignore = false; // XWithdrawWindow()
787 if( ignore )
788 return;
790 switch( mappingState())
792 case IconicState:
793 releaseWindow();
794 return;
795 case NormalState:
796 // maybe we will be destroyed soon. Check this first.
797 XEvent ev;
798 if( XCheckTypedWindowEvent (display(), window(),
799 DestroyNotify, &ev) ) // TODO I don't like this much
801 destroyClient(); // deletes this
802 return;
804 releaseWindow();
805 break;
806 default:
807 assert( false );
811 void Client::destroyNotifyEvent( XDestroyWindowEvent* e )
813 if( e->window != window())
814 return;
815 destroyClient();
820 Handles client messages for the client window
822 void Client::clientMessageEvent( XClientMessageEvent* e )
824 if( e->window != window())
825 return; // ignore frame/wrapper
826 // WM_STATE
827 if ( e->message_type == atoms->kde_wm_change_state )
829 if( isTopMenu() && workspace()->managingTopMenus())
830 return; // kwin controls these
831 bool avoid_animation = ( e->data.l[ 1 ] );
832 if( e->data.l[ 0 ] == IconicState )
833 minimize();
834 else if( e->data.l[ 0 ] == NormalState )
835 { // copied from mapRequest()
836 if( isMinimized())
837 unminimize( avoid_animation );
838 if( isShade())
839 setShade( ShadeNone );
840 if( !isOnCurrentDesktop())
842 if( workspace()->allowClientActivation( this ))
843 workspace()->activateClient( this );
844 else
845 demandAttention();
849 else if ( e->message_type == atoms->wm_change_state)
851 if( isTopMenu() && workspace()->managingTopMenus())
852 return; // kwin controls these
853 if ( e->data.l[0] == IconicState )
854 minimize();
855 return;
861 Handles configure requests of the client window
863 void Client::configureRequestEvent( XConfigureRequestEvent* e )
865 if( e->window != window())
866 return; // ignore frame/wrapper
867 if ( isResize() || isMove())
868 return; // we have better things to do right now
870 if( fullscreen_mode == FullScreenNormal ) // refuse resizing of fullscreen windows
871 { // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode
872 sendSyntheticConfigureNotify();
873 return;
875 if( isSplash() // no manipulations with splashscreens either
876 || isTopMenu()) // topmenus neither
878 sendSyntheticConfigureNotify();
879 return;
882 if ( e->value_mask & CWBorderWidth )
884 // first, get rid of a window border
885 XWindowChanges wc;
886 unsigned int value_mask = 0;
888 wc.border_width = 0;
889 value_mask = CWBorderWidth;
890 XConfigureWindow( display(), window(), value_mask, & wc );
893 if( e->value_mask & ( CWX | CWY | CWHeight | CWWidth ))
894 configureRequest( e->value_mask, e->x, e->y, e->width, e->height, 0, false );
896 if ( e->value_mask & CWStackMode )
897 restackWindow( e->above, e->detail, NET::FromApplication, userTime(), false );
899 // Sending a synthetic configure notify always is fine, even in cases where
900 // the ICCCM doesn't require this - it can be though of as 'the WM decided to move
901 // the window later'. The client should not cause that many configure request,
902 // so this should not have any significant impact. With user moving/resizing
903 // the it should be optimized though (see also Client::setGeometry()/plainResize()/move()).
904 sendSyntheticConfigureNotify();
906 // SELI TODO accept configure requests for isDesktop windows (because kdesktop
907 // may get XRANDR resize event before kwin), but check it's still at the bottom?
912 Handles property changes of the client window
914 void Client::propertyNotifyEvent( XPropertyEvent* e )
916 Toplevel::propertyNotifyEvent( e );
917 if( e->window != window())
918 return; // ignore frame/wrapper
919 switch ( e->atom )
921 case XA_WM_NORMAL_HINTS:
922 getWmNormalHints();
923 break;
924 case XA_WM_NAME:
925 fetchName();
926 break;
927 case XA_WM_ICON_NAME:
928 fetchIconicName();
929 break;
930 case XA_WM_TRANSIENT_FOR:
931 readTransient();
932 break;
933 case XA_WM_HINTS:
934 getWMHints();
935 getIcons(); // because KWin::icon() uses WMHints as fallback
936 break;
937 default:
938 if ( e->atom == atoms->wm_protocols )
939 getWindowProtocols();
940 else if( e->atom == atoms->motif_wm_hints )
941 getMotifHints();
942 else if( e->atom == atoms->net_wm_sync_request_counter )
943 getSyncCounter();
944 break;
949 void Client::enterNotifyEvent( XCrossingEvent* e )
951 if( e->window != frameId())
952 return; // care only about entering the whole frame
953 if( e->mode == NotifyNormal ||
954 ( !options->focusPolicyIsReasonable() &&
955 e->mode == NotifyUngrab ) )
958 if (options->shadeHover && isShade())
960 delete shadeHoverTimer;
961 shadeHoverTimer = new QTimer( this );
962 connect( shadeHoverTimer, SIGNAL( timeout() ), this, SLOT( shadeHover() ));
963 shadeHoverTimer->setSingleShot( true );
964 shadeHoverTimer->start( options->shadeHoverInterval );
967 if ( options->focusPolicy == Options::ClickToFocus )
968 return;
970 if ( options->autoRaise && !isDesktop() &&
971 !isDock() && !isTopMenu() && workspace()->focusChangeEnabled() &&
972 workspace()->topClientOnDesktop( workspace()->currentDesktop()) != this )
974 delete autoRaiseTimer;
975 autoRaiseTimer = new QTimer( this );
976 connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) );
977 autoRaiseTimer->setSingleShot( true );
978 autoRaiseTimer->start( options->autoRaiseInterval );
981 QPoint currentPos( e->x_root, e->y_root );
982 if ( options->focusPolicy != Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) )
983 return;
984 // for FocusFollowsMouse, change focus only if the mouse has actually been moved, not if the focus
985 // change came because of window changes (e.g. closing a window) - #92290
986 if( options->focusPolicy != Options::FocusFollowsMouse
987 || currentPos != workspace()->focusMousePosition())
989 if ( options->delayFocus )
990 workspace()->requestDelayFocus( this );
991 else
992 workspace()->requestFocus( this );
994 return;
998 void Client::leaveNotifyEvent( XCrossingEvent* e )
1000 if( e->window != frameId())
1001 return; // care only about leaving the whole frame
1002 if ( e->mode == NotifyNormal )
1004 if ( !buttonDown )
1006 mode = PositionCenter;
1007 updateCursor();
1009 bool lostMouse = !rect().contains( QPoint( e->x, e->y ) );
1010 // 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations
1011 // (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event
1012 // comes after leaving the rect) - so lets check if the pointer is really outside the window
1014 // TODO this still sucks if a window appears above this one - it should lose the mouse
1015 // if this window is another client, but not if it's a popup ... maybe after KDE3.1 :(
1016 // (repeat after me 'AARGHL!')
1017 if ( !lostMouse && e->detail != NotifyInferior )
1019 int d1, d2, d3, d4;
1020 unsigned int d5;
1021 Window w, child;
1022 if( XQueryPointer( display(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5 ) == False
1023 || child == None )
1024 lostMouse = true; // really lost the mouse
1026 if ( lostMouse )
1028 cancelAutoRaise();
1029 workspace()->cancelDelayFocus();
1030 cancelShadeHover();
1031 if ( shade_mode == ShadeHover && !moveResizeMode && !buttonDown )
1032 setShade( ShadeNormal );
1034 if ( options->focusPolicy == Options::FocusStrictlyUnderMouse )
1035 if ( isActive() && lostMouse )
1036 workspace()->requestFocus( 0 ) ;
1037 return;
1041 #define XCapL KKeyServer::modXLock()
1042 #define XNumL KKeyServer::modXNumLock()
1043 #define XScrL KKeyServer::modXScrollLock()
1044 void Client::grabButton( int modifier )
1046 unsigned int mods[ 8 ] =
1048 0, XCapL, XNumL, XNumL | XCapL,
1049 XScrL, XScrL | XCapL,
1050 XScrL | XNumL, XScrL | XNumL | XCapL
1052 for( int i = 0;
1053 i < 8;
1054 ++i )
1055 XGrabButton( display(), AnyButton,
1056 modifier | mods[ i ],
1057 wrapperId(), false, ButtonPressMask,
1058 GrabModeSync, GrabModeAsync, None, None );
1061 void Client::ungrabButton( int modifier )
1063 unsigned int mods[ 8 ] =
1065 0, XCapL, XNumL, XNumL | XCapL,
1066 XScrL, XScrL | XCapL,
1067 XScrL | XNumL, XScrL | XNumL | XCapL
1069 for( int i = 0;
1070 i < 8;
1071 ++i )
1072 XUngrabButton( display(), AnyButton,
1073 modifier | mods[ i ], wrapperId());
1075 #undef XCapL
1076 #undef XNumL
1077 #undef XScrL
1080 Releases the passive grab for some modifier combinations when a
1081 window becomes active. This helps broken X programs that
1082 missinterpret LeaveNotify events in grab mode to work properly
1083 (Motif, AWT, Tk, ...)
1085 void Client::updateMouseGrab()
1087 if( workspace()->globalShortcutsDisabled())
1089 XUngrabButton( display(), AnyButton, AnyModifier, wrapperId());
1090 // keep grab for the simple click without modifiers if needed (see below)
1091 bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this;
1092 if( !( !options->clickRaise || not_obscured ))
1093 grabButton( None );
1094 return;
1096 if( isActive() && !workspace()->forcedGlobalMouseGrab()) // see Workspace::establishTabBoxGrab()
1098 // first grab all modifier combinations
1099 XGrabButton( display(), AnyButton, AnyModifier, wrapperId(), false,
1100 ButtonPressMask,
1101 GrabModeSync, GrabModeAsync,
1102 None, None );
1103 // remove the grab for no modifiers only if the window
1104 // is unobscured or if the user doesn't want click raise
1105 // (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is
1106 // the most recently raised window)
1107 bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this;
1108 if( !options->clickRaise || not_obscured )
1109 ungrabButton( None );
1110 else
1111 grabButton( None );
1112 ungrabButton( ShiftMask );
1113 ungrabButton( ControlMask );
1114 ungrabButton( ControlMask | ShiftMask );
1116 else
1118 XUngrabButton( display(), AnyButton, AnyModifier, wrapperId());
1119 // simply grab all modifier combinations
1120 XGrabButton(display(), AnyButton, AnyModifier, wrapperId(), false,
1121 ButtonPressMask,
1122 GrabModeSync, GrabModeAsync,
1123 None, None );
1127 // Qt propagates mouse events up the widget hierachy, which means events
1128 // for the decoration window cannot be (easily) intercepted as X11 events
1129 bool Client::eventFilter( QObject* o, QEvent* e )
1131 if( decoration == NULL
1132 || o != decoration->widget())
1133 return false;
1134 if( e->type() == QEvent::MouseButtonPress )
1136 QMouseEvent* ev = static_cast< QMouseEvent* >( e );
1137 return buttonPressEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->buttons(), ev->modifiers() ),
1138 ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1140 if( e->type() == QEvent::MouseButtonRelease )
1142 QMouseEvent* ev = static_cast< QMouseEvent* >( e );
1143 return buttonReleaseEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->buttons(), ev->modifiers() ),
1144 ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1146 if( e->type() == QEvent::MouseMove ) // FRAME i fake z enter/leave?
1148 QMouseEvent* ev = static_cast< QMouseEvent* >( e );
1149 return motionNotifyEvent( decorationId(), qtToX11State( ev->buttons(), ev->modifiers() ),
1150 ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1152 if( e->type() == QEvent::Wheel )
1154 QWheelEvent* ev = static_cast< QWheelEvent* >( e );
1155 bool r = buttonPressEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->buttons(), ev->modifiers() ),
1156 ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1157 r = r || buttonReleaseEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->buttons(), ev->modifiers() ),
1158 ev->x(), ev->y(), ev->globalX(), ev->globalY() );
1159 return r;
1161 if( e->type() == QEvent::Resize )
1163 QResizeEvent* ev = static_cast< QResizeEvent* >( e );
1164 // Filter out resize events that inform about size different than frame size.
1165 // This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync.
1166 // These events only seem to be delayed events from initial resizing before show() was called
1167 // on the decoration widget.
1168 if( ev->size() != size())
1169 return true;
1170 // HACK: Avoid decoration redraw delays. On resize Qt sets WA_WStateConfigPending
1171 // which delays all painting until a matching ConfigureNotify event comes.
1172 // But this process itself is the window manager, so it's not needed
1173 // to wait for that event, the geometry is known.
1174 // Note that if Qt in the future changes how this flag is handled and what it
1175 // triggers then this may potentionally break things. See mainly QETWidget::translateConfigEvent().
1176 decoration->widget()->setAttribute( Qt::WA_WState_ConfigPending, false );
1177 decoration->widget()->update();
1178 return false;
1180 return false;
1183 // return value matters only when filtering events before decoration gets them
1184 bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root )
1186 if (buttonDown)
1188 if( w == wrapperId())
1189 XAllowEvents(display(), SyncPointer, CurrentTime ); //xTime());
1190 return true;
1193 if( w == wrapperId() || w == frameId() || w == decorationId())
1194 { // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
1195 updateUserTime();
1196 workspace()->setWasUserInteraction();
1197 uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ?
1198 KKeyServer::modXMeta() :
1199 KKeyServer::modXAlt();
1200 bool bModKeyHeld = keyModX != 0 && ( state & KKeyServer::accelModMaskX()) == keyModX;
1202 if( isSplash()
1203 && button == Button1 && !bModKeyHeld )
1204 { // hide splashwindow if the user clicks on it
1205 hideClient( true );
1206 if( w == wrapperId())
1207 XAllowEvents(display(), SyncPointer, CurrentTime ); //xTime());
1208 return true;
1211 Options::MouseCommand com = Options::MouseNothing;
1212 bool was_action = false;
1213 bool perform_handled = false;
1214 if ( bModKeyHeld )
1216 was_action = true;
1217 switch (button)
1219 case Button1:
1220 com = options->commandAll1();
1221 break;
1222 case Button2:
1223 com = options->commandAll2();
1224 break;
1225 case Button3:
1226 com = options->commandAll3();
1227 break;
1228 case Button4:
1229 case Button5:
1230 com = options->operationWindowMouseWheel( button == Button4 ? 120 : -120 );
1231 break;
1234 else
1235 { // inactive inner window
1236 if( !isActive() && w == wrapperId())
1238 was_action = true;
1239 perform_handled = true;
1240 switch (button)
1242 case Button1:
1243 com = options->commandWindow1();
1244 break;
1245 case Button2:
1246 com = options->commandWindow2();
1247 break;
1248 case Button3:
1249 com = options->commandWindow3();
1250 break;
1251 default:
1252 com = Options::MouseActivateAndPassClick;
1255 // active inner window
1256 if( isActive() && w == wrapperId()
1257 && options->clickRaise && button < 4 ) // exclude wheel
1259 com = Options::MouseActivateRaiseAndPassClick;
1260 was_action = true;
1261 perform_handled = true;
1264 if( was_action )
1266 bool replay = performMouseCommand( com, QPoint( x_root, y_root), perform_handled );
1268 if ( isSpecialWindow())
1269 replay = true;
1271 if( w == wrapperId()) // these can come only from a grab
1272 XAllowEvents(display(), replay? ReplayPointer : SyncPointer, CurrentTime ); //xTime());
1273 return true;
1277 if( w == wrapperId()) // these can come only from a grab
1279 XAllowEvents(display(), ReplayPointer, CurrentTime ); //xTime());
1280 return true;
1282 if( w == decorationId())
1283 return false; // don't eat decoration events
1284 if( w == frameId())
1285 processDecorationButtonPress( button, state, x, y, x_root, y_root );
1286 return true;
1290 // this function processes button press events only after decoration decides not to handle them,
1291 // unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them
1292 void Client::processDecorationButtonPress( int button, int /*state*/, int x, int y, int x_root, int y_root )
1294 Options::MouseCommand com = Options::MouseNothing;
1295 bool active = isActive();
1296 if ( !wantsInput() ) // we cannot be active, use it anyway
1297 active = true;
1299 if ( button == Button1 )
1300 com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1();
1301 else if ( button == Button2 )
1302 com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2();
1303 else if ( button == Button3 )
1304 com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
1305 if( button == Button1
1306 && com != Options::MouseOperationsMenu // actions where it's not possible to get the matching
1307 && com != Options::MouseMinimize ) // mouse release event
1309 mode = mousePosition( QPoint( x, y ));
1310 buttonDown = true;
1311 moveOffset = QPoint( x, y );
1312 invertedMoveOffset = rect().bottomRight() - moveOffset;
1313 unrestrictedMoveResize = false;
1314 startDelayedMoveResize();
1315 updateCursor();
1317 performMouseCommand( com, QPoint( x_root, y_root ));
1320 // called from decoration
1321 void Client::processMousePressEvent( QMouseEvent* e )
1323 if( e->type() != QEvent::MouseButtonPress )
1325 kWarning() << "processMousePressEvent()" ;
1326 return;
1328 int button;
1329 switch( e->button())
1331 case Qt::LeftButton:
1332 button = Button1;
1333 break;
1334 case Qt::MidButton:
1335 button = Button2;
1336 break;
1337 case Qt::RightButton:
1338 button = Button3;
1339 break;
1340 default:
1341 return;
1343 processDecorationButtonPress( button, e->buttons(), e->x(), e->y(), e->globalX(), e->globalY());
1346 // return value matters only when filtering events before decoration gets them
1347 bool Client::buttonReleaseEvent( Window w, int /*button*/, int state, int x, int y, int x_root, int y_root )
1349 if( w == decorationId() && !buttonDown)
1350 return false;
1351 if( w == wrapperId())
1353 XAllowEvents(display(), SyncPointer, CurrentTime ); //xTime());
1354 return true;
1356 if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
1357 return true;
1358 x = this->x(); // translate from grab window to local coords
1359 y = this->y();
1360 if ( (state & ( Button1Mask & Button2Mask & Button3Mask )) == 0 )
1362 buttonDown = false;
1363 stopDelayedMoveResize();
1364 if ( moveResizeMode )
1366 finishMoveResize( false );
1367 // mouse position is still relative to old Client position, adjust it
1368 QPoint mousepos( x_root - x, y_root - y );
1369 mode = mousePosition( mousepos );
1371 updateCursor();
1373 return true;
1376 static bool was_motion = false;
1377 static Time next_motion_time = CurrentTime;
1378 // Check whole incoming X queue for MotionNotify events
1379 // checking whole queue is done by always returning False in the predicate.
1380 // If there are more MotionNotify events in the queue, all until the last
1381 // one may be safely discarded (if a ButtonRelease event comes, a MotionNotify
1382 // will be faked from it, so there's no need to check other events).
1383 // This helps avoiding being overloaded by being flooded from many events
1384 // from the XServer.
1385 static Bool motion_predicate( Display*, XEvent* ev, XPointer )
1387 if( ev->type == MotionNotify )
1389 was_motion = true;
1390 next_motion_time = ev->xmotion.time; // for setting time
1392 return False;
1395 static bool waitingMotionEvent()
1397 // The queue doesn't need to be checked until the X timestamp
1398 // of processes events reaches the timestamp of the last suitable
1399 // MotionNotify event in the queue.
1400 if( next_motion_time != CurrentTime
1401 && timestampCompare( xTime(), next_motion_time ) < 0 )
1402 return true;
1403 was_motion = false;
1404 XSync( display(), False ); // this helps to discard more MotionNotify events
1405 XEvent dummy;
1406 XCheckIfEvent( display(), &dummy, motion_predicate, NULL );
1407 return was_motion;
1410 // return value matters only when filtering events before decoration gets them
1411 bool Client::motionNotifyEvent( Window w, int /*state*/, int x, int y, int x_root, int y_root )
1413 if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
1414 return true; // care only about the whole frame
1415 if ( !buttonDown )
1417 Position newmode = mousePosition( QPoint( x, y ));
1418 if( newmode != mode )
1420 mode = newmode;
1421 updateCursor();
1423 // reset the timestamp for the optimization, otherwise with long passivity
1424 // the option in waitingMotionEvent() may be always true
1425 next_motion_time = CurrentTime;
1426 return false;
1428 if( w == moveResizeGrabWindow())
1430 x = this->x(); // translate from grab window to local coords
1431 y = this->y();
1433 if( !waitingMotionEvent())
1434 handleMoveResize( x, y, x_root, y_root );
1435 return true;
1438 void Client::focusInEvent( XFocusInEvent* e )
1440 if( e->window != window())
1441 return; // only window gets focus
1442 if ( e->mode == NotifyUngrab )
1443 return; // we don't care
1444 if ( e->detail == NotifyPointer )
1445 return; // we don't care
1446 if( !isShown( false ) || !isOnCurrentDesktop()) // we unmapped it, but it got focus meanwhile ->
1447 return; // activateNextClient() already transferred focus elsewhere
1448 // check if this client is in should_get_focus list or if activation is allowed
1449 bool activate = workspace()->allowClientActivation( this, -1U, true );
1450 workspace()->gotFocusIn( this ); // remove from should_get_focus list
1451 if( activate )
1452 setActive( true );
1453 else
1455 workspace()->restoreFocus();
1456 demandAttention();
1460 // When a client loses focus, FocusOut events are usually immediatelly
1461 // followed by FocusIn events for another client that gains the focus
1462 // (unless the focus goes to another screen, or to the nofocus widget).
1463 // Without this check, the former focused client would have to be
1464 // deactivated, and after that, the new one would be activated, with
1465 // a short time when there would be no active client. This can cause
1466 // flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred
1467 // from it to its transient, the fullscreen would be kept in the Active layer
1468 // at the beginning and at the end, but not in the middle, when the active
1469 // client would be temporarily none (see Client::belongToLayer() ).
1470 // Therefore, the events queue is checked, whether it contains the matching
1471 // FocusIn event, and if yes, deactivation of the previous client will
1472 // be skipped, as activation of the new one will automatically deactivate
1473 // previously active client.
1474 static bool follows_focusin = false;
1475 static bool follows_focusin_failed = false;
1476 static Bool predicate_follows_focusin( Display*, XEvent* e, XPointer arg )
1478 if( follows_focusin || follows_focusin_failed )
1479 return False;
1480 Client* c = ( Client* ) arg;
1481 if( e->type == FocusIn && c->workspace()->findClient( WindowMatchPredicate( e->xfocus.window )))
1482 { // found FocusIn
1483 follows_focusin = true;
1484 return False;
1486 // events that may be in the queue before the FocusIn event that's being
1487 // searched for
1488 if( e->type == FocusIn || e->type == FocusOut || e->type == KeymapNotify )
1489 return False;
1490 follows_focusin_failed = true; // a different event - stop search
1491 return False;
1494 static bool check_follows_focusin( Client* c )
1496 follows_focusin = follows_focusin_failed = false;
1497 XEvent dummy;
1498 // XCheckIfEvent() is used to make the search non-blocking, the predicate
1499 // always returns False, so nothing is removed from the events queue.
1500 // XPeekIfEvent() would block.
1501 XCheckIfEvent( display(), &dummy, predicate_follows_focusin, (XPointer)c );
1502 return follows_focusin;
1506 void Client::focusOutEvent( XFocusOutEvent* e )
1508 if( e->window != window())
1509 return; // only window gets focus
1510 if ( e->mode == NotifyGrab )
1511 return; // we don't care
1512 if ( isShade() )
1513 return; // here neither
1514 if ( e->detail != NotifyNonlinear
1515 && e->detail != NotifyNonlinearVirtual )
1516 // SELI check all this
1517 return; // hack for motif apps like netscape
1518 if ( QApplication::activePopupWidget() )
1519 return;
1520 if( !check_follows_focusin( this ))
1521 setActive( false );
1524 // performs _NET_WM_MOVERESIZE
1525 void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction )
1527 if( direction == NET::Move )
1528 performMouseCommand( Options::MouseMove, QPoint( x_root, y_root ));
1529 else if( moveResizeMode && direction == NET::MoveResizeCancel)
1531 finishMoveResize( true );
1532 buttonDown = false;
1533 updateCursor();
1535 else if( direction >= NET::TopLeft && direction <= NET::Left )
1537 static const Position convert[] =
1539 PositionTopLeft,
1540 PositionTop,
1541 PositionTopRight,
1542 PositionRight,
1543 PositionBottomRight,
1544 PositionBottom,
1545 PositionBottomLeft,
1546 PositionLeft
1548 if(!isResizable() || isShade())
1549 return;
1550 if( moveResizeMode )
1551 finishMoveResize( false );
1552 buttonDown = true;
1553 moveOffset = QPoint( x_root - x(), y_root - y()); // map from global
1554 invertedMoveOffset = rect().bottomRight() - moveOffset;
1555 unrestrictedMoveResize = false;
1556 mode = convert[ direction ];
1557 if( !startMoveResize())
1558 buttonDown = false;
1559 updateCursor();
1561 else if( direction == NET::KeyboardMove )
1562 { // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm
1563 QCursor::setPos( geometry().center() );
1564 performMouseCommand( Options::MouseUnrestrictedMove, geometry().center());
1566 else if( direction == NET::KeyboardSize )
1567 { // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm
1568 QCursor::setPos( geometry().bottomRight());
1569 performMouseCommand( Options::MouseUnrestrictedResize, geometry().bottomRight());
1573 void Client::keyPressEvent( uint key_code )
1575 updateUserTime();
1576 if ( !isMove() && !isResize() )
1577 return;
1578 bool is_control = key_code & Qt::CTRL;
1579 bool is_alt = key_code & Qt::ALT;
1580 key_code = key_code & ~Qt::KeyboardModifierMask;
1581 int delta = is_control?1:is_alt?32:8;
1582 QPoint pos = cursorPos();
1583 switch ( key_code )
1585 case Qt::Key_Left:
1586 pos.rx() -= delta;
1587 break;
1588 case Qt::Key_Right:
1589 pos.rx() += delta;
1590 break;
1591 case Qt::Key_Up:
1592 pos.ry() -= delta;
1593 break;
1594 case Qt::Key_Down:
1595 pos.ry() += delta;
1596 break;
1597 case Qt::Key_Space:
1598 case Qt::Key_Return:
1599 case Qt::Key_Enter:
1600 finishMoveResize( false );
1601 buttonDown = false;
1602 updateCursor();
1603 break;
1604 case Qt::Key_Escape:
1605 finishMoveResize( true );
1606 buttonDown = false;
1607 updateCursor();
1608 break;
1609 default:
1610 return;
1612 QCursor::setPos( pos );
1615 #ifdef HAVE_XSYNC
1616 void Client::syncEvent( XSyncAlarmNotifyEvent* e )
1618 if( e->alarm == sync_alarm && XSyncValueEqual( e->counter_value, sync_counter_value ))
1620 ready_for_painting = true;
1621 if( isResize())
1623 delete sync_timeout;
1624 sync_timeout = NULL;
1625 if( sync_resize_pending )
1626 performMoveResize();
1630 #endif
1632 // ****************************************
1633 // Unmanaged
1634 // ****************************************
1636 bool Unmanaged::windowEvent( XEvent* e )
1638 double old_opacity = opacity();
1639 unsigned long dirty[ 2 ];
1640 info->event( e, dirty, 2 ); // pass through the NET stuff
1641 if( dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2Opacity )
1643 if( compositing())
1645 addRepaintFull();
1646 scene->windowOpacityChanged( this );
1647 if( effects )
1648 static_cast<EffectsHandlerImpl*>(effects)->windowOpacityChanged( effectWindow(), old_opacity );
1651 switch (e->type)
1653 case UnmapNotify:
1654 unmapNotifyEvent( &e->xunmap );
1655 break;
1656 case MapNotify:
1657 mapNotifyEvent( &e->xmap );
1658 break;
1659 case ConfigureNotify:
1660 configureNotifyEvent( &e->xconfigure );
1661 break;
1662 case PropertyNotify:
1663 propertyNotifyEvent( &e->xproperty );
1664 break;
1665 default:
1667 if( e->type == Extensions::shapeNotifyEvent() )
1669 detectShape( window());
1670 addDamageFull();
1671 if( scene != NULL )
1672 scene->windowGeometryShapeChanged( this );
1673 if( effects != NULL )
1674 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geometry());
1676 #ifdef HAVE_XDAMAGE
1677 if( e->type == Extensions::damageNotifyEvent())
1678 damageNotifyEvent( reinterpret_cast< XDamageNotifyEvent* >( e ));
1679 #endif
1680 break;
1683 return false; // don't eat events, even our own unmanaged widgets are tracked
1686 void Unmanaged::mapNotifyEvent( XMapEvent* )
1690 void Unmanaged::unmapNotifyEvent( XUnmapEvent* )
1692 release();
1695 void Unmanaged::configureNotifyEvent( XConfigureEvent* e )
1697 if( effects )
1698 static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowStacking(); // keep them on top
1699 QRect newgeom( e->x, e->y, e->width, e->height );
1700 if( newgeom != geom )
1702 addWorkspaceRepaint( geometry()); // damage old area
1703 QRect old = geom;
1704 geom = newgeom;
1705 discardWindowPixmap();
1706 if( scene != NULL )
1707 scene->windowGeometryShapeChanged( this );
1708 if( effects != NULL )
1709 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), old );
1711 workspace()->restackUnmanaged( this, e->above );
1714 // ****************************************
1715 // Toplevel
1716 // ****************************************
1718 void Toplevel::propertyNotifyEvent( XPropertyEvent* e )
1720 if( e->window != window())
1721 return; // ignore frame/wrapper
1722 switch ( e->atom )
1724 default:
1725 if (e->atom == atoms->wm_client_leader )
1726 getWmClientLeader();
1727 else if( e->atom == atoms->wm_window_role )
1728 getWindowRole();
1729 break;
1731 if( effects )
1732 static_cast< EffectsHandlerImpl* >( effects )->propertyNotify( effectWindow(), e->atom );
1735 // ****************************************
1736 // Group
1737 // ****************************************
1739 bool Group::groupEvent( XEvent* e )
1741 unsigned long dirty[ 2 ];
1742 leader_info->event( e, dirty, 2 ); // pass through the NET stuff
1743 if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 )
1744 getIcons();
1745 if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 )
1746 startupIdChanged();
1747 return false;
1751 } // namespace