not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / client.cpp
blob4e86f953af54465346fc0f506877481333f13aee
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 *********************************************************************/
22 #include "client.h"
24 #include <QApplication>
25 #include <QPainter>
26 #include <QDateTime>
27 #include <QProcess>
28 #include <unistd.h>
29 #include <kstandarddirs.h>
30 #include <QWhatsThis>
31 #include <kwindowsystem.h>
32 #include <kiconloader.h>
33 #include <stdlib.h>
34 #include <signal.h>
36 #include "bridge.h"
37 #include "group.h"
38 #include "workspace.h"
39 #include "atoms.h"
40 #include "notifications.h"
41 #include "rules.h"
42 #include "scene.h"
43 #include "effects.h"
44 #include "deleted.h"
46 #include <X11/extensions/shape.h>
47 #include <QX11Info>
49 #ifdef HAVE_XSYNC
50 #include <X11/extensions/sync.h>
51 #endif
53 // Put all externs before the namespace statement to allow the linker
54 // to resolve them properly
56 namespace KWin
59 // Creating a client:
60 // - only by calling Workspace::createClient()
61 // - it creates a new client and calls manage() for it
63 // Destroying a client:
64 // - destroyClient() - only when the window itself has been destroyed
65 // - releaseWindow() - the window is kept, only the client itself is destroyed
67 /**
68 * \class Client client.h
69 * \brief The Client class encapsulates a window decoration frame.
72 /**
73 * This ctor is "dumb" - it only initializes data. All the real initialization
74 * is done in manage().
76 Client::Client( Workspace* ws )
77 : Toplevel( ws )
78 , client( None )
79 , wrapper( None )
80 , decoration( NULL )
81 , bridge( new Bridge( this ))
82 , move_faked_activity( false )
83 , move_resize_grab_window( None )
84 , move_resize_has_keyboard_grab( false )
85 , transient_for( NULL )
86 , transient_for_id( None )
87 , original_transient_for_id( None )
88 , autoRaiseTimer( NULL )
89 , shadeHoverTimer( NULL )
90 , delayedMoveResizeTimer( NULL )
91 , in_group( NULL )
92 , window_group( None )
93 , in_layer( UnknownLayer )
94 , ping_timer( NULL )
95 , process_killer( NULL )
96 , user_time( CurrentTime ) // Not known yet
97 , allowed_actions( 0 )
98 , block_geometry_updates( 0 )
99 , pending_geometry_update( PendingGeometryNone )
100 , shade_geometry_change( false )
101 #ifdef HAVE_XSYNC
102 , sync_counter( None )
103 , sync_alarm( None )
104 #endif
105 , sync_timeout( NULL )
106 , sync_resize_pending( false )
107 , border_left( 0 )
108 , border_right( 0 )
109 , border_top( 0 )
110 , border_bottom( 0 )
111 , sm_stacking_order( -1 )
112 , demandAttentionKNotifyTimer( NULL )
113 { // TODO: Do all as initialization
115 // Set the initial mapping state
116 mapping_state = Withdrawn;
117 desk = 0; // No desktop yet
119 mode = PositionCenter;
120 buttonDown = false;
121 moveResizeMode = false;
123 info = NULL;
125 shade_mode = ShadeNone;
126 active = false;
127 deleting = false;
128 keep_above = false;
129 keep_below = false;
130 motif_may_move = true;
131 motif_may_resize = true;
132 motif_may_close = true;
133 fullscreen_mode = FullScreenNone;
134 skip_taskbar = false;
135 original_skip_taskbar = false;
136 minimized = false;
137 hidden = false;
138 modal = false;
139 noborder = false;
140 app_noborder = false;
141 urgency = false;
142 ignore_focus_stealing = false;
143 demands_attention = false;
144 check_active_modal = false;
146 Pdeletewindow = 0;
147 Ptakefocus = 0;
148 Ptakeactivity = 0;
149 Pcontexthelp = 0;
150 Pping = 0;
151 input = false;
152 skip_pager = false;
154 max_mode = MaximizeRestore;
155 maxmode_restore = MaximizeRestore;
157 cmap = None;
159 geom = QRect( 0, 0, 100, 100 ); // So that decorations don't start with size being (0,0)
160 client_size = QSize( 100, 100 );
161 #if defined(HAVE_XSYNC) || defined(HAVE_XDAMAGE)
162 ready_for_painting = false; // wait for first damage or sync reply
163 #endif
165 // SELI TODO: Initialize xsizehints??
169 * "Dumb" destructor.
171 Client::~Client()
173 #ifdef HAVE_XSYNC
174 if( sync_alarm != None )
175 XSyncDestroyAlarm( display(), sync_alarm );
176 #endif
177 assert(!moveResizeMode);
178 assert( client == None );
179 assert( wrapper == None );
180 //assert( frameId() == None );
181 assert( decoration == NULL );
182 assert( block_geometry_updates == 0 );
183 assert( !check_active_modal );
184 delete bridge;
187 // Use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
188 void Client::deleteClient( Client* c, allowed_t )
190 delete c;
194 * Releases the window. The client has done its job and the window is still existing.
196 void Client::releaseWindow( bool on_shutdown )
198 assert( !deleting );
199 deleting = true;
200 Deleted* del = Deleted::create( this );
201 if( effects )
203 static_cast<EffectsHandlerImpl*>(effects)->windowClosed( effectWindow());
204 scene->windowClosed( this, del );
206 finishCompositing();
207 workspace()->discardUsedWindowRules( this, true ); // Remove ForceTemporarily rules
208 StackingUpdatesBlocker blocker( workspace());
209 if (moveResizeMode)
210 leaveMoveResize();
211 finishWindowRules();
212 ++block_geometry_updates;
213 if( isOnCurrentDesktop() && isShown( true ))
214 addWorkspaceRepaint( geometry());
215 // Grab X during the release to make removing of properties, setting to withdrawn state
216 // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2)
217 grabXServer();
218 exportMappingState( WithdrawnState );
219 setModal( false ); // Otherwise its mainwindow wouldn't get focus
220 hidden = true; // So that it's not considered visible anymore (can't use hideClient(), it would set flags)
221 if( !on_shutdown )
222 workspace()->clientHidden( this );
223 XUnmapWindow( display(), frameId()); // Destroying decoration would cause ugly visual effect
224 destroyDecoration();
225 cleanGrouping();
226 if( !on_shutdown )
228 workspace()->removeClient( this, Allowed );
229 // Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7)
230 info->setDesktop( 0 );
231 desk = 0;
232 info->setState( 0, info->state()); // Reset all state flags
234 XDeleteProperty( display(), client, atoms->kde_net_wm_user_creation_time);
235 XDeleteProperty( display(), client, atoms->net_frame_extents );
236 XDeleteProperty( display(), client, atoms->kde_net_wm_frame_strut );
237 XReparentWindow( display(), client, rootWindow(), x(), y());
238 XRemoveFromSaveSet( display(), client );
239 XSelectInput( display(), client, NoEventMask );
240 if( on_shutdown )
241 // Map the window, so it can be found after another WM is started
242 XMapWindow( display(), client );
243 // TODO: Preserve minimized, shaded etc. state?
244 else // Make sure it's not mapped if the app unmapped it (#65279). The app
245 // may do map+unmap before we initially map the window by calling rawShow() from manage().
246 XUnmapWindow( display(), client );
247 client = None;
248 XDestroyWindow( display(), wrapper );
249 wrapper = None;
250 XDestroyWindow( display(), frameId());
251 //frame = None;
252 --block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry
253 disownDataPassedToDeleted();
254 del->unrefWindow();
255 checkNonExistentClients();
256 deleteClient( this, Allowed );
257 ungrabXServer();
261 * Like releaseWindow(), but this one is called when the window has been already destroyed
262 * (E.g. The application closed it)
264 void Client::destroyClient()
266 assert( !deleting );
267 deleting = true;
268 Deleted* del = Deleted::create( this );
269 if( effects )
271 static_cast<EffectsHandlerImpl*>(effects)->windowClosed( effectWindow());
272 scene->windowClosed( this, del );
274 finishCompositing();
275 workspace()->discardUsedWindowRules( this, true ); // Remove ForceTemporarily rules
276 StackingUpdatesBlocker blocker( workspace());
277 if (moveResizeMode)
278 leaveMoveResize();
279 finishWindowRules();
280 ++block_geometry_updates;
281 if( isOnCurrentDesktop() && isShown( true ))
282 addWorkspaceRepaint( geometry());
283 setModal( false );
284 hidden = true; // So that it's not considered visible anymore
285 workspace()->clientHidden( this );
286 destroyDecoration();
287 cleanGrouping();
288 workspace()->removeClient( this, Allowed );
289 client = None; // invalidate
290 XDestroyWindow( display(), wrapper );
291 wrapper = None;
292 XDestroyWindow( display(), frameId());
293 //frame = None;
294 --block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry
295 disownDataPassedToDeleted();
296 del->unrefWindow();
297 checkNonExistentClients();
298 deleteClient( this, Allowed );
301 void Client::updateDecoration( bool check_workspace_pos, bool force )
303 if( !force &&
304 (( decoration == NULL && noBorder() ) || ( decoration != NULL && !noBorder() )))
305 return;
306 bool do_show = false;
307 QRect oldgeom = geometry();
308 blockGeometryUpdates( true );
309 if( force )
310 destroyDecoration();
311 if( !noBorder() )
313 setMask( QRegion()); // Reset shape mask
314 decoration = workspace()->createDecoration( bridge );
315 // TODO: Check decoration's minimum size?
316 decoration->init();
317 decoration->widget()->installEventFilter( this );
318 XReparentWindow( display(), decoration->widget()->winId(), frameId(), 0, 0 );
319 decoration->widget()->lower();
320 decoration->borders( border_left, border_right, border_top, border_bottom );
321 int save_workarea_diff_x = workarea_diff_x;
322 int save_workarea_diff_y = workarea_diff_y;
323 move( calculateGravitation( false ));
324 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
325 workarea_diff_x = save_workarea_diff_x;
326 workarea_diff_y = save_workarea_diff_y;
327 do_show = true;
328 if( compositing() )
329 discardWindowPixmap();
330 if( scene != NULL )
331 scene->windowGeometryShapeChanged( this );
332 if( effects != NULL )
333 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom );
335 else
336 destroyDecoration();
337 if( check_workspace_pos )
338 checkWorkspacePosition();
339 blockGeometryUpdates( false );
340 if( do_show )
341 decoration->widget()->show();
342 updateFrameExtents();
345 void Client::destroyDecoration()
347 QRect oldgeom = geometry();
348 if( decoration != NULL )
350 delete decoration;
351 decoration = NULL;
352 QPoint grav = calculateGravitation( true );
353 border_left = border_right = border_top = border_bottom = 0;
354 setMask( QRegion()); // Reset shape mask
355 int save_workarea_diff_x = workarea_diff_x;
356 int save_workarea_diff_y = workarea_diff_y;
357 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
358 move( grav );
359 workarea_diff_x = save_workarea_diff_x;
360 workarea_diff_y = save_workarea_diff_y;
361 if( compositing() )
362 discardWindowPixmap();
363 if( scene != NULL && !deleting )
364 scene->windowGeometryShapeChanged( this );
365 if( effects != NULL && !deleting )
366 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom );
370 bool Client::checkBorderSizes( bool also_resize )
372 if( decoration == NULL )
373 return false;
374 int new_left, new_right, new_top, new_bottom;
375 decoration->borders( new_left, new_right, new_top, new_bottom );
376 if( new_left == border_left && new_right == border_right &&
377 new_top == border_top && new_bottom == border_bottom )
378 return false;
379 if( !also_resize )
381 border_left = new_left;
382 border_right = new_right;
383 border_top = new_top;
384 border_bottom = new_bottom;
385 return true;
387 GeometryUpdatesBlocker blocker( this );
388 move( calculateGravitation( true ));
389 border_left = new_left;
390 border_right = new_right;
391 border_top = new_top;
392 border_bottom = new_bottom;
393 move( calculateGravitation( false ));
394 plainResize( sizeForClientSize( clientSize() ), ForceGeometrySet );
395 checkWorkspacePosition();
396 return true;
399 void Client::repaintDecoration()
401 if( decoration != NULL )
402 decoration->widget()->update();
405 void Client::detectNoBorder()
407 if( shape())
409 noborder = true;
410 app_noborder = true;
411 return;
413 switch( windowType())
415 case NET::Desktop :
416 case NET::Dock :
417 case NET::TopMenu :
418 case NET::Splash :
419 noborder = true;
420 app_noborder = true;
421 break;
422 case NET::Unknown :
423 case NET::Normal :
424 case NET::Toolbar :
425 case NET::Menu :
426 case NET::Dialog :
427 case NET::Utility :
428 noborder = false;
429 break;
430 default:
431 abort();
433 // NET::Override is some strange beast without clear definition, usually
434 // just meaning "noborder", so let's treat it only as such flag, and ignore it as
435 // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it)
436 if( info->windowType( SUPPORTED_MANAGED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override )
438 noborder = true;
439 app_noborder = true;
443 void Client::updateFrameExtents()
445 NETStrut strut;
446 strut.left = border_left;
447 strut.right = border_right;
448 strut.top = border_top;
449 strut.bottom = border_bottom;
450 info->setFrameExtents( strut );
454 * Resizes the decoration, and makes sure the decoration widget gets resize event
455 * even if the size hasn't changed. This is needed to make sure the decoration
456 * re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes,
457 * the decoration may turn on/off some borders, but the actual size
458 * of the decoration stays the same).
460 void Client::resizeDecoration( const QSize& s )
462 if( decoration == NULL )
463 return;
464 QSize oldsize = decoration->widget()->size();
465 decoration->resize( s );
466 if( oldsize == s )
468 QResizeEvent e( s, oldsize );
469 QApplication::sendEvent( decoration->widget(), &e );
473 bool Client::noBorder() const
475 return noborder || isFullScreen();
478 bool Client::userCanSetNoBorder() const
480 return !isFullScreen() && !isShade();
483 void Client::setNoBorder( bool set )
485 if( !userCanSetNoBorder() )
486 return;
487 set = rules()->checkNoBorder( set );
488 if( noborder == set )
489 return;
490 noborder = set;
491 updateDecoration( true, false );
492 updateWindowRules();
495 void Client::updateShape()
497 if( shape() )
498 { // Workaround for #19644 - Shaped windows shouldn't have decoration
499 if( !app_noborder )
500 { // Only when shape is detected for the first time, still let the user to override
501 app_noborder = true;
502 noborder = true;
503 updateDecoration( true );
506 if( shape() && noBorder() )
507 XShapeCombineShape( display(), frameId(), ShapeBounding,
508 clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSet );
510 // Decoration mask (i.e. 'else' here) setting is done in setMask()
511 // when the decoration calls it or when the decoration is created/destroyed
512 updateInputShape();
513 if( compositing())
515 addRepaintFull();
516 addWorkspaceRepaint( geometry()); // In case shape change removes part of this window
518 if( scene != NULL )
519 scene->windowGeometryShapeChanged( this );
520 if( effects != NULL )
521 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geometry());
524 static Window shape_helper_window = None;
526 void Client::updateInputShape()
528 if( hiddenPreview() ) // Sets it to none, don't change
529 return;
530 if( Extensions::shapeInputAvailable())
531 { // There appears to be no way to find out if a window has input
532 // shape set or not, so always propagate the input shape
533 // (it's the same like the bounding shape by default).
534 // Also, build the shape using a helper window, not directly
535 // in the frame window, because the sequence set-shape-to-frame,
536 // remove-shape-of-client, add-input-shape-of-client has the problem
537 // that after the second step there's a hole in the input shape
538 // until the real shape of the client is added and that can make
539 // the window lose focus (which is a problem with mouse focus policies)
540 // TODO: It seems there is, after all - XShapeGetRectangles() - but maybe this is better
541 if( shape_helper_window == None )
542 shape_helper_window = XCreateSimpleWindow( display(), rootWindow(),
543 0, 0, 1, 1, 0, 0, 0 );
544 XResizeWindow( display(), shape_helper_window, width(), height());
545 XShapeCombineShape( display(), shape_helper_window, ShapeInput, 0, 0,
546 frameId(), ShapeBounding, ShapeSet );
547 XShapeCombineShape( display(), shape_helper_window, ShapeInput,
548 clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSubtract );
549 XShapeCombineShape( display(), shape_helper_window, ShapeInput,
550 clientPos().x(), clientPos().y(), window(), ShapeInput, ShapeUnion );
551 XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0,
552 shape_helper_window, ShapeInput, ShapeSet );
556 void Client::setMask( const QRegion& reg, int mode )
558 if( _mask == reg )
559 return;
560 _mask = reg;
561 Window shape_window = frameId();
562 if( shape() )
563 { // The same way of applying a shape without strange intermediate states like above
564 if( shape_helper_window == None )
565 shape_helper_window = XCreateSimpleWindow( display(), rootWindow(),
566 0, 0, 1, 1, 0, 0, 0 );
567 shape_window = shape_helper_window;
569 if( reg.isEmpty() )
570 XShapeCombineMask( display(), shape_window, ShapeBounding, 0, 0, None, ShapeSet );
571 else if( mode == X::Unsorted )
572 XShapeCombineRegion( display(), shape_window, ShapeBounding, 0, 0, reg.handle(), ShapeSet );
573 else
575 QVector< QRect > rects = reg.rects();
576 XRectangle* xrects = new XRectangle[rects.count()];
577 for( int i = 0; i < rects.count(); ++i )
579 xrects[i].x = rects[i].x();
580 xrects[i].y = rects[i].y();
581 xrects[i].width = rects[i].width();
582 xrects[i].height = rects[i].height();
584 XShapeCombineRectangles( display(), shape_window, ShapeBounding, 0, 0,
585 xrects, rects.count(), ShapeSet, mode );
586 delete[] xrects;
588 if( shape() )
589 { // The rest of the applyign using a temporary window
590 XRectangle rec = { 0, 0, clientSize().width(), clientSize().height() };
591 XShapeCombineRectangles( display(), shape_helper_window, ShapeBounding,
592 clientPos().x(), clientPos().y(), &rec, 1, ShapeSubtract, Unsorted );
593 XShapeCombineShape( display(), shape_helper_window, ShapeBounding,
594 clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeUnion );
595 XShapeCombineShape( display(), frameId(), ShapeBounding, 0, 0,
596 shape_helper_window, ShapeBounding, ShapeSet );
598 if( scene != NULL )
599 scene->windowGeometryShapeChanged( this );
600 if( effects != NULL )
601 static_cast<EffectsHandlerImpl*>( effects )->windowGeometryShapeChanged( effectWindow(), geometry() );
602 updateShape();
605 QRegion Client::mask() const
607 if( _mask.isEmpty() )
608 return QRegion( 0, 0, width(), height() );
609 return _mask;
612 void Client::hideClient( bool hide )
614 if( hidden == hide )
615 return;
616 hidden = hide;
617 updateVisibility();
621 * Returns whether the window is minimizable or not
623 bool Client::isMinimizable() const
625 if( isSpecialWindow() )
626 return false;
627 if( isTransient() )
628 { // #66868 - Let other xmms windows be minimized when the mainwindow is minimized
629 bool shown_mainwindow = false;
630 ClientList mainclients = mainClients();
631 for( ClientList::ConstIterator it = mainclients.constBegin();
632 it != mainclients.constEnd();
633 ++it )
634 if( (*it)->isShown( true ))
635 shown_mainwindow = true;
636 if( !shown_mainwindow )
637 return true;
639 #if 0
640 // This is here because kicker's taskbar doesn't provide separate entries
641 // for windows with an explicitly given parent
642 // TODO: perhaps this should be redone
643 // Disabled for now, since at least modal dialogs should be minimizable
644 // (resulting in the mainwindow being minimized too).
645 if( transientFor() != NULL )
646 return false;
647 #endif
648 if( !wantsTabFocus() ) // SELI, TODO: - NET::Utility? why wantsTabFocus() - skiptaskbar? ?
649 return false;
650 return true;
654 * Minimizes this client plus its transients
656 void Client::minimize( bool avoid_animation )
658 if( !isMinimizable() || isMinimized() )
659 return;
661 Notify::raise( Notify::Minimize );
663 minimized = true;
665 updateVisibility();
666 updateAllowedActions();
667 workspace()->updateMinimizedOfTransients( this );
668 updateWindowRules();
669 workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast );
670 if( effects && !avoid_animation ) // TODO: Shouldn't it tell effects at least about the change?
671 static_cast<EffectsHandlerImpl*>(effects)->windowMinimized( effectWindow());
674 void Client::unminimize( bool avoid_animation )
676 if( !isMinimized())
677 return;
679 Notify::raise( Notify::UnMinimize );
680 minimized = false;
681 updateVisibility();
682 updateAllowedActions();
683 workspace()->updateMinimizedOfTransients( this );
684 updateWindowRules();
685 if( effects && !avoid_animation )
686 static_cast<EffectsHandlerImpl*>( effects )->windowUnminimized( effectWindow() );
689 QRect Client::iconGeometry() const
691 NETRect r = info->iconGeometry();
692 QRect geom( r.pos.x, r.pos.y, r.size.width, r.size.height );
693 if( geom.isValid() )
694 return geom;
695 else
696 { // Check all mainwindows of this window (recursively)
697 foreach( Client* mainwin, mainClients() )
699 geom = mainwin->iconGeometry();
700 if( geom.isValid() )
701 return geom;
703 // No mainwindow (or their parents) with icon geometry was found
704 return QRect();
708 bool Client::isShadeable() const
710 return !isSpecialWindow() && !noBorder();
713 void Client::setShade( ShadeMode mode )
715 if( !isShadeable())
716 return;
717 mode = rules()->checkShade( mode );
718 if( shade_mode == mode )
719 return;
720 bool was_shade = isShade();
721 ShadeMode was_shade_mode = shade_mode;
722 shade_mode = mode;
723 if( was_shade == isShade())
725 if( decoration != NULL ) // Decoration may want to update after e.g. hover-shade changes
726 decoration->shadeChange();
727 return; // No real change in shaded state
730 if( shade_mode == ShadeNormal )
732 if( isShown( true ) && isOnCurrentDesktop() )
733 Notify::raise( Notify::ShadeUp );
735 else if( shade_mode == ShadeNone )
737 if( isShown( true ) && isOnCurrentDesktop() )
738 Notify::raise( Notify::ShadeDown );
741 assert( decoration != NULL ); // noborder windows can't be shaded
742 GeometryUpdatesBlocker blocker( this );
743 // Decorations may turn off some borders when shaded
744 decoration->borders( border_left, border_right, border_top, border_bottom );
746 // TODO: All this unmapping, resizing etc. feels too much duplicated from elsewhere
747 if ( isShade())
748 { // shade_mode == ShadeNormal
749 addWorkspaceRepaint( geometry() );
750 // Shade
751 shade_geometry_change = true;
752 QSize s( sizeForClientSize( QSize( clientSize() )));
753 s.setHeight( border_top + border_bottom );
754 XSelectInput( display(), wrapper, ClientWinMask ); // Avoid getting UnmapNotify
755 XUnmapWindow( display(), wrapper );
756 XUnmapWindow( display(), client );
757 XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask );
758 plainResize( s );
759 shade_geometry_change = false;
760 if( isActive())
762 if( was_shade_mode == ShadeHover )
763 workspace()->activateNextClient( this );
764 else
765 workspace()->focusToNull();
768 else
770 shade_geometry_change = true;
771 QSize s( sizeForClientSize( clientSize() ));
772 shade_geometry_change = false;
773 plainResize( s );
774 if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
775 setActive( true );
776 XMapWindow( display(), wrapperId() );
777 XMapWindow( display(), window() );
778 if ( isActive() )
779 workspace()->requestFocus( this );
781 checkMaximizeGeometry();
782 info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
783 info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
784 discardWindowPixmap();
785 updateVisibility();
786 updateAllowedActions();
787 workspace()->updateMinimizedOfTransients( this );
788 decoration->shadeChange();
789 updateWindowRules();
792 void Client::shadeHover()
794 setShade( ShadeHover );
795 cancelShadeHoverTimer();
798 void Client::shadeUnhover()
800 setShade( ShadeNormal );
801 cancelShadeHoverTimer();
804 void Client::cancelShadeHoverTimer()
806 delete shadeHoverTimer;
807 shadeHoverTimer = 0;
810 void Client::toggleShade()
811 { // If the mode is ShadeHover or ShadeActive, cancel shade too
812 setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
815 void Client::updateVisibility()
817 if( deleting )
818 return;
819 if( hidden )
821 info->setState( NET::Hidden, NET::Hidden );
822 setSkipTaskbar( true, false ); // Also hide from taskbar
823 if( compositing() && options->hiddenPreviews == HiddenPreviewsAlways )
824 internalKeep( Allowed );
825 else
826 internalHide( Allowed );
827 return;
829 setSkipTaskbar( original_skip_taskbar, false ); // Reset from 'hidden'
830 if( minimized )
832 info->setState( NET::Hidden, NET::Hidden );
833 if( compositing() && options->hiddenPreviews == HiddenPreviewsAlways )
834 internalKeep( Allowed );
835 else
836 internalHide( Allowed );
837 return;
839 info->setState( 0, NET::Hidden );
840 if( !isOnCurrentDesktop())
842 if( compositing() && options->hiddenPreviews != HiddenPreviewsNever )
843 internalKeep( Allowed );
844 else
845 internalHide( Allowed );
846 return;
848 bool belongs_to_desktop = false;
849 for( ClientList::ConstIterator it = group()->members().constBegin();
850 it != group()->members().constEnd();
851 ++it )
852 if( (*it)->isDesktop() )
854 belongs_to_desktop = true;
855 break;
857 if( !belongs_to_desktop && workspace()->showingDesktop())
858 workspace()->resetShowingDesktop( true );
859 internalShow( Allowed );
863 * Sets the client window's mapping state. Possible values are
864 * WithdrawnState, IconicState, NormalState.
866 void Client::exportMappingState( int s )
868 assert( client != None );
869 assert( !deleting || s == WithdrawnState );
870 if( s == WithdrawnState )
872 XDeleteProperty( display(), window(), atoms->wm_state );
873 return;
875 assert( s == NormalState || s == IconicState );
877 unsigned long data[2];
878 data[0] = (unsigned long) s;
879 data[1] = (unsigned long) None;
880 XChangeProperty(display(), window(), atoms->wm_state, atoms->wm_state, 32,
881 PropModeReplace, (unsigned char*)( data ), 2);
884 void Client::internalShow( allowed_t )
886 if( mapping_state == Mapped )
887 return;
888 MappingState old = mapping_state;
889 mapping_state = Mapped;
890 if( old == Unmapped || old == Withdrawn )
891 map( Allowed );
892 if( old == Kept )
893 updateHiddenPreview();
894 workspace()->checkUnredirect();
897 void Client::internalHide( allowed_t )
899 if( mapping_state == Unmapped )
900 return;
901 MappingState old = mapping_state;
902 mapping_state = Unmapped;
903 if( old == Mapped || old == Kept )
904 unmap( Allowed );
905 if( old == Kept )
906 updateHiddenPreview();
907 addWorkspaceRepaint( geometry() );
908 workspace()->clientHidden( this );
909 workspace()->checkUnredirect();
912 void Client::internalKeep( allowed_t )
914 assert( compositing() );
915 if( mapping_state == Kept )
916 return;
917 MappingState old = mapping_state;
918 mapping_state = Kept;
919 if( old == Unmapped || old == Withdrawn )
920 map( Allowed );
921 updateHiddenPreview();
922 addWorkspaceRepaint( geometry() );
923 workspace()->clientHidden( this );
924 workspace()->checkUnredirect();
928 * Maps (shows) the client. Note that it is mapping state of the frame,
929 * not necessarily the client window itself (i.e. a shaded window is here
930 * considered mapped, even though it is in IconicState).
932 void Client::map( allowed_t )
934 // XComposite invalidates backing pixmaps on unmap (minimize, different
935 // virtual desktop, etc.). We kept the last known good pixmap around
936 // for use in effects, but now we want to have access to the new pixmap
937 if( compositing() )
938 discardWindowPixmap();
939 if( decoration != NULL )
940 decoration->widget()->show(); // Not really necessary, but let it know the state
941 XMapWindow( display(), frameId());
942 if( !isShade())
944 XMapWindow( display(), wrapper );
945 XMapWindow( display(), client );
946 exportMappingState( NormalState );
948 else
949 exportMappingState( IconicState );
953 * Unmaps the client. Again, this is about the frame.
955 void Client::unmap( allowed_t )
957 // Here it may look like a race condition, as some other client might try to unmap
958 // the window between these two XSelectInput() calls. However, they're supposed to
959 // use XWithdrawWindow(), which also sends a synthetic event to the root window,
960 // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
961 // will be missed is also very minimal, so I don't think it's needed to grab the server
962 // here.
963 XSelectInput( display(), wrapper, ClientWinMask ); // Avoid getting UnmapNotify
964 XUnmapWindow( display(), frameId() );
965 XUnmapWindow( display(), wrapper );
966 XUnmapWindow( display(), client );
967 XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask );
968 if( decoration != NULL )
969 decoration->widget()->hide(); // Not really necessary, but let it know the state
970 exportMappingState( IconicState );
974 * XComposite doesn't keep window pixmaps of unmapped windows, which means
975 * there wouldn't be any previews of windows that are minimized or on another
976 * virtual desktop. Therefore rawHide() actually keeps such windows mapped.
977 * However special care needs to be taken so that such windows don't interfere.
978 * Therefore they're put very low in the stacking order and they have input shape
979 * set to none, which hopefully is enough. If there's no input shape available,
980 * then it's hoped that there will be some other desktop above it *shrug*.
981 * Using normal shape would be better, but that'd affect other things, e.g. painting
982 * of the actual preview.
984 void Client::updateHiddenPreview()
986 if( hiddenPreview() )
988 workspace()->forceRestacking();
989 if( Extensions::shapeInputAvailable() )
990 XShapeCombineRectangles( display(), frameId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
992 else
994 workspace()->forceRestacking();
995 updateInputShape();
999 void Client::sendClientMessage( Window w, Atom a, Atom protocol, long data1, long data2, long data3 )
1001 XEvent ev;
1002 long mask;
1004 memset( &ev, 0, sizeof( ev ));
1005 ev.xclient.type = ClientMessage;
1006 ev.xclient.window = w;
1007 ev.xclient.message_type = a;
1008 ev.xclient.format = 32;
1009 ev.xclient.data.l[0] = protocol;
1010 ev.xclient.data.l[1] = xTime();
1011 ev.xclient.data.l[2] = data1;
1012 ev.xclient.data.l[3] = data2;
1013 ev.xclient.data.l[4] = data3;
1014 mask = 0L;
1015 if( w == rootWindow() )
1016 mask = SubstructureRedirectMask; // Magic!
1017 XSendEvent( display(), w, False, mask, &ev );
1021 * Returns whether the window may be closed (have a close button)
1023 bool Client::isCloseable() const
1025 return rules()->checkCloseable( motif_may_close && !isSpecialWindow() );
1029 * Closes the window by either sending a delete_window message or using XKill.
1031 void Client::closeWindow()
1033 if( !isCloseable() )
1034 return;
1036 // Update user time, because the window may create a confirming dialog.
1037 updateUserTime();
1039 if ( Pdeletewindow )
1041 Notify::raise( Notify::Close );
1042 sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
1043 pingWindow();
1045 else // Client will not react on wm_delete_window. We have not choice
1046 // but destroy his connection to the XServer.
1047 killWindow();
1052 * Kills the window via XKill
1054 void Client::killWindow()
1056 kDebug( 1212 ) << "Client::killWindow():" << caption();
1058 // Not sure if we need an Notify::Kill or not.. until then, use
1059 // Notify::Close
1060 Notify::raise( Notify::Close );
1062 if( isDialog() )
1063 Notify::raise( Notify::TransDelete );
1064 if( isNormalWindow() )
1065 Notify::raise( Notify::Delete );
1066 killProcess( false );
1067 XKillClient(display(), window() ); // Always kill this client at the server
1068 destroyClient();
1072 * Send a ping to the window using _NET_WM_PING if possible if it
1073 * doesn't respond within a reasonable time, it will be killed.
1075 void Client::pingWindow()
1077 if( !Pping )
1078 return; // Can't ping :(
1079 if( options->killPingTimeout == 0 )
1080 return; // Turned off
1081 if( ping_timer != NULL )
1082 return; // Pinging already
1083 ping_timer = new QTimer( this );
1084 connect( ping_timer, SIGNAL( timeout() ), SLOT( pingTimeout() ));
1085 ping_timer->setSingleShot( true );
1086 ping_timer->start( options->killPingTimeout );
1087 ping_timestamp = xTime();
1088 workspace()->sendPingToWindow( window(), ping_timestamp );
1091 void Client::gotPing( Time timestamp )
1093 // Just plain compare is not good enough because of 64bit and truncating and whatnot
1094 if( NET::timestampCompare( timestamp, ping_timestamp ) != 0 )
1095 return;
1096 delete ping_timer;
1097 ping_timer = NULL;
1098 if( process_killer != NULL )
1100 process_killer->kill();
1101 // Recycle when the process manager has noticed that the process exited
1102 // a delete process_killer here sometimes causes a hang in waitForFinished
1103 connect(process_killer, SIGNAL( finished(int, QProcess::ExitStatus) ),
1104 process_killer, SLOT( deleteLater() ));
1105 process_killer = NULL;
1109 void Client::pingTimeout()
1111 kDebug( 1212 ) << "Ping timeout:" << caption();
1112 ping_timer->deleteLater();
1113 ping_timer = NULL;
1114 killProcess( true, ping_timestamp );
1117 void Client::killProcess( bool ask, Time timestamp )
1119 if( process_killer != NULL )
1120 return;
1121 Q_ASSERT( !ask || timestamp != CurrentTime );
1122 QByteArray machine = wmClientMachine( true );
1123 pid_t pid = info->pid();
1124 if( pid <= 0 || machine.isEmpty()) // Needed properties missing
1125 return;
1126 kDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")";
1127 if( !ask )
1129 if( machine != "localhost" )
1131 QStringList lst;
1132 lst << machine << "kill" << QString::number( pid );
1133 QProcess::startDetached( "xon",lst );
1135 else
1136 ::kill( pid, SIGTERM );
1138 else
1140 process_killer = new QProcess( this );
1141 connect( process_killer, SIGNAL( error(QProcess::ProcessError) ), SLOT( processKillerExited() ));
1142 connect( process_killer, SIGNAL( finished(int, QProcess::ExitStatus) ), SLOT( processKillerExited() ));
1143 process_killer->start( KStandardDirs::findExe( "kwin_killer_helper" ),
1144 QStringList() << "--pid" << QByteArray().setNum( unsigned( pid )) << "--hostname" << machine
1145 << "--windowname" << caption()
1146 << "--applicationname" << resourceClass()
1147 << "--wid" << QString::number( window() )
1148 << "--timestamp" << QString::number( timestamp ));
1152 void Client::processKillerExited()
1154 kDebug( 1212 ) << "Killer exited";
1155 delete process_killer;
1156 process_killer = NULL;
1159 void Client::setSkipTaskbar( bool b, bool from_outside )
1161 int was_wants_tab_focus = wantsTabFocus();
1162 if( from_outside )
1164 b = rules()->checkSkipTaskbar( b );
1165 original_skip_taskbar = b;
1167 if( b == skipTaskbar() )
1168 return;
1169 skip_taskbar = b;
1170 info->setState( b ? NET::SkipTaskbar : 0, NET::SkipTaskbar );
1171 updateWindowRules();
1172 if( was_wants_tab_focus != wantsTabFocus())
1173 workspace()->updateFocusChains( this,
1174 isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate );
1177 void Client::setSkipPager( bool b )
1179 b = rules()->checkSkipPager( b );
1180 if( b == skipPager() )
1181 return;
1182 skip_pager = b;
1183 info->setState( b ? NET::SkipPager : 0, NET::SkipPager );
1184 updateWindowRules();
1187 void Client::setModal( bool m )
1188 { // Qt-3.2 can have even modal normal windows :(
1189 if( modal == m )
1190 return;
1191 modal = m;
1192 if( !modal )
1193 return;
1194 // Changing modality for a mapped window is weird (?)
1195 // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
1198 void Client::setDesktop( int desktop )
1200 if( desktop != NET::OnAllDesktops ) // Do range check
1201 desktop = qMax( 1, qMin( workspace()->numberOfDesktops(), desktop ));
1202 desktop = qMin( workspace()->numberOfDesktops(), rules()->checkDesktop( desktop ));
1203 if( desk == desktop )
1204 return;
1205 int was_desk = desk;
1206 desk = desktop;
1207 info->setDesktop( desktop );
1208 if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
1209 { // onAllDesktops changed
1210 if( isShown( true ))
1211 Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
1212 workspace()->updateOnAllDesktopsOfTransients( this );
1214 if( decoration != NULL )
1215 decoration->desktopChange();
1216 workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
1217 updateVisibility();
1218 updateWindowRules();
1222 * Returns the virtual desktop within the workspace() the client window
1223 * is located in, 0 if it isn't located on any special desktop (not mapped yet),
1224 * or NET::OnAllDesktops. Do not use desktop() directly, use
1225 * isOnDesktop() instead.
1227 int Client::desktop() const
1229 return desk;
1232 void Client::setOnAllDesktops( bool b )
1234 if(( b && isOnAllDesktops() ) ||
1235 ( !b && !isOnAllDesktops() ))
1236 return;
1237 if( b )
1238 setDesktop( NET::OnAllDesktops );
1239 else
1240 setDesktop( workspace()->currentDesktop());
1244 * Performs activation and/or raising of the window
1246 void Client::takeActivity( int flags, bool handled, allowed_t )
1248 if( !handled || !Ptakeactivity )
1250 if( flags & ActivityFocus )
1251 takeFocus( Allowed );
1252 if( flags & ActivityRaise )
1253 workspace()->raiseClient( this );
1254 return;
1257 #ifndef NDEBUG
1258 static Time previous_activity_timestamp;
1259 static Client* previous_client;
1261 //if( previous_activity_timestamp == xTime() && previous_client != this )
1262 // {
1263 // kDebug( 1212 ) << "Repeated use of the same X timestamp for activity";
1264 // kDebug( 1212 ) << kBacktrace();
1265 // }
1267 previous_activity_timestamp = xTime();
1268 previous_client = this;
1269 #endif
1271 workspace()->sendTakeActivity( this, xTime(), flags );
1275 * Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
1277 void Client::takeFocus( allowed_t )
1279 #ifndef NDEBUG
1280 static Time previous_focus_timestamp;
1281 static Client* previous_client;
1283 //if( previous_focus_timestamp == xTime() && previous_client != this )
1284 // {
1285 // kDebug( 1212 ) << "Repeated use of the same X timestamp for focus";
1286 // kDebug( 1212 ) << kBacktrace();
1287 // }
1289 previous_focus_timestamp = xTime();
1290 previous_client = this;
1291 #endif
1292 if( rules()->checkAcceptFocus( input ))
1293 XSetInputFocus( display(), window(), RevertToPointerRoot, xTime() );
1294 if( Ptakefocus )
1295 sendClientMessage( window(), atoms->wm_protocols, atoms->wm_take_focus );
1296 workspace()->setShouldGetFocus( this );
1300 * Returns whether the window provides context help or not. If it does,
1301 * you should show a help menu item or a help button like '?' and call
1302 * contextHelp() if this is invoked.
1304 * \sa contextHelp()
1306 bool Client::providesContextHelp() const
1308 return Pcontexthelp;
1312 * Invokes context help on the window. Only works if the window
1313 * actually provides context help.
1315 * \sa providesContextHelp()
1317 void Client::showContextHelp()
1319 if( Pcontexthelp )
1321 sendClientMessage( window(), atoms->wm_protocols, atoms->net_wm_context_help );
1322 QWhatsThis::enterWhatsThisMode(); // SELI TODO: ?
1327 * Fetches the window's caption (WM_NAME property). It will be
1328 * stored in the client's caption().
1330 void Client::fetchName()
1332 setCaption( readName());
1335 QString Client::readName() const
1337 if( info->name() && info->name()[0] != '\0' )
1338 return QString::fromUtf8( info->name() );
1339 else
1340 return KWindowSystem::readNameProperty( window(), XA_WM_NAME );
1343 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
1345 // The list is taken from http://www.unicode.org/reports/tr9/ (#154840)
1346 QChar LRM(0x200E);
1347 QChar RLM(0x200F);
1348 QChar LRE(0x202A);
1349 QChar RLE(0x202B);
1350 QChar LRO(0x202D);
1351 QChar RLO(0x202E);
1352 QChar PDF(0x202C);
1354 void Client::setCaption( const QString& _s, bool force )
1356 QString s = _s;
1357 if( s != cap_normal || force )
1359 bool reset_name = force;
1360 for( int i = 0; i < s.length(); ++i )
1361 if( !s[i].isPrint() )
1362 s[i] = QChar( ' ' );
1363 cap_normal = s;
1364 bool was_suffix = ( !cap_suffix.isEmpty() );
1365 QString machine_suffix;
1366 if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false )))
1367 machine_suffix = QString( " <@" ) + wmClientMachine( true ) + '>' + LRM;
1368 QString shortcut_suffix = !shortcut().isEmpty() ? ( " {" + shortcut().toString() + '}' ) : QString();
1369 cap_suffix = machine_suffix + shortcut_suffix;
1370 if(( !isSpecialWindow() || isToolbar() ) && workspace()->findClient( FetchNameInternalPredicate( this )))
1372 int i = 2;
1375 cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + LRM + shortcut_suffix;
1376 i++;
1377 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
1378 info->setVisibleName( caption().toUtf8() );
1379 reset_name = false;
1381 if(( was_suffix && cap_suffix.isEmpty() ) || reset_name )
1382 { // If it was new window, it may have old value still set, if the window is reused
1383 info->setVisibleName( "" );
1384 info->setVisibleIconName( "" );
1386 else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty())
1387 // Keep the same suffix in iconic name if it's set
1388 info->setVisibleIconName( ( cap_iconic + cap_suffix ).toUtf8() );
1390 if( isManaged() && decoration != NULL )
1391 decoration->captionChange();
1395 void Client::updateCaption()
1397 setCaption( cap_normal, true );
1400 void Client::fetchIconicName()
1402 QString s;
1403 if( info->iconName() && info->iconName()[0] != '\0' )
1404 s = QString::fromUtf8( info->iconName() );
1405 else
1406 s = KWindowSystem::readNameProperty( window(), XA_WM_ICON_NAME );
1407 if( s != cap_iconic )
1409 bool was_set = !cap_iconic.isEmpty();
1410 cap_iconic = s;
1411 if( !cap_suffix.isEmpty())
1413 if( !cap_iconic.isEmpty()) // Keep the same suffix in iconic name if it's set
1414 info->setVisibleIconName( ( s + cap_suffix ).toUtf8() );
1415 else if( was_set )
1416 info->setVisibleIconName( "" );
1422 * \reimp
1424 QString Client::caption( bool full ) const
1426 return full ? cap_normal + cap_suffix : cap_normal;
1429 void Client::getWMHints()
1431 XWMHints* hints = XGetWMHints( display(), window() );
1432 input = true;
1433 window_group = None;
1434 urgency = false;
1435 if( hints )
1437 if( hints->flags & InputHint )
1438 input = hints->input;
1439 if( hints->flags & WindowGroupHint )
1440 window_group = hints->window_group;
1441 urgency = !!( hints->flags & UrgencyHint ); // Need boolean, it's a uint bitfield
1442 XFree( (char*)hints );
1444 checkGroup();
1445 updateUrgency();
1446 updateAllowedActions(); // Group affects isMinimizable()
1449 void Client::getMotifHints()
1451 bool mnoborder, mresize, mmove, mminimize, mmaximize, mclose;
1452 Motif::readFlags( client, mnoborder, mresize, mmove, mminimize, mmaximize, mclose );
1453 if( mnoborder )
1455 noborder = true;
1456 app_noborder = true;
1458 if( !hasNETSupport() )
1459 { // NETWM apps should set type and size constraints
1460 motif_may_resize = mresize; // This should be set using minsize==maxsize, but oh well
1461 motif_may_move = mmove;
1463 else
1464 motif_may_resize = motif_may_move = true;
1466 // mminimize; - Ignore, bogus - E.g. shading or sending to another desktop is "minimizing" too
1467 // mmaximize; - Ignore, bogus - Maximizing is basically just resizing
1468 motif_may_close = mclose; // Motif apps like to crash when they set this hint and WM closes them anyway
1469 if( isManaged() )
1470 updateDecoration( true ); // Check if noborder state has changed
1473 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon )
1475 // Get the icons, allow scaling
1476 if( icon != NULL )
1477 *icon = KWindowSystem::icon( win, 32, 32, true, KWindowSystem::NETWM | KWindowSystem::WMHints );
1478 if( miniicon != NULL )
1480 if( icon == NULL || !icon->isNull() )
1481 *miniicon = KWindowSystem::icon( win, 16, 16, true, KWindowSystem::NETWM | KWindowSystem::WMHints );
1482 else
1483 *miniicon = QPixmap();
1487 void Client::getIcons()
1489 // First read icons from the window itself
1490 readIcons( window(), &icon_pix, &miniicon_pix );
1491 if( icon_pix.isNull() )
1492 { // Then try window group
1493 icon_pix = group()->icon();
1494 miniicon_pix = group()->miniIcon();
1496 if( icon_pix.isNull() && isTransient() )
1497 { // Then mainclients
1498 ClientList mainclients = mainClients();
1499 for( ClientList::ConstIterator it = mainclients.constBegin();
1500 it != mainclients.constEnd() && icon_pix.isNull();
1501 ++it )
1503 icon_pix = (*it)->icon();
1504 miniicon_pix = (*it)->miniIcon();
1507 if( icon_pix.isNull())
1508 { // And if nothing else, load icon from classhint or xapp icon
1509 icon_pix = KWindowSystem::icon( window(), 32, 32, true, KWindowSystem::ClassHint | KWindowSystem::XApp );
1510 miniicon_pix = KWindowSystem::icon( window(), 16, 16, true, KWindowSystem::ClassHint | KWindowSystem::XApp );
1512 if( isManaged() && decoration != NULL )
1513 decoration->iconChange();
1516 void Client::getWindowProtocols()
1518 Atom* p;
1519 int i,n;
1521 Pdeletewindow = 0;
1522 Ptakefocus = 0;
1523 Ptakeactivity = 0;
1524 Pcontexthelp = 0;
1525 Pping = 0;
1527 if( XGetWMProtocols( display(), window(), &p, &n ))
1529 for( i = 0; i < n; i++ )
1531 if( p[i] == atoms->wm_delete_window )
1532 Pdeletewindow = 1;
1533 else if( p[i] == atoms->wm_take_focus )
1534 Ptakefocus = 1;
1535 else if( p[i] == atoms->net_wm_take_activity )
1536 Ptakeactivity = 1;
1537 else if( p[i] == atoms->net_wm_context_help )
1538 Pcontexthelp = 1;
1539 else if( p[i] == atoms->net_wm_ping )
1540 Pping = 1;
1542 if( n > 0 )
1543 XFree( p );
1547 void Client::getSyncCounter()
1549 #ifdef HAVE_XSYNC
1550 if( !Extensions::syncAvailable() )
1551 return;
1553 Atom retType;
1554 unsigned long nItemRet;
1555 unsigned long byteRet;
1556 int formatRet;
1557 unsigned char* propRet;
1558 int ret = XGetWindowProperty( display(), window(), atoms->net_wm_sync_request_counter,
1559 0, 1, false, XA_CARDINAL, &retType, &formatRet, &nItemRet, &byteRet, &propRet );
1561 if( ret == Success && formatRet == 32 )
1563 sync_counter = *(long*)( propRet );
1564 XSyncIntToValue( &sync_counter_value, 0 );
1565 XSyncValue zero;
1566 XSyncIntToValue( &zero, 0 );
1567 XSyncSetCounter( display(), sync_counter, zero );
1568 if( sync_alarm == None )
1570 XSyncAlarmAttributes attrs;
1571 attrs.trigger.counter = sync_counter;
1572 attrs.trigger.value_type = XSyncRelative;
1573 attrs.trigger.test_type = XSyncPositiveTransition;
1574 XSyncIntToValue( &attrs.trigger.wait_value, 1 );
1575 XSyncIntToValue( &attrs.delta, 1 );
1576 sync_alarm = XSyncCreateAlarm( display(),
1577 XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCADelta | XSyncCAValue,
1578 &attrs );
1581 #endif
1585 * Send the client a _NET_SYNC_REQUEST
1587 void Client::sendSyncRequest()
1589 #ifdef HAVE_XSYNC
1590 if( sync_counter == None )
1591 return;
1593 // We increment before the notify so that after the notify
1594 // syncCounterSerial will equal the value we are expecting
1595 // in the acknowledgement
1596 int overflow;
1597 XSyncValue one;
1598 XSyncIntToValue( &one, 1 );
1599 #undef XSyncValueAdd // It causes a warning :-/
1600 XSyncValueAdd( &sync_counter_value, sync_counter_value, one, &overflow );
1602 // Send the message to client
1603 XEvent ev;
1604 ev.xclient.type = ClientMessage;
1605 ev.xclient.window = window();
1606 ev.xclient.format = 32;
1607 ev.xclient.message_type = atoms->wm_protocols;
1608 ev.xclient.data.l[0] = atoms->net_wm_sync_request;
1609 ev.xclient.data.l[1] = xTime();
1610 ev.xclient.data.l[2] = XSyncValueLow32( sync_counter_value );
1611 ev.xclient.data.l[3] = XSyncValueHigh32( sync_counter_value );
1612 ev.xclient.data.l[4] = 0;
1613 XSendEvent( display(), window(), False, NoEventMask, &ev );
1614 XSync( display(), false );
1615 #endif
1618 bool Client::wantsTabFocus() const
1620 return ( isNormalWindow() || isDialog() ) && wantsInput();
1623 bool Client::wantsInput() const
1625 return rules()->checkAcceptFocus( input || Ptakefocus );
1628 bool Client::isSpecialWindow() const
1629 { // TODO
1630 return isDesktop() || isDock() || isSplash() || isTopMenu() || isToolbar();
1634 * Sets an appropriate cursor shape for the logical mouse position \a m
1636 void Client::updateCursor()
1638 Position m = mode;
1639 if( !isResizable() || isShade() )
1640 m = PositionCenter;
1641 QCursor c;
1642 switch( m )
1644 case PositionTopLeft:
1645 case PositionBottomRight:
1646 c = Qt::SizeFDiagCursor;
1647 break;
1648 case PositionBottomLeft:
1649 case PositionTopRight:
1650 c = Qt::SizeBDiagCursor;
1651 break;
1652 case PositionTop:
1653 case PositionBottom:
1654 c = Qt::SizeVerCursor;
1655 break;
1656 case PositionLeft:
1657 case PositionRight:
1658 c = Qt::SizeHorCursor;
1659 break;
1660 default:
1661 if( moveResizeMode )
1662 c = Qt::SizeAllCursor;
1663 else
1664 c = Qt::ArrowCursor;
1665 break;
1667 if( c.handle() == cursor.handle())
1668 return;
1669 cursor = c;
1670 if( decoration != NULL )
1671 decoration->widget()->setCursor( cursor );
1672 XDefineCursor( display(), frameId(), cursor.handle() );
1673 if( moveResizeMode ) // XDefineCursor doesn't change cursor if there's pointer grab active
1674 XChangeActivePointerGrab( display(),
1675 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
1676 cursor.handle(), xTime());
1679 Client::Position Client::mousePosition( const QPoint& p ) const
1681 if( decoration != NULL )
1682 return decoration->mousePosition( p );
1683 return PositionCenter;
1686 void Client::updateAllowedActions( bool force )
1688 if( !isManaged() && !force )
1689 return;
1690 unsigned long old_allowed_actions = allowed_actions;
1691 allowed_actions = 0;
1692 if( isMovable() )
1693 allowed_actions |= NET::ActionMove;
1694 if( isResizable() )
1695 allowed_actions |= NET::ActionResize;
1696 if( isMinimizable() )
1697 allowed_actions |= NET::ActionMinimize;
1698 if( isShadeable() )
1699 allowed_actions |= NET::ActionShade;
1700 // Sticky state not supported
1701 if( isMaximizable() )
1702 allowed_actions |= NET::ActionMax;
1703 if( userCanSetFullScreen() )
1704 allowed_actions |= NET::ActionFullScreen;
1705 allowed_actions |= NET::ActionChangeDesktop; // Always (Pagers shouldn't show Docks etc.)
1706 if( isCloseable() )
1707 allowed_actions |= NET::ActionClose;
1708 if( old_allowed_actions == allowed_actions )
1709 return;
1710 // TODO: This could be delayed and compressed - It's only for pagers etc. anyway
1711 info->setAllowedActions( allowed_actions );
1712 // TODO: This should also tell the decoration, so that it can update the buttons
1715 void Client::autoRaise()
1717 workspace()->raiseClient( this );
1718 cancelAutoRaise();
1721 void Client::cancelAutoRaise()
1723 delete autoRaiseTimer;
1724 autoRaiseTimer = 0;
1727 void Client::debug( kdbgstream& stream ) const
1729 stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":"
1730 << resourceName() << ";Caption:" << caption() << "\'";
1733 QPixmap* kwin_get_menu_pix_hack()
1735 static QPixmap p;
1736 if( p.isNull() )
1737 p = SmallIcon( "bx2" );
1738 return &p;
1741 } // namespace
1743 #include "client.moc"