• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • twin
 

twin

  • twin
client.cpp
1/*****************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
8You can Freely distribute this program under the GNU General Public
9License. See the file "COPYING" for the exact licensing terms.
10******************************************************************/
11
12#include "client.h"
13
14#include <math.h>
15
16#include <tqapplication.h>
17#include <tqpainter.h>
18#include <tqdatetime.h>
19#include <tqimage.h>
20#include <tqfile.h>
21#include <tdeprocess.h>
22#include <unistd.h>
23#include <tdestandarddirs.h>
24#include <tqwhatsthis.h>
25#include <twin.h>
26#include <kiconloader.h>
27#include <tdelocale.h>
28#include <stdlib.h>
29
30#ifdef Q_OS_SOLARIS
31#include <procfs.h>
32#include <libgen.h>
33#endif /* SunOS */
34
35#include "bridge.h"
36#include "group.h"
37#include "workspace.h"
38#include "atoms.h"
39#include "notifications.h"
40#include "rules.h"
41
42#include <X11/extensions/shape.h>
43
44// put all externs before the namespace statement to allow the linker
45// to resolve them properly
46
47extern Atom tqt_wm_state;
48extern Atom tqt_window_role;
49extern Atom tqt_sm_client_id;
50
51// wait 200 ms before drawing shadow after move/resize
52static const int SHADOW_DELAY = 200;
53
54namespace KWinInternal
55{
56
57/* TODO: Remove this once X has real translucency.
58 *
59 * A list of the regions covered by all shadows and the Clients to which they
60 * belong. Used to redraw shadows when a window overlapping or underlying a
61 * shadow is moved, resized, or hidden.
62 */
63struct ShadowRegion
64 {
65 TQRegion region;
66 Client *client;
67 };
68static TQValueList<ShadowRegion> shadowRegions;
69
70/*
71
72 Creating a client:
73 - only by calling Workspace::createClient()
74 - it creates a new client and calls manage() for it
75
76 Destroying a client:
77 - destroyClient() - only when the window itself has been destroyed
78 - releaseWindow() - the window is kept, only the client itself is destroyed
79
80*/
81
82
94Client::Client( Workspace *ws )
95 : TQObject( NULL ),
96 client( None ),
97 wrapper( None ),
98 frame( None ),
99 decoration( NULL ),
100 wspace( ws ),
101 bridge( new Bridge( this )),
102 inhibitConfigureRequests(false),
103 move_faked_activity( false ),
104 move_resize_grab_window( None ),
105 transient_for( NULL ),
106 transient_for_id( None ),
107 original_transient_for_id( None ),
108 in_group( NULL ),
109 window_group( None ),
110 in_layer( UnknownLayer ),
111 ping_timer( NULL ),
112 process_killer( NULL ),
113 process_resumer( NULL ),
114 user_time( CurrentTime ), // not known yet
115 allowed_actions( 0 ),
116 postpone_geometry_updates( 0 ),
117 pending_geometry_update( false ),
118 shade_geometry_change( false ),
119 border_left( 0 ),
120 border_right( 0 ),
121 border_top( 0 ),
122 border_bottom( 0 ),
123 opacity_( 0 ),
124 demandAttentionKNotifyTimer( NULL ),
125 activeMaximizing(false),
126 activeTiled(false)
127// SELI do all as initialization
128 {
129 autoRaiseTimer = 0;
130 shadeHoverTimer = 0;
131
132 configureRequestTimer = new TQTimer(this);
133 connect(configureRequestTimer, TQ_SIGNAL(timeout()), TQ_SLOT(configureRequestTimeout()));
134
135 shadowDelayTimer = new TQTimer(this);
136 opacityCache = &activeOpacityCache;
137 shadowAfterClient = NULL;
138 shadowWidget = NULL;
139 shadowMe = true;
140 connect(shadowDelayTimer, TQ_SIGNAL(timeout()), TQ_SLOT(drawShadow()));
141
142 // set the initial mapping state
143 mapping_state = WithdrawnState;
144 desk = 0; // no desktop yet
145
146 mode = PositionCenter;
147 buttonDown = FALSE;
148 moveResizeMode = FALSE;
149
150 info = NULL;
151
152 shade_mode = ShadeNone;
153 active = FALSE;
154 deleting = false;
155 keep_above = FALSE;
156 keep_below = FALSE;
157 is_shape = FALSE;
158 motif_noborder = false;
159 motif_may_move = TRUE;
160 motif_may_resize = TRUE;
161 motif_may_close = TRUE;
162 fullscreen_mode = FullScreenNone;
163 skip_taskbar = FALSE;
164 original_skip_taskbar = false;
165 minimized = false;
166 hidden = false;
167 modal = false;
168 noborder = false;
169 user_noborder = false;
170 urgency = false;
171 ignore_focus_stealing = false;
172 demands_attention = false;
173 check_active_modal = false;
174
175 Pdeletewindow = 0;
176 Ptakefocus = 0;
177 Ptakeactivity = 0;
178 Pcontexthelp = 0;
179 Pping = 0;
180 input = FALSE;
181 skip_pager = FALSE;
182
183 max_mode = MaximizeRestore;
184 maxmode_restore = MaximizeRestore;
185
186 cmap = None;
187
188 frame_geometry = TQRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
189 client_size = TQSize( 100, 100 );
190 custom_opacity = false;
191 rule_opacity_active = 0; //translucency rules
192 rule_opacity_inactive = 0; //dito.
193
194 // SELI initialize xsizehints??
195 }
196
200Client::~Client()
201 {
202 assert(!moveResizeMode);
203 assert( client == None );
204 assert( frame == None && wrapper == None );
205 assert( decoration == NULL );
206 assert( postpone_geometry_updates == 0 );
207 assert( !check_active_modal );
208 delete info;
209 delete bridge;
210 }
211
212// use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
213void Client::deleteClient( Client* c, allowed_t )
214 {
215 delete c;
216 }
217
221void Client::releaseWindow( bool on_shutdown )
222 {
223 assert( !deleting );
224 deleting = true;
225 workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules
226 StackingUpdatesBlocker blocker( workspace());
227 if (!custom_opacity) setOpacity(FALSE);
228 if (moveResizeMode)
229 leaveMoveResize();
230 removeShadow();
231 drawIntersectingShadows();
232 finishWindowRules();
233 ++postpone_geometry_updates;
234 // grab X during the release to make removing of properties, setting to withdrawn state
235 // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2)
236 grabXServer();
237 setMappingState( WithdrawnState );
238 setModal( false ); // otherwise its mainwindow wouldn't get focus
239 hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags)
240 if( !on_shutdown )
241 workspace()->clientHidden( this );
242 XUnmapWindow( tqt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect
243 destroyDecoration();
244 cleanGrouping();
245 if( !on_shutdown )
246 {
247 workspace()->removeClient( this, Allowed );
248 // only when the window is being unmapped, not when closing down KWin
249 // (NETWM sections 5.5,5.7)
250 info->setDesktop( 0 );
251 desk = 0;
252 info->setState( 0, info->state()); // reset all state flags
253 }
254 XDeleteProperty( tqt_xdisplay(), client, atoms->kde_net_wm_user_creation_time);
255 XDeleteProperty( tqt_xdisplay(), client, atoms->net_frame_extents );
256 XDeleteProperty( tqt_xdisplay(), client, atoms->kde_net_wm_frame_strut );
257 XReparentWindow( tqt_xdisplay(), client, workspace()->rootWin(), x(), y());
258 XRemoveFromSaveSet( tqt_xdisplay(), client );
259 XSelectInput( tqt_xdisplay(), client, NoEventMask );
260 if( on_shutdown )
261 { // map the window, so it can be found after another WM is started
262 XMapWindow( tqt_xdisplay(), client );
263 // TODO preserve minimized, shaded etc. state?
264 }
265 else
266 {
267 // Make sure it's not mapped if the app unmapped it (#65279). The app
268 // may do map+unmap before we initially map the window by calling rawShow() from manage().
269 XUnmapWindow( tqt_xdisplay(), client );
270 }
271 client = None;
272 XDestroyWindow( tqt_xdisplay(), wrapper );
273 wrapper = None;
274 XDestroyWindow( tqt_xdisplay(), frame );
275 frame = None;
276 --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry
277 checkNonExistentClients();
278 deleteClient( this, Allowed );
279 ungrabXServer();
280 }
281
282// like releaseWindow(), but this one is called when the window has been already destroyed
283// (e.g. the application closed it)
284void Client::destroyClient()
285 {
286 assert( !deleting );
287 deleting = true;
288 workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules
289 StackingUpdatesBlocker blocker( workspace());
290 if (moveResizeMode)
291 leaveMoveResize();
292 removeShadow();
293 drawIntersectingShadows();
294 finishWindowRules();
295 ++postpone_geometry_updates;
296 setModal( false );
297 hidden = true; // so that it's not considered visible anymore
298 workspace()->clientHidden( this );
299 destroyDecoration();
300 cleanGrouping();
301 workspace()->removeClient( this, Allowed );
302 client = None; // invalidate
303 XDestroyWindow( tqt_xdisplay(), wrapper );
304 wrapper = None;
305 XDestroyWindow( tqt_xdisplay(), frame );
306 frame = None;
307 --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry
308 checkNonExistentClients();
309 deleteClient( this, Allowed );
310 }
311
312void Client::updateDecoration( bool check_workspace_pos, bool force )
313 {
314 if( !force && (( decoration == NULL && noBorder())
315 || ( decoration != NULL && !noBorder())))
316 return;
317 bool do_show = false;
318 postponeGeometryUpdates( true );
319 if( force )
320 destroyDecoration();
321 if( !noBorder())
322 {
323 setMask( TQRegion()); // reset shape mask
324 decoration = workspace()->createDecoration( bridge );
325 // TODO check decoration's minimum size?
326 decoration->init();
327 decoration->widget()->installEventFilter( this );
328 XReparentWindow( tqt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 );
329 decoration->widget()->lower();
330 decoration->borders( border_left, border_right, border_top, border_bottom );
331 options->onlyDecoTranslucent ?
332 setDecoHashProperty(border_top, border_right, border_bottom, border_left):
333 unsetDecoHashProperty();
334 int save_workarea_diff_x = workarea_diff_x;
335 int save_workarea_diff_y = workarea_diff_y;
336 move( calculateGravitation( false ));
337 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
338 workarea_diff_x = save_workarea_diff_x;
339 workarea_diff_y = save_workarea_diff_y;
340 do_show = true;
341 }
342 else
343 destroyDecoration();
344 if( check_workspace_pos )
345 checkWorkspacePosition();
346 postponeGeometryUpdates( false );
347 if( do_show )
348 decoration->widget()->show();
349 updateFrameExtents();
350 updateOpacityCache();
351 }
352
353void Client::destroyDecoration()
354 {
355 if( decoration != NULL )
356 {
357 delete decoration;
358 decoration = NULL;
359 TQPoint grav = calculateGravitation( true );
360 border_left = border_right = border_top = border_bottom = 0;
361 setMask( TQRegion()); // reset shape mask
362 int save_workarea_diff_x = workarea_diff_x;
363 int save_workarea_diff_y = workarea_diff_y;
364 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
365 move( grav );
366 workarea_diff_x = save_workarea_diff_x;
367 workarea_diff_y = save_workarea_diff_y;
368 }
369 }
370
371void Client::checkBorderSizes()
372 {
373 if( decoration == NULL )
374 return;
375 int new_left, new_right, new_top, new_bottom;
376 decoration->borders( new_left, new_right, new_top, new_bottom );
377 if( new_left == border_left && new_right == border_right
378 && new_top == border_top && new_bottom == border_bottom )
379 return;
380 GeometryUpdatesPostponer blocker( this );
381 move( calculateGravitation( true ));
382 border_left = new_left;
383 border_right = new_right;
384 border_top = new_top;
385 border_bottom = new_bottom;
386 if (border_left != new_left ||
387 border_right != new_right ||
388 border_top != new_top ||
389 border_bottom != new_bottom)
390 options->onlyDecoTranslucent ?
391 setDecoHashProperty(new_top, new_right, new_bottom, new_left):
392 unsetDecoHashProperty();
393 move( calculateGravitation( false ));
394 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
395 checkWorkspacePosition();
396 }
397
398void Client::detectNoBorder()
399 {
400 if( Shape::hasShape( window()))
401 {
402 noborder = true;
403 return;
404 }
405 switch( windowType())
406 {
407 case NET::Desktop :
408 case NET::Dock :
409 case NET::TopMenu :
410 case NET::Splash :
411 noborder = true;
412 break;
413 case NET::Unknown :
414 case NET::Normal :
415 case NET::Toolbar :
416 case NET::Menu :
417 case NET::Dialog :
418 case NET::Utility :
419 noborder = false;
420 break;
421 default:
422 assert( false );
423 }
424 // NET::Override is some strange beast without clear definition, usually
425 // just meaning "noborder", so let's treat it only as such flag, and ignore it as
426 // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it)
427 if( info->windowType( SUPPORTED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override )
428 noborder = true;
429 }
430
431void Client::detectShapable()
432 {
433 if( Shape::hasShape( window()))
434 return;
435 switch( windowType())
436 {
437 case NET::Desktop :
438 case NET::Dock :
439 case NET::TopMenu :
440 case NET::Splash :
441 break;
442 case NET::Unknown :
443 case NET::Normal :
444 case NET::Toolbar :
445 case NET::Menu :
446 case NET::Dialog :
447 case NET::Utility :
448 setShapable(FALSE);
449 break;
450 default:
451 assert( false );
452 }
453 }
454
455void Client::updateFrameExtents()
456 {
457 NETStrut strut;
458 strut.left = border_left;
459 strut.right = border_right;
460 strut.top = border_top;
461 strut.bottom = border_bottom;
462 info->setFrameExtents( strut );
463 }
464
465// Resizes the decoration, and makes sure the decoration widget gets resize event
466// even if the size hasn't changed. This is needed to make sure the decoration
467// re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes,
468// the decoration may turn on/off some borders, but the actual size
469// of the decoration stays the same).
470void Client::resizeDecoration( const TQSize& s )
471 {
472 if( decoration == NULL )
473 return;
474 TQSize oldsize = decoration->widget()->size();
475 decoration->resize( s );
476 if( oldsize == s )
477 {
478 TQResizeEvent e( s, oldsize );
479 TQApplication::sendEvent( decoration->widget(), &e );
480 }
481 if (!moveResizeMode && options->shadowEnabled(isActive()))
482 {
483 // If the user is manually resizing, let Client::leaveMoveResize()
484 // decide when to redraw the shadow
485 updateOpacityCache();
486 }
487 }
488
489bool Client::noBorder() const
490 {
491 return noborder || isFullScreen() || user_noborder || motif_noborder;
492 }
493
494bool Client::userCanSetNoBorder() const
495 {
496 return !noborder && !isFullScreen() && !isShade();
497 }
498
499bool Client::isUserNoBorder() const
500 {
501 return user_noborder;
502 }
503
504void Client::setUserNoBorder( bool set )
505 {
506 if( !userCanSetNoBorder())
507 return;
508 set = rules()->checkNoBorder( set );
509 if( user_noborder == set )
510 return;
511 user_noborder = set;
512 updateDecoration( true, false );
513 updateWindowRules();
514 }
515
516bool Client::isModalSystemNotification() const
517 {
518 unsigned char *data = 0;
519 Atom actual;
520 int format, result;
521 unsigned long n, left;
522 result = XGetWindowProperty(tqt_xdisplay(), window(), atoms->net_wm_system_modal_notification, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data);
523 if (result == Success && data && format == 32 )
524 {
525 return TRUE;
526 }
527 return FALSE;
528 }
529
530void Client::updateShape()
531 {
532 // workaround for #19644 - shaped windows shouldn't have decoration
533 if( shape() && !noBorder())
534 {
535 noborder = true;
536 updateDecoration( true );
537 }
538 updateOpacityCache();
539 if ( shape() )
540 {
541 XShapeCombineShape(tqt_xdisplay(), frameId(), ShapeBounding,
542 clientPos().x(), clientPos().y(),
543 window(), ShapeBounding, ShapeSet);
544 setShapable(TRUE);
545 }
546 // !shape() mask setting is done in setMask() when the decoration
547 // calls it or when the decoration is created/destroyed
548
549 if( Shape::version() >= 0x11 ) // 1.1, has input shape support
550 { // There appears to be no way to find out if a window has input
551 // shape set or not, so always propagate the input shape
552 // (it's the same like the bounding shape by default).
553 // Also, build the shape using a helper window, not directly
554 // in the frame window, because the sequence set-shape-to-frame,
555 // remove-shape-of-client, add-input-shape-of-client has the problem
556 // that after the second step there's a hole in the input shape
557 // until the real shape of the client is added and that can make
558 // the window lose focus (which is a problem with mouse focus policies)
559 static Window helper_window = None;
560 if( helper_window == None )
561 helper_window = XCreateSimpleWindow( tqt_xdisplay(), tqt_xrootwin(),
562 0, 0, 1, 1, 0, 0, 0 );
563 XResizeWindow( tqt_xdisplay(), helper_window, width(), height());
564 XShapeCombineShape( tqt_xdisplay(), helper_window, ShapeInput, 0, 0,
565 frameId(), ShapeBounding, ShapeSet );
566 XShapeCombineShape( tqt_xdisplay(), helper_window, ShapeInput,
567 clientPos().x(), clientPos().y(),
568 window(), ShapeBounding, ShapeSubtract );
569 XShapeCombineShape( tqt_xdisplay(), helper_window, ShapeInput,
570 clientPos().x(), clientPos().y(),
571 window(), ShapeInput, ShapeUnion );
572 XShapeCombineShape( tqt_xdisplay(), frameId(), ShapeInput, 0, 0,
573 helper_window, ShapeInput, ShapeSet );
574 }
575 }
576
577void Client::setMask( const TQRegion& reg, int mode )
578 {
579 _mask = reg;
580 if( reg.isNull())
581 XShapeCombineMask( tqt_xdisplay(), frameId(), ShapeBounding, 0, 0,
582 None, ShapeSet );
583 else if( mode == X::Unsorted )
584 XShapeCombineRegion( tqt_xdisplay(), frameId(), ShapeBounding, 0, 0,
585 reg.handle(), ShapeSet );
586 else
587 {
588 TQMemArray< TQRect > rects = reg.rects();
589 XRectangle* xrects = new XRectangle[ rects.count() ];
590 for( unsigned int i = 0;
591 i < rects.count();
592 ++i )
593 {
594 xrects[ i ].x = rects[ i ].x();
595 xrects[ i ].y = rects[ i ].y();
596 xrects[ i ].width = rects[ i ].width();
597 xrects[ i ].height = rects[ i ].height();
598 }
599 XShapeCombineRectangles( tqt_xdisplay(), frameId(), ShapeBounding, 0, 0,
600 xrects, rects.count(), ShapeSet, mode );
601 delete[] xrects;
602 }
603 updateShape();
604 }
605
606TQRegion Client::mask() const
607 {
608 if( _mask.isEmpty())
609 return TQRegion( 0, 0, width(), height());
610 return _mask;
611 }
612
613void Client::setShapable(bool b)
614 {
615 long tmp = b?1:0;
616 XChangeProperty(tqt_xdisplay(), frameId(), atoms->net_wm_window_shapable, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &tmp, 1L);
617 }
618
619void Client::hideClient( bool hide )
620 {
621 if( hidden == hide )
622 return;
623 hidden = hide;
624 updateVisibility();
625 }
626
630bool Client::isMinimizable() const
631 {
632 if( isSpecialWindow())
633 return false;
634 if( isModalSystemNotification())
635 return false;
636 if( isTransient())
637 { // #66868 - let other xmms windows be minimized when the mainwindow is minimized
638 bool shown_mainwindow = false;
639 ClientList mainclients = mainClients();
640 for( ClientList::ConstIterator it = mainclients.begin();
641 it != mainclients.end();
642 ++it )
643 {
644 if( (*it)->isShown( true ))
645 shown_mainwindow = true;
646 }
647 if( !shown_mainwindow )
648 return true;
649 }
650 // this is here because kicker's taskbar doesn't provide separate entries
651 // for windows with an explicitly given parent
652 // TODO perhaps this should be redone
653 if( transientFor() != NULL )
654 return false;
655 if( !wantsTabFocus()) // SELI - NET::Utility? why wantsTabFocus() - skiptaskbar? ?
656 return false;
657 return true;
658 }
659
663bool Client::keepAbove() const
664 {
665 if( isModalSystemNotification())
666 return true;
667 return keep_above;
668 }
669
673void Client::minimize( bool avoid_animation )
674 {
675 if ( !isMinimizable() || isMinimized())
676 return;
677
678 if (isShade()) // NETWM restriction - KWindowInfo::isMinimized() == Hidden && !Shaded
679 info->setState(0, NET::Shaded);
680
681 Notify::raise( Notify::Minimize );
682
683 // SELI mainClients().isEmpty() ??? - and in unminimize() too
684 if ( mainClients().isEmpty() && isOnCurrentDesktop() && isShown( true ) && !avoid_animation )
685 animateMinimizeOrUnminimize( true ); // was visible or shaded
686
687 minimized = true;
688
689 updateVisibility();
690 updateAllowedActions();
691 workspace()->updateMinimizedOfTransients( this );
692 updateWindowRules();
693 workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast );
694 }
695
696void Client::unminimize( bool avoid_animation )
697 {
698 if (!queryUserSuspendedResume())
699 return;
700
701 if( !isMinimized())
702 return;
703
704 if (isShade()) // NETWM restriction - KWindowInfo::isMinimized() == Hidden && !Shaded
705 info->setState(NET::Shaded, NET::Shaded);
706
707 Notify::raise( Notify::UnMinimize );
708 minimized = false;
709 if( isOnCurrentDesktop() && isShown( true ))
710 {
711 if( mainClients().isEmpty() && !avoid_animation )
712 animateMinimizeOrUnminimize( FALSE );
713 }
714 updateVisibility();
715 updateAllowedActions();
716 workspace()->updateMinimizedOfTransients( this );
717 updateWindowRules();
718 }
719
720extern bool blockAnimation;
721
722void Client::animateMinimizeOrUnminimize( bool minimize )
723 {
724 if ( blockAnimation )
725 return;
726 if ( !options->animateMinimize )
727 return;
728
729 if( decoration != NULL && decoration->animateMinimize( minimize ))
730 return; // decoration did it
731
732 // the function is a bit tricky since it will ensure that an
733 // animation action needs always the same time regardless of the
734 // performance of the machine or the X-Server.
735
736 float lf,rf,tf,bf,step;
737
738 int speed = options->animateMinimizeSpeed;
739 if ( speed > 10 )
740 speed = 10;
741 if ( speed < 0 )
742 speed = 0;
743
744 step = 40. * (11 - speed );
745
746 NETRect r = info->iconGeometry();
747 TQRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height );
748 if ( !icongeom.isValid() )
749 return;
750
751 TQPixmap pm = animationPixmap( minimize ? width() : icongeom.width() );
752
753 TQRect before, after;
754 if ( minimize )
755 {
756 before = TQRect( x(), y(), width(), pm.height() );
757 after = TQRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
758 }
759 else
760 {
761 before = TQRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
762 after = TQRect( x(), y(), width(), pm.height() );
763 }
764
765 lf = (after.left() - before.left())/step;
766 rf = (after.right() - before.right())/step;
767 tf = (after.top() - before.top())/step;
768 bf = (after.bottom() - before.bottom())/step;
769
770 grabXServer();
771
772 TQRect area = before;
773 TQRect area2;
774 TQPixmap pm2;
775
776 TQTime t;
777 t.start();
778 float diff;
779
780 TQPainter p ( workspace()->desktopWidget() );
781 bool need_to_clear = FALSE;
782 TQPixmap pm3;
783 do
784 {
785 if (area2 != area)
786 {
787 pm = animationPixmap( area.width() );
788 pm2 = TQPixmap::grabWindow( tqt_xrootwin(), area.x(), area.y(), area.width(), area.height() );
789 p.drawPixmap( area.x(), area.y(), pm );
790 if ( need_to_clear )
791 {
792 p.drawPixmap( area2.x(), area2.y(), pm3 );
793 need_to_clear = FALSE;
794 }
795 area2 = area;
796 }
797 XFlush(tqt_xdisplay());
798 XSync( tqt_xdisplay(), FALSE );
799 diff = t.elapsed();
800 if (diff > step)
801 diff = step;
802 area.setLeft(before.left() + int(diff*lf));
803 area.setRight(before.right() + int(diff*rf));
804 area.setTop(before.top() + int(diff*tf));
805 area.setBottom(before.bottom() + int(diff*bf));
806 if (area2 != area )
807 {
808 if ( area2.intersects( area ) )
809 p.drawPixmap( area2.x(), area2.y(), pm2 );
810 else
811 { // no overlap, we can clear later to avoid flicker
812 pm3 = pm2;
813 need_to_clear = TRUE;
814 }
815 }
816 } while ( t.elapsed() < step);
817 if (area2 == area || need_to_clear )
818 p.drawPixmap( area2.x(), area2.y(), pm2 );
819
820 p.end();
821 ungrabXServer();
822 }
823
824
828TQPixmap Client::animationPixmap( int w )
829 {
830 TQFont font = options->font(isActive());
831 TQFontMetrics fm( font );
832 TQPixmap pm( w, fm.lineSpacing() );
833 pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) );
834 TQPainter p( &pm );
835 p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() ));
836 p.setFont(options->font(isActive()));
837 p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() );
838 return pm;
839 }
840
841
842bool Client::isShadeable() const
843 {
844 return !isSpecialWindow() && !noBorder();
845 }
846
847void Client::setShade( ShadeMode mode )
848 {
849 if( !isShadeable())
850 return;
851 if( isModalSystemNotification())
852 return;
853 mode = rules()->checkShade( mode );
854 if( shade_mode == mode )
855 return;
856 bool was_shade = isShade();
857 ShadeMode was_shade_mode = shade_mode;
858 shade_mode = mode;
859 if( was_shade == isShade())
860 {
861 if( decoration != NULL ) // decoration may want to update after e.g. hover-shade changes
862 decoration->shadeChange();
863 return; // no real change in shaded state
864 }
865
866 if( shade_mode == ShadeNormal )
867 {
868 if ( isShown( true ) && isOnCurrentDesktop())
869 Notify::raise( Notify::ShadeUp );
870 }
871 else if( shade_mode == ShadeNone )
872 {
873 if( isShown( true ) && isOnCurrentDesktop())
874 Notify::raise( Notify::ShadeDown );
875 }
876
877 assert( decoration != NULL ); // noborder windows can't be shaded
878 GeometryUpdatesPostponer blocker( this );
879 // decorations may turn off some borders when shaded
880 decoration->borders( border_left, border_right, border_top, border_bottom );
881
882 int as = options->animateShade? 10 : 1;
883// TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere
884 if ( isShade())
885 { // shade_mode == ShadeNormal
886 // we're about to shade, texx xcompmgr to prepare
887 long _shade = 1;
888 XChangeProperty(tqt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L);
889 // shade
890 int h = height();
891 shade_geometry_change = true;
892 TQSize s( sizeForClientSize( TQSize( clientSize())));
893 s.setHeight( border_top + border_bottom );
894 XSelectInput( tqt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
895 XUnmapWindow( tqt_xdisplay(), wrapper );
896 XUnmapWindow( tqt_xdisplay(), client );
897 XSelectInput( tqt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
898 //as we hid the unmap event, xcompmgr didn't recognize the client wid has vanished, so we'll extra inform it
899 //done xcompmgr workaround
900// FRAME repaint( FALSE );
901// bool wasStaticContents = testWFlags( WStaticContents );
902// setWFlags( WStaticContents );
903 int step = TQMAX( 4, TQABS( h - s.height() ) / as )+1;
904 do
905 {
906 h -= step;
907 XResizeWindow( tqt_xdisplay(), frameId(), s.width(), h );
908 resizeDecoration( TQSize( s.width(), h ));
909 TQApplication::syncX();
910 } while ( h > s.height() + step );
911// if ( !wasStaticContents )
912// clearWFlags( WStaticContents );
913 plainResize( s );
914 shade_geometry_change = false;
915 if( isActive())
916 {
917 if( was_shade_mode == ShadeHover )
918 workspace()->activateNextClient( this );
919 else
920 workspace()->focusToNull();
921 }
922 // tell xcompmgr shade's done
923 _shade = 2;
924 XChangeProperty(tqt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L);
925 }
926 else
927 {
928 int h = height();
929 shade_geometry_change = true;
930 TQSize s( sizeForClientSize( clientSize()));
931// FRAME bool wasStaticContents = testWFlags( WStaticContents );
932// setWFlags( WStaticContents );
933 int step = TQMAX( 4, TQABS( h - s.height() ) / as )+1;
934 do
935 {
936 h += step;
937 XResizeWindow( tqt_xdisplay(), frameId(), s.width(), h );
938 resizeDecoration( TQSize( s.width(), h ));
939 // assume a border
940 // we do not have time to wait for X to send us paint events
941// FRAME repaint( 0, h - step-5, width(), step+5, TRUE);
942 TQApplication::syncX();
943 } while ( h < s.height() - step );
944// if ( !wasStaticContents )
945// clearWFlags( WStaticContents );
946 shade_geometry_change = false;
947 plainResize( s );
948 if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
949 setActive( TRUE );
950 XMapWindow( tqt_xdisplay(), wrapperId());
951 XMapWindow( tqt_xdisplay(), window());
952 XDeleteProperty (tqt_xdisplay(), client, atoms->net_wm_window_shade);
953 if (options->shadowEnabled(false))
954 {
955 for (ClientList::ConstIterator it = transients().begin();
956 it != transients().end(); ++it)
957 {
958 (*it)->removeShadow();
959 (*it)->drawDelayedShadow();
960 }
961 }
962
963 if ( isActive() )
964 workspace()->requestFocus( this );
965 }
966 checkMaximizeGeometry();
967 info->setState( (isShade() && !isMinimized()) ? NET::Shaded : 0, NET::Shaded );
968 info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
969 updateVisibility();
970 updateAllowedActions();
971 workspace()->updateMinimizedOfTransients( this );
972 decoration->shadeChange();
973 updateWindowRules();
974 }
975
976void Client::configureRequestTimeout()
977 {
978 inhibitConfigureRequests = false;
979 sendSyntheticConfigureNotify();
980 }
981
982void Client::shadeHover()
983 {
984 setShade( ShadeHover );
985 cancelShadeHover();
986 }
987
988void Client::cancelShadeHover()
989 {
990 delete shadeHoverTimer;
991 shadeHoverTimer = 0;
992 }
993
994void Client::toggleShade()
995 {
996 // if the mode is ShadeHover or ShadeActive, cancel shade too
997 setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
998 }
999
1000void Client::updateVisibility()
1001 {
1002 if( deleting )
1003 return;
1004 bool show = true;
1005 if( hidden )
1006 {
1007 setMappingState( IconicState );
1008 info->setState( NET::Hidden, NET::Hidden );
1009 setSkipTaskbar( true, false ); // also hide from taskbar
1010 rawHide();
1011 show = false;
1012 }
1013 else
1014 {
1015 setSkipTaskbar( original_skip_taskbar, false );
1016 }
1017 if( minimized )
1018 {
1019 setMappingState( IconicState );
1020 info->setState( NET::Hidden, NET::Hidden );
1021 rawHide();
1022 show = false;
1023 }
1024 if( show )
1025 info->setState( 0, NET::Hidden );
1026 if( !isOnCurrentDesktop())
1027 {
1028 setMappingState( IconicState );
1029 rawHide();
1030 show = false;
1031 }
1032 if( show )
1033 {
1034 bool belongs_to_desktop = false;
1035 for( ClientList::ConstIterator it = group()->members().begin();
1036 it != group()->members().end();
1037 ++it )
1038 if( (*it)->isDesktop())
1039 {
1040 belongs_to_desktop = true;
1041 break;
1042 }
1043 if( !belongs_to_desktop && workspace()->showingDesktop())
1044 workspace()->resetShowingDesktop( true );
1045 if( isShade())
1046 setMappingState( IconicState );
1047 else
1048 setMappingState( NormalState );
1049 rawShow();
1050 }
1051 }
1052
1053void Client::setShadowed(bool shadowed)
1054{
1055 bool wasShadowed;
1056
1057 wasShadowed = isShadowed();
1058 shadowMe = options->shadowEnabled(isActive()) ? shadowed : false;
1059
1060 if (shadowMe) {
1061 if (!wasShadowed)
1062 drawShadow();
1063 }
1064 else {
1065 if (wasShadowed) {
1066 removeShadow();
1067
1068 if (!activeOpacityCache.isNull())
1069 activeOpacityCache.resize(0);
1070 if (!inactiveOpacityCache.isNull())
1071 inactiveOpacityCache.resize(0);
1072 }
1073 }
1074}
1075
1076void Client::updateOpacityCache()
1077{
1078 if (!activeOpacityCache.isNull())
1079 activeOpacityCache.resize(0);
1080 if (!inactiveOpacityCache.isNull())
1081 inactiveOpacityCache.resize(0);
1082
1083 if (!moveResizeMode) {
1084 // If the user is manually resizing, let Client::finishMoveResize()
1085 // decide when to redraw the shadow
1086 removeShadow();
1087 drawIntersectingShadows();
1088 if (options->shadowEnabled(isActive()))
1089 drawDelayedShadow();
1090 }
1091}
1092
1097void Client::drawIntersectingShadows() {
1098 //Client *reshadowClient;
1099 TQRegion region;
1100 //TQPtrList<Client> reshadowClients;
1101 TQValueList<Client *> reshadowClients;
1102 TQValueListIterator<ShadowRegion> it;
1103 TQValueListIterator<Client *> it2;
1104
1105 if (!options->shadowEnabled(false))
1106 // No point in redrawing overlapping/overlapped shadows if only the
1107 // active window has a shadow.
1108 return;
1109
1110 region = shapeBoundingRegion;
1111
1112 // Generate list of Clients whose shadows need to be redrawn. That is,
1113 // those that are currently intersecting or intersected by other windows or
1114 // shadows.
1115 for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it)
1116 if ((isOnAllDesktops() || (*it).client->isOnCurrentDesktop()) &&
1117 !(*it).region.intersect(region).isEmpty())
1118 reshadowClients.append((*it).client);
1119
1120 // Redraw shadows for each of the Clients in the list generated above
1121 for (it2 = reshadowClients.begin(); it2 != reshadowClients.end();
1122 ++it2) {
1123 (*it2)->removeShadow();
1124 (*it2)->drawDelayedShadow();
1125 }
1126}
1127
1133void Client::drawOverlappingShadows(bool waitForMe)
1134{
1135 Client *aClient;
1136 TQRegion region;
1137 TQValueList<Client *> reshadowClients;
1138 ClientList stacking_order;
1139 ClientList::ConstIterator it;
1140 TQValueListIterator<ShadowRegion> it2;
1141 TQValueListIterator<Client *> it3;
1142
1143 if (!options->shadowEnabled(false))
1144 // No point in redrawing overlapping/overlapped shadows if only the
1145 // active window has a shadow.
1146 return;
1147
1148 region = shapeBoundingRegion;
1149
1150 stacking_order = workspace()->stackingOrder();
1151 for (it = stacking_order.fromLast(); it != stacking_order.end(); --it) {
1152 // Find the position of this window in the stacking order.
1153 if ((*it) == this)
1154 break;
1155 }
1156 ++it;
1157 while (it != stacking_order.end()) {
1158 if ((*it)->windowType() == NET::Dock) {
1159 // This function is only interested in windows whose shadows don't
1160 // have weird stacking rules.
1161 ++it;
1162 continue;
1163 }
1164
1165 // Generate list of Clients whose shadows need to be redrawn. That is,
1166 // those that are currently overlapping or overlapped by other windows
1167 // or shadows. The list should be in order from bottom to top in the
1168 // stacking order.
1169 for (it2 = shadowRegions.begin(); it2 != shadowRegions.end(); ++it2) {
1170 if ((*it2).client == (*it)) {
1171 if ((isOnAllDesktops() || (*it2).client->isOnCurrentDesktop())
1172 && !(*it2).region.intersect(region).isEmpty())
1173 reshadowClients.append((*it2).client);
1174 }
1175 }
1176 ++it;
1177 }
1178
1179 // Redraw shadows for each of the Clients in the list generated above
1180 for (it3 = reshadowClients.begin(); it3 != reshadowClients.end(); ++it3) {
1181 (*it3)->removeShadow();
1182 if (it3 == reshadowClients.begin()) {
1183 if (waitForMe)
1184 (*it3)->drawShadowAfter(this);
1185 else
1186 (*it3)->drawDelayedShadow();
1187 }
1188 else {
1189 --it3;
1190 aClient = (*it3);
1191 ++it3;
1192 (*it3)->drawShadowAfter(aClient);
1193 }
1194 }
1195}
1196
1201void Client::drawDelayedShadow()
1202{
1203 shadowDelayTimer->stop();
1204 shadowDelayTimer->start(SHADOW_DELAY, true);
1205}
1206
1210void Client::drawShadowAfter(Client *after)
1211{
1212 shadowAfterClient = after;
1213 connect(after, TQ_SIGNAL(shadowDrawn()), TQ_SLOT(drawShadow()));
1214}
1215
1219void Client::drawShadow()
1220{
1221 Window shadows[2];
1222 XRectangle *shapes;
1223 int i, count, ordering;
1224
1225 // If we are waiting for another Client's shadow to be drawn, stop waiting now
1226 if (shadowAfterClient != NULL) {
1227 disconnect(shadowAfterClient, TQ_SIGNAL(shadowDrawn()), this, TQ_SLOT(drawShadow()));
1228 shadowAfterClient = NULL;
1229 }
1230
1231 if (!isOnCurrentDesktop())
1232 return;
1233
1234 /* Store this window's ShapeBoundingRegion even if shadows aren't drawn for
1235 * this type of window. Otherwise, drawIntersectingShadows() won't update
1236 * properly when this window is moved/resized/hidden/closed.
1237 */
1238 shapes = XShapeGetRectangles(tqt_xdisplay(), frameId(), ShapeBounding,
1239 &count, &ordering);
1240 if (!shapes)
1241 // XShape extension not supported
1242 shapeBoundingRegion = TQRegion(x(), y(), width(), height());
1243 else {
1244 shapeBoundingRegion = TQRegion();
1245 for (i = 0; i < count; i++) {
1246 // Translate XShaped window into a TQRegion
1247 TQRegion shapeRectangle(shapes[i].x, shapes[i].y, shapes[i].width,
1248 shapes[i].height);
1249 shapeBoundingRegion += shapeRectangle;
1250 }
1251 if (isShade())
1252 // Since XResize() doesn't change a window's XShape regions, ensure that
1253 // shapeBoundingRegion is not taller than the window's shaded height,
1254 // or the bottom shadow will appear to be missing
1255 shapeBoundingRegion &= TQRegion(0, 0, width(), height());
1256 shapeBoundingRegion.translate(x(), y());
1257 }
1258
1259 if (!isShadowed() || hidden || isMinimized() ||
1260 maximizeMode() == MaximizeFull ||
1261 !options->shadowWindowType(windowType())) {
1262 XFree(shapes);
1263
1264 // Tell whatever Clients are listening that this Client's shadow has been drawn.
1265 // It hasn't, but there's no sense waiting for something that won't happen.
1266 emit shadowDrawn();
1267
1268 return;
1269 }
1270
1271 removeShadow();
1272
1273 TQMemArray<TQRgb> pixelData;
1274 TQPixmap shadowPixmap;
1275 TQRect shadow;
1276 TQRegion exposedRegion;
1277 ShadowRegion shadowRegion;
1278 int thickness, xOffset, yOffset;
1279
1280 thickness = options->shadowThickness(isActive());
1281 xOffset = options->shadowXOffset(isActive());
1282 yOffset = options->shadowYOffset(isActive());
1283 opacityCache = active? &activeOpacityCache : &inactiveOpacityCache;
1284
1285 shadow.setRect(x() - thickness + xOffset, y() - thickness + yOffset,
1286 width() + thickness * 2, height() + thickness * 2);
1287 shadowPixmap.resize(shadow.size());
1288
1289 // Create a fake drop-down shadow effect via blended Xwindows
1290 shadowWidget = new TQWidget(0, 0, (WFlags)(WStyle_Customize | WX11BypassWM));
1291 shadowWidget->setGeometry(shadow);
1292 XSelectInput(tqt_xdisplay(), shadowWidget->winId(),
1293 ButtonPressMask | ButtonReleaseMask | StructureNotifyMask);
1294 shadowWidget->installEventFilter(this);
1295
1296 if (!shapes) {
1297 // XShape extension not supported
1298 exposedRegion = getExposedRegion(shapeBoundingRegion, shadow.x(),
1299 shadow.y(), shadow.width(), shadow.height(), thickness,
1300 xOffset, yOffset);
1301 shadowRegion.region = exposedRegion;
1302 shadowRegion.client = this;
1303 shadowRegions.append(shadowRegion);
1304
1305 if (opacityCache->isNull())
1306 imposeRegionShadow(shadowPixmap, shapeBoundingRegion,
1307 exposedRegion, thickness,
1308 options->shadowOpacity(isActive()));
1309 else
1310 imposeCachedShadow(shadowPixmap, exposedRegion);
1311 }
1312 else {
1313 TQMemArray<TQRect> exposedRects;
1314 TQMemArray<TQRect>::Iterator it, itEnd;
1315 XRectangle *shadowShapes;
1316
1317 exposedRegion = getExposedRegion(shapeBoundingRegion, shadow.x(),
1318 shadow.y(), shadow.width(), shadow.height(), thickness,
1319 xOffset, yOffset);
1320 shadowRegion.region = exposedRegion;
1321 shadowRegion.client = this;
1322 shadowRegions.append(shadowRegion);
1323
1324 // XShape the shadow
1325 exposedRects = exposedRegion.rects();
1326 i = 0;
1327 itEnd = exposedRects.end();
1328 shadowShapes = new XRectangle[exposedRects.count()];
1329 for (it = exposedRects.begin(); it != itEnd; ++it) {
1330 shadowShapes[i].x = (*it).x();
1331 shadowShapes[i].y = (*it).y();
1332 shadowShapes[i].width = (*it).width();
1333 shadowShapes[i].height = (*it).height();
1334 i++;
1335 }
1336 XShapeCombineRectangles(tqt_xdisplay(), shadowWidget->winId(),
1337 ShapeBounding, -x() + thickness - xOffset,
1338 -y() + thickness - yOffset, shadowShapes, i, ShapeSet,
1339 Unsorted);
1340 delete [] shadowShapes;
1341
1342 if (opacityCache->isNull())
1343 imposeRegionShadow(shadowPixmap, shapeBoundingRegion,
1344 exposedRegion, thickness,
1345 options->shadowOpacity(isActive()));
1346 else
1347 imposeCachedShadow(shadowPixmap, exposedRegion);
1348 }
1349
1350 XFree(shapes);
1351
1352 // Set the background pixmap
1353 //shadowPixmap.convertFromImage(shadowImage);
1354 shadowWidget->setErasePixmap(shadowPixmap);
1355
1356 // Restack shadows under this window so that shadows drawn for a newly
1357 // focused (but not raised) window don't overlap any windows above it.
1358 if (isDock()) {
1359 ClientList stacking_order = workspace()->stackingOrder();
1360 for (ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
1361 if ((*it)->isDesktop())
1362 {
1363 ++it;
1364 shadows[0] = (*it)->frameId();
1365 shadows[1] = shadowWidget->winId();
1366 }
1367 }
1368 else {
1369 shadows[0] = frameId();
1370 if (shadowWidget != NULL)
1371 shadows[1] = shadowWidget->winId();
1372 }
1373
1374 XRestackWindows(tqt_xdisplay(), shadows, 2);
1375
1376 // Don't use TQWidget::show() so we don't confuse QEffects, thus causing
1377 // broken focus.
1378 XMapWindow(tqt_xdisplay(), shadowWidget->winId());
1379
1380 // Tell whatever Clients are listening that this Client's shadow has been drawn.
1381 emit shadowDrawn();
1382}
1383
1387void Client::removeShadow()
1388{
1389 TQValueList<ShadowRegion>::Iterator it;
1390
1391 shadowDelayTimer->stop();
1392
1393 if (shadowWidget != NULL) {
1394 for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it)
1395 if ((*it).client == this) {
1396 shadowRegions.remove(it);
1397 break;
1398 }
1399 delete shadowWidget;
1400 shadowWidget = NULL;
1401 }
1402}
1403
1408TQRegion Client::getExposedRegion(TQRegion occludedRegion, int x, int y, int w,
1409 int h, int thickness, int xOffset, int yOffset)
1410{
1411 TQRegion exposedRegion;
1412
1413 exposedRegion = TQRegion(x, y, w, h);
1414 exposedRegion -= occludedRegion;
1415
1416 if (thickness > 0) {
1417 // Limit exposedRegion to include only where a shadow of the specified
1418 // thickness will be drawn
1419 TQMemArray<TQRect> occludedRects;
1420 TQMemArray<TQRect>::Iterator it, itEnd;
1421 TQRegion shadowRegion;
1422
1423 occludedRects = occludedRegion.rects();
1424 itEnd = occludedRects.end();
1425 for (it = occludedRects.begin(); it != itEnd; ++it) {
1426 // Expand each of the occluded region's shape rectangles to contain
1427 // where a shadow of the specified thickness will be drawn. Create
1428 // a new TQRegion that contains the expanded occluded region
1429 it->setTop(it->top() - thickness + yOffset);
1430 it->setLeft(it->left() - thickness + xOffset);
1431 it->setRight(it->right() + thickness + xOffset);
1432 it->setBottom(it->bottom() + thickness + yOffset);
1433 shadowRegion += TQRegion(*it);
1434 }
1435 exposedRegion -= exposedRegion - shadowRegion;
1436 }
1437
1438 return exposedRegion;
1439}
1440
1444void Client::imposeCachedShadow(TQPixmap &pixmap, TQRegion exposed)
1445{
1446 TQRgb pixel;
1447 double opacity;
1448 int red, green, blue, pixelRed, pixelGreen, pixelBlue;
1449 int subW, subH, w, x, y, zeroX, zeroY;
1450 TQImage image;
1451 TQMemArray<TQRect>::Iterator it, itEnd;
1452 TQMemArray<TQRect> rectangles;
1453 TQPixmap subPixmap;
1454 Window rootWindow;
1455 int thickness, windowX, windowY, xOffset, yOffset;
1456
1457 rectangles = exposed.rects();
1458 rootWindow = tqt_xrootwin();
1459 thickness = options->shadowThickness(isActive());
1460 windowX = this->x();
1461 windowY = this->y();
1462 xOffset = options->shadowXOffset(isActive());
1463 yOffset = options->shadowYOffset(isActive());
1464 options->shadowColour(isActive()).rgb(&red, &green, &blue);
1465 w = pixmap.width();
1466
1467 itEnd = rectangles.end();
1468 for (it = rectangles.begin(); it != itEnd; ++it) {
1469 subW = (*it).width();
1470 subH = (*it).height();
1471 subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(),
1472 subW, subH);
1473 zeroX = (*it).x() - windowX + thickness - xOffset;
1474 zeroY = (*it).y() - windowY + thickness - yOffset;
1475 image = subPixmap.convertToImage();
1476
1477 for (x = 0; x < subW; x++) {
1478 for (y = 0; y < subH; y++) {
1479 opacity = (*(opacityCache))[(zeroY + y) * w + zeroX + x];
1480 pixel = image.pixel(x, y);
1481 pixelRed = tqRed(pixel);
1482 pixelGreen = tqGreen(pixel);
1483 pixelBlue = tqBlue(pixel);
1484 image.setPixel(x, y,
1485 tqRgb((int)(pixelRed + (red - pixelRed) * opacity),
1486 (int)(pixelGreen + (green - pixelGreen) * opacity),
1487 (int)(pixelBlue + (blue - pixelBlue) * opacity)));
1488 }
1489 }
1490
1491 subPixmap.convertFromImage(image);
1492 bitBlt(&pixmap, zeroX, zeroY, &subPixmap);
1493 }
1494}
1495
1499void Client::imposeRegionShadow(TQPixmap &pixmap, TQRegion occluded,
1500 TQRegion exposed, int thickness, double maxOpacity)
1501{
1502 int distance, intersectCount, i, j, x, y;
1503 TQRgb pixel;
1504 double decay, factor, opacity;
1505 int red, green, blue, pixelRed, pixelGreen, pixelBlue;
1506 int lineIntersects, maxIntersects, maxY;
1507 int irBottom, irLeft, irRight, irTop, yIncrement;
1508 int subW, subH, w, h, zeroX, zeroY;
1509 TQImage image;
1510 TQMemArray<TQRect>::Iterator it, itEnd;
1511 TQMemArray<TQRect> rectangles;
1512 TQPixmap subPixmap;
1513 Window rootWindow;
1514 int windowX, windowY, xOffset, yOffset;
1515
1516 rectangles = exposed.rects();
1517 rootWindow = tqt_xrootwin();
1518 windowX = this->x();
1519 windowY = this->y();
1520 xOffset = options->shadowXOffset(isActive());
1521 yOffset = options->shadowYOffset(isActive());
1522 options->shadowColour(isActive()).rgb(&red, &green, &blue);
1523 maxIntersects = thickness * thickness * 4 + (thickness * 4) + 1;
1524 lineIntersects = thickness * 2 + 1;
1525 factor = maxIntersects / maxOpacity;
1526 decay = (lineIntersects / 0.0125 - factor) / pow((double)maxIntersects, 3.0);
1527 w = pixmap.width();
1528 h = pixmap.height();
1529 xOffset = options->shadowXOffset(isActive());
1530 yOffset = options->shadowYOffset(isActive());
1531
1532 opacityCache->resize(0);
1533 opacityCache->resize(w * h);
1534 occluded.translate(-windowX + thickness, -windowY + thickness);
1535
1536 itEnd = rectangles.end();
1537 for (it = rectangles.begin(); it != itEnd; ++it) {
1538 subW = (*it).width();
1539 subH = (*it).height();
1540 subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(),
1541 subW, subH);
1542 maxY = subH;
1543 zeroX = (*it).x() - windowX + thickness - xOffset;
1544 zeroY = (*it).y() - windowY + thickness - yOffset;
1545 image = subPixmap.convertToImage();
1546
1547 intersectCount = 0;
1548 opacity = -1;
1549 y = 0;
1550 yIncrement = 1;
1551 for (x = 0; x < subW; x++) {
1552 irLeft = zeroX + x - thickness;
1553 irRight = zeroX + x + thickness;
1554
1555 while (y != maxY) {
1556 // horizontal row about to leave the intersect region, not
1557 // necessarily the top row
1558 irTop = zeroY + y - thickness * yIncrement;
1559 // horizontal row that just came into the intersect region,
1560 // not necessarily the bottom row
1561 irBottom = zeroY + y + thickness * yIncrement;
1562
1563 if (opacity == -1) {
1564 // If occluded pixels caused an intersect count to be
1565 // skipped, recount it
1566 intersectCount = 0;
1567
1568 for (j = irTop; j != irBottom; j += yIncrement) {
1569 // irTop is not necessarily larger than irBottom and
1570 // yIncrement isn't necessarily positive
1571 for (i = irLeft; i <= irRight; i++) {
1572 if (occluded.contains(TQPoint(i, j)))
1573 intersectCount++;
1574 }
1575 }
1576 }
1577 else {
1578 if (intersectCount < 0)
1579 intersectCount = 0;
1580
1581 for (i = irLeft; i <= irRight; i++) {
1582 if (occluded.contains(TQPoint(i, irBottom)))
1583 intersectCount++;
1584 }
1585 }
1586
1587 distance = maxIntersects - intersectCount;
1588 opacity = intersectCount / (factor + pow((double)distance, 3.0) * decay);
1589
1590 (*(opacityCache))[(zeroY + y) * w + zeroX + x] = opacity;
1591 pixel = image.pixel(x, y);
1592 pixelRed = tqRed(pixel);
1593 pixelGreen = tqGreen(pixel);
1594 pixelBlue = tqBlue(pixel);
1595 image.setPixel(x, y,
1596 tqRgb((int)(pixelRed + (red - pixelRed) * opacity),
1597 (int)(pixelGreen + (green - pixelGreen) * opacity),
1598 (int)(pixelBlue + (blue - pixelBlue) * opacity)));
1599
1600 for (i = irLeft; i <= irRight; i++) {
1601 if (occluded.contains(TQPoint(i, irTop)))
1602 intersectCount--;
1603 }
1604
1605 y += yIncrement;
1606 }
1607 y -= yIncrement;
1608
1609 irTop += yIncrement;
1610 for (j = irTop; j != irBottom; j += yIncrement) {
1611 if (occluded.contains(TQPoint(irLeft, j)))
1612 intersectCount--;
1613 }
1614 irRight++;
1615 for (j = irTop; j != irBottom; j += yIncrement) {
1616 if (occluded.contains(TQPoint(irRight, j)))
1617 intersectCount++;
1618 }
1619
1620 yIncrement *= -1;
1621 if (yIncrement < 0)
1622 // Scan Y-axis bottom-up for next X-coordinate iteration
1623 maxY = -1;
1624 else
1625 // Scan Y-axis top-down for next X-coordinate iteration
1626 maxY = subH;
1627 }
1628
1629 subPixmap.convertFromImage(image);
1630 bitBlt(&pixmap, zeroX, zeroY, &subPixmap);
1631 }
1632}
1633
1638void Client::setMappingState(int s)
1639 {
1640 assert( client != None );
1641 assert( !deleting || s == WithdrawnState );
1642 if( mapping_state == s )
1643 return;
1644 bool was_unmanaged = ( mapping_state == WithdrawnState );
1645 mapping_state = s;
1646 if( mapping_state == WithdrawnState )
1647 {
1648 XDeleteProperty( tqt_xdisplay(), window(), tqt_wm_state );
1649 return;
1650 }
1651 assert( s == NormalState || s == IconicState );
1652
1653 unsigned long data[2];
1654 data[0] = (unsigned long) s;
1655 data[1] = (unsigned long) None;
1656 XChangeProperty(tqt_xdisplay(), window(), tqt_wm_state, tqt_wm_state, 32,
1657 PropModeReplace, (unsigned char *)data, 2);
1658
1659 if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry
1660 postponeGeometryUpdates( false );
1661 }
1662
1667void Client::rawShow()
1668 {
1669 if( decoration != NULL )
1670 decoration->widget()->show(); // not really necessary, but let it know the state
1671 XMapWindow( tqt_xdisplay(), frame );
1672 if( !isShade())
1673 {
1674 XMapWindow( tqt_xdisplay(), wrapper );
1675 XMapWindow( tqt_xdisplay(), client );
1676 }
1677 if (options->shadowEnabled(isActive()))
1678 drawDelayedShadow();
1679 }
1680
1686void Client::rawHide()
1687 {
1688// Here it may look like a race condition, as some other client might try to unmap
1689// the window between these two XSelectInput() calls. However, they're supposed to
1690// use XWithdrawWindow(), which also sends a synthetic event to the root window,
1691// which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
1692// will be missed is also very minimal, so I don't think it's needed to grab the server
1693// here.
1694 removeShadow();
1695 drawIntersectingShadows();
1696 XSelectInput( tqt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
1697 XUnmapWindow( tqt_xdisplay(), frame );
1698 XUnmapWindow( tqt_xdisplay(), wrapper );
1699 XUnmapWindow( tqt_xdisplay(), client );
1700 XSelectInput( tqt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
1701 if( decoration != NULL )
1702 decoration->widget()->hide(); // not really necessary, but let it know the state
1703 workspace()->clientHidden( this );
1704 }
1705
1706void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3)
1707 {
1708 XEvent ev;
1709 long mask;
1710
1711 memset(&ev, 0, sizeof(ev));
1712 ev.xclient.type = ClientMessage;
1713 ev.xclient.window = w;
1714 ev.xclient.message_type = a;
1715 ev.xclient.format = 32;
1716 ev.xclient.data.l[0] = protocol;
1717 ev.xclient.data.l[1] = get_tqt_x_time();
1718 ev.xclient.data.l[2] = data1;
1719 ev.xclient.data.l[3] = data2;
1720 ev.xclient.data.l[4] = data3;
1721 mask = 0L;
1722 if (w == tqt_xrootwin())
1723 mask = SubstructureRedirectMask; /* magic! */
1724 XSendEvent(tqt_xdisplay(), w, False, mask, &ev);
1725 }
1726
1727/*
1728 Returns whether the window may be closed (have a close button)
1729 */
1730bool Client::isCloseable() const
1731 {
1732 if( isModalSystemNotification())
1733 return false;
1734 return rules()->checkCloseable( motif_may_close && !isSpecialWindow());
1735 }
1736
1741void Client::closeWindow()
1742 {
1743 if( !isCloseable())
1744 return;
1745 // Update user time, because the window may create a confirming dialog.
1746 updateUserTime();
1747 if ( Pdeletewindow )
1748 {
1749 Notify::raise( Notify::Close );
1750 sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
1751 pingWindow();
1752 }
1753 else
1754 {
1755 // client will not react on wm_delete_window. We have not choice
1756 // but destroy his connection to the XServer.
1757 killWindow();
1758 }
1759 }
1760
1761
1765void Client::killWindow()
1766 {
1767 kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl;
1768 // not sure if we need an Notify::Kill or not.. until then, use
1769 // Notify::Close
1770 Notify::raise( Notify::Close );
1771
1772 if( isDialog())
1773 Notify::raise( Notify::TransDelete );
1774 if( isNormalWindow())
1775 Notify::raise( Notify::Delete );
1776 killProcess( false );
1777 // always kill this client at the server
1778 XKillClient(tqt_xdisplay(), window() );
1779 destroyClient();
1780 }
1781
1782// send a ping to the window using _NET_WM_PING if possible
1783// if it doesn't respond within a reasonable time, it will be
1784// killed
1785void Client::pingWindow()
1786 {
1787 if( !Pping )
1788 return; // can't ping :(
1789 if( options->killPingTimeout == 0 )
1790 return; // turned off
1791 if( ping_timer != NULL )
1792 return; // pinging already
1793 ping_timer = new TQTimer( this );
1794 connect( ping_timer, TQ_SIGNAL( timeout()), TQ_SLOT( pingTimeout()));
1795 ping_timer->start( options->killPingTimeout, true );
1796 ping_timestamp = get_tqt_x_time();
1797 workspace()->sendPingToWindow( window(), ping_timestamp );
1798 }
1799
1800void Client::gotPing( Time timestamp )
1801 {
1802 // just plain compare is not good enough because of 64bit and truncating and whatnot
1803 if( NET::timestampCompare( timestamp, ping_timestamp ) != 0 )
1804 return;
1805 delete ping_timer;
1806 ping_timer = NULL;
1807 if( process_killer != NULL )
1808 {
1809 process_killer->kill();
1810 delete process_killer;
1811 process_killer = NULL;
1812 }
1813 }
1814
1815void Client::pingTimeout()
1816 {
1817 kdDebug( 1212 ) << "Ping timeout:" << caption() << endl;
1818 delete ping_timer;
1819 ping_timer = NULL;
1820 killProcess( true, ping_timestamp );
1821 }
1822
1823void Client::killProcess( bool ask, Time timestamp )
1824 {
1825 if( process_killer != NULL )
1826 return;
1827 Q_ASSERT( !ask || timestamp != CurrentTime );
1828 TQCString machine = wmClientMachine( true );
1829 pid_t pid = info->pid();
1830 if( pid <= 0 || machine.isEmpty()) // needed properties missing
1831 return;
1832 kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl;
1833 if( !ask )
1834 {
1835 if( machine != "localhost" )
1836 {
1837 TDEProcess proc;
1838 proc << "xon" << machine << "kill" << pid;
1839 proc.start( TDEProcess::DontCare );
1840 }
1841 else
1842 ::kill( pid, SIGTERM );
1843 }
1844 else
1845 { // SELI TODO handle the window created by handler specially (on top,urgent?)
1846 process_killer = new TDEProcess( this );
1847 *process_killer << TDEStandardDirs::findExe( "twin_killer_helper" )
1848 << "--pid" << TQCString().setNum( pid ) << "--hostname" << machine
1849 << "--windowname" << caption().utf8()
1850 << "--applicationname" << resourceClass()
1851 << "--wid" << TQCString().setNum( window())
1852 << "--timestamp" << TQCString().setNum( timestamp );
1853 connect( process_killer, TQ_SIGNAL( processExited( TDEProcess* )),
1854 TQ_SLOT( processKillerExited()));
1855 if( !process_killer->start( TDEProcess::NotifyOnExit ))
1856 {
1857 delete process_killer;
1858 process_killer = NULL;
1859 return;
1860 }
1861 }
1862 }
1863
1864bool Client::isSuspendable() const
1865 {
1866 bool cansuspend = true;
1867 if( skipTaskbar() || skipPager() )
1868 return false;
1869 TQCString machine = wmClientMachine( true );
1870 pid_t pid = info->pid();
1871 if( pid <= 0 || machine.isEmpty()) // needed properties missing
1872 return false;
1873 kdDebug( 1212 ) << "Check suspendable process:" << pid << "(" << machine << ")" << endl;
1874 if( machine != "localhost" )
1875 {
1876 return false;
1877 }
1878 else
1879 {
1880#ifdef Q_OS_SOLARIS
1881 TQFile procStatFile(TQString("/proc/%1/lwp/1/lwpsinfo").arg(pid));
1882#else /* default */
1883 TQFile procStatFile(TQString("/proc/%1/stat").arg(pid));
1884#endif
1885 if (procStatFile.open(IO_ReadOnly))
1886 {
1887 TQByteArray statRaw = procStatFile.readAll();
1888 procStatFile.close();
1889#ifdef Q_OS_SOLARIS
1890 lwpsinfo_t *inf = (lwpsinfo_t *)statRaw.data();
1891 char tbuf[PATH_MAX];
1892 TQString tcomm;
1893 TQString state(TQChar(inf->pr_sname));
1894
1895 readlink(TQString("/proc/%1/path/a.out").arg(pid).latin1(),
1896 tbuf, sizeof(tbuf));
1897 tcomm = basename(tbuf);
1898#else /* default */
1899 TQString statString(statRaw);
1900 TQStringList statFields = TQStringList::split(" ", statString, TRUE);
1901 TQString tcomm = statFields[1];
1902 TQString state = statFields[2];
1903#endif /* default */
1904 if( state != "T" )
1905 {
1906 // Make sure no windows of this process are special
1907 for ( ClientList::ConstIterator it = workspace()->clients.begin(); it != workspace()->clients.end(); ++it)
1908 {
1909 Client* nextclient = *it;
1910 pid_t nextpid = nextclient->info->pid();
1911 TQCString nextmachine = nextclient->wmClientMachine( true );
1912 if( nextpid > 0 && (!nextmachine.isEmpty()))
1913 {
1914 if( ( nextmachine == "localhost" ) && ( pid == nextpid ) )
1915 {
1916 if( nextclient->skipTaskbar() || nextclient->skipPager() )
1917 cansuspend = false;
1918 }
1919 }
1920 }
1921 // Process exception list
1922 TQString execname(tcomm);
1923 execname.truncate(execname.length()-1);
1924 execname = execname.remove(0,1);
1925 // FIXME This list should not be hardcoded
1926 if( (execname == "kdesktop") || (execname == "kicker") )
1927 return false;
1928 else
1929 return cansuspend;
1930 }
1931 else
1932 {
1933 return false;
1934 }
1935 }
1936 else
1937 {
1938 return false;
1939 }
1940 }
1941 }
1942
1943bool Client::isResumeable() const
1944 {
1945 TQCString machine = wmClientMachine( true );
1946 pid_t pid = info->pid();
1947 if( pid <= 0 || machine.isEmpty()) // needed properties missing
1948 return false;
1949 kdDebug( 1212 ) << "Check resumeable process:" << pid << "(" << machine << ")" << endl;
1950 if( machine != "localhost" )
1951 {
1952 return false;
1953 }
1954 else
1955 {
1956#ifdef Q_OS_SOLARIS
1957 TQFile procStatFile(TQString("/proc/%1/lwp/1/lwpsinfo").arg(pid));
1958#else /* default */
1959 TQFile procStatFile(TQString("/proc/%1/stat").arg(pid));
1960#endif
1961 if (procStatFile.open(IO_ReadOnly))
1962 {
1963 TQByteArray statRaw = procStatFile.readAll();
1964 procStatFile.close();
1965#ifdef Q_OS_SOLARIS
1966 lwpsinfo_t *inf = (lwpsinfo_t *)statRaw.data();
1967 TQString state(TQChar(inf->pr_sname));
1968#else /* default */
1969 TQString statString(statRaw);
1970 TQStringList statFields = TQStringList::split(" ", statString, TRUE);
1971 TQString tcomm = statFields[1];
1972 TQString state = statFields[2];
1973#endif /* default */
1974 if( state == "T" )
1975 {
1976 return true;
1977 }
1978 else
1979 {
1980 return false;
1981 }
1982 }
1983 else
1984 {
1985 return false;
1986 }
1987 }
1988 }
1989
1990bool Client::queryUserSuspendedResume()
1991 {
1992 if (isResumeable())
1993 {
1994 if (process_resumer != NULL)
1995 {
1996 return false;
1997 }
1998 // FIXME We should display a busy cursor until twin_resumer_helper loads
1999 process_resumer = new TDEProcess( this );
2000 *process_resumer << TDEStandardDirs::findExe( "twin_resumer_helper" )
2001 << "--pid" << TQCString().setNum( info->pid() ) << "--hostname" << wmClientMachine( true )
2002 << "--windowname" << caption().utf8()
2003 << "--applicationname" << resourceClass()
2004 << "--wid" << TQCString().setNum( window());
2005 connect( process_resumer, TQ_SIGNAL( processExited( TDEProcess* )),
2006 TQ_SLOT( processResumerExited()));
2007 if( !process_resumer->start( TDEProcess::NotifyOnExit ))
2008 {
2009 delete process_resumer;
2010 process_resumer = NULL;
2011 return true;
2012 }
2013 return false;
2014 }
2015 else
2016 {
2017 return true;
2018 }
2019 }
2020
2021void Client::suspendWindow()
2022 {
2023 TQCString machine = wmClientMachine( true );
2024 pid_t pid = info->pid();
2025 if( pid <= 0 || machine.isEmpty()) // needed properties missing
2026 return;
2027 kdDebug( 1212 ) << "Suspend process:" << pid << "(" << machine << ")" << endl;
2028 if( machine != "localhost" )
2029 {
2030 return;
2031 }
2032 else
2033 {
2034 for ( ClientList::ConstIterator it = workspace()->clients.begin(); it != workspace()->clients.end(); ++it)
2035 {
2036 Client* nextclient = *it;
2037 pid_t nextpid = nextclient->info->pid();
2038 TQCString nextmachine = nextclient->wmClientMachine( true );
2039 if( nextpid > 0 && (!nextmachine.isEmpty()))
2040 {
2041 if( ( nextmachine == "localhost" ) && ( pid == nextpid ) )
2042 {
2043 TQString newCaption = TQString(readName()).append(" <").append(i18n("Suspended")).append(">");
2044 nextclient->info->setVisibleName(newCaption.utf8());
2045 nextclient->info->setVisibleIconName(newCaption.utf8());
2046 nextclient->minimized_before_suspend = nextclient->isMinimized();
2047 nextclient->minimize(true);
2048 }
2049 }
2050 }
2051 ::kill( pid, SIGSTOP );
2052 }
2053 }
2054
2055void Client::resumeWindow()
2056 {
2057 TQCString machine = wmClientMachine( true );
2058 pid_t pid = info->pid();
2059 if( pid <= 0 || machine.isEmpty()) // needed properties missing
2060 return;
2061 kdDebug( 1212 ) << "Resume process:" << pid << "(" << machine << ")" << endl;
2062 if( machine != "localhost" )
2063 {
2064 return;
2065 }
2066 else
2067 {
2068 ::kill( pid, SIGCONT );
2069 for ( ClientList::ConstIterator it = workspace()->clients.begin(); it != workspace()->clients.end(); ++it)
2070 {
2071 Client* nextclient = *it;
2072 pid_t nextpid = nextclient->info->pid();
2073 TQCString nextmachine = nextclient->wmClientMachine( true );
2074 if( nextpid > 0 && (!nextmachine.isEmpty()))
2075 {
2076 if( ( nextmachine == "localhost" ) && ( pid == nextpid ) )
2077 {
2078 if (!nextclient->minimized_before_suspend)
2079 {
2080 nextclient->unminimize(true);
2081 }
2082 nextclient->updateCaption();
2083 }
2084 }
2085 }
2086 }
2087 }
2088
2089void Client::processKillerExited()
2090 {
2091 kdDebug( 1212 ) << "Killer exited" << endl;
2092 delete process_killer;
2093 process_killer = NULL;
2094 }
2095
2096void Client::processResumerExited()
2097 {
2098 kdDebug( 1212 ) << "Resumer exited" << endl;
2099 // 0 means the user clicked Resume; 2 means that the resumer dialog failed to launch somehow
2100 if ((process_resumer->exitStatus() == 0) || (process_resumer->exitStatus() == 2))
2101 {
2102 resumeWindow();
2103 takeFocus( Allowed );
2104 }
2105 delete process_resumer;
2106 process_resumer = NULL;
2107 }
2108
2109void Client::setSkipTaskbar( bool b, bool from_outside )
2110 {
2111 int was_wants_tab_focus = wantsTabFocus();
2112 if( from_outside )
2113 {
2114 b = rules()->checkSkipTaskbar( b );
2115 original_skip_taskbar = b;
2116 }
2117 if ( b == skipTaskbar() )
2118 return;
2119 skip_taskbar = b;
2120 info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar );
2121 updateWindowRules();
2122 if( was_wants_tab_focus != wantsTabFocus())
2123 workspace()->updateFocusChains( this,
2124 isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate );
2125 }
2126
2127void Client::setSkipPager( bool b )
2128 {
2129 b = rules()->checkSkipPager( b );
2130 if ( b == skipPager() )
2131 return;
2132 skip_pager = b;
2133 info->setState( b?NET::SkipPager:0, NET::SkipPager );
2134 updateWindowRules();
2135 }
2136
2137void Client::setModal( bool m )
2138 { // Qt-3.2 can have even modal normal windows :(
2139 if( modal == m )
2140 return;
2141 modal = m;
2142 if( !modal )
2143 return;
2144 // changing modality for a mapped window is weird (?)
2145 // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
2146 }
2147
2148void Client::setDesktop( int desktop )
2149 {
2150 if( desktop != NET::OnAllDesktops ) // do range check
2151 desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop ));
2152 desktop = rules()->checkDesktop( desktop );
2153 if( desk == desktop )
2154 return;
2155 int was_desk = desk;
2156 desk = desktop;
2157 info->setDesktop( desktop );
2158 if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
2159 { // onAllDesktops changed
2160 if ( isShown( true ))
2161 Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
2162 workspace()->updateOnAllDesktopsOfTransients( this );
2163 }
2164 if( decoration != NULL )
2165 decoration->desktopChange();
2166 workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
2167 updateVisibility();
2168 updateWindowRules();
2169 }
2170
2171void Client::setOnAllDesktops( bool b )
2172 {
2173 if(( b && isOnAllDesktops())
2174 || ( !b && !isOnAllDesktops()))
2175 return;
2176 if( b )
2177 setDesktop( NET::OnAllDesktops );
2178 else
2179 setDesktop( workspace()->currentDesktop());
2180 }
2181
2182bool Client::isOnCurrentDesktop() const
2183 {
2184 return isOnDesktop( workspace()->currentDesktop());
2185 }
2186
2187int Client::screen() const
2188 {
2189 if( !options->xineramaEnabled )
2190 return 0;
2191 return workspace()->screenNumber( geometry().center());
2192 }
2193
2194bool Client::isOnScreen( int screen ) const
2195 {
2196 if( !options->xineramaEnabled )
2197 return screen == 0;
2198 return workspace()->screenGeometry( screen ).intersects( geometry());
2199 }
2200
2201// performs activation and/or raising of the window
2202void Client::takeActivity( int flags, bool handled, allowed_t )
2203 {
2204 if( !handled || !Ptakeactivity )
2205 {
2206 if( flags & ActivityFocus )
2207 takeFocus( Allowed );
2208 if( flags & ActivityRaise )
2209 workspace()->raiseClient( this );
2210 return;
2211 }
2212
2213#ifndef NDEBUG
2214 static Time previous_activity_timestamp;
2215 static Client* previous_client;
2216 if( previous_activity_timestamp == get_tqt_x_time() && previous_client != this )
2217 {
2218 kdDebug( 1212 ) << "Repeated use of the same X timestamp for activity" << endl;
2219 kdDebug( 1212 ) << kdBacktrace() << endl;
2220 }
2221 previous_activity_timestamp = get_tqt_x_time();
2222 previous_client = this;
2223#endif
2224 workspace()->sendTakeActivity( this, get_tqt_x_time(), flags );
2225 }
2226
2227// performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
2228void Client::takeFocus( allowed_t )
2229 {
2230#ifndef NDEBUG
2231 static Time previous_focus_timestamp;
2232 static Client* previous_client;
2233 if( previous_focus_timestamp == get_tqt_x_time() && previous_client != this )
2234 {
2235 kdDebug( 1212 ) << "Repeated use of the same X timestamp for focus" << endl;
2236 kdDebug( 1212 ) << kdBacktrace() << endl;
2237 }
2238 previous_focus_timestamp = get_tqt_x_time();
2239 previous_client = this;
2240#endif
2241 if ( rules()->checkAcceptFocus( input ))
2242 {
2243 XSetInputFocus( tqt_xdisplay(), window(), RevertToPointerRoot, get_tqt_x_time() );
2244 // Work around opacity bug
2245 bool activePrev = active;
2246 active = true;
2247 updateOpacity();
2248 active = activePrev;
2249 }
2250 if ( Ptakefocus )
2251 {
2252 sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
2253 }
2254 workspace()->setShouldGetFocus( this );
2255 }
2256
2264bool Client::providesContextHelp() const
2265 {
2266 if (isModalSystemNotification())
2267 return false;
2268 return Pcontexthelp;
2269 }
2270
2271
2278void Client::showContextHelp()
2279 {
2280 if ( Pcontexthelp )
2281 {
2282 sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
2283 TQWhatsThis::enterWhatsThisMode(); // SELI?
2284 }
2285 }
2286
2287
2292void Client::fetchName()
2293 {
2294 setCaption( readName());
2295 }
2296
2297TQString Client::readName() const
2298 {
2299 if ( info->name() && info->name()[ 0 ] != '\0' )
2300 return TQString::fromUtf8( info->name() );
2301 else
2302 return KWin::readNameProperty( window(), XA_WM_NAME );
2303 }
2304
2305KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
2306
2307void Client::setCaption( const TQString& s, bool force )
2308 {
2309 if ( s != cap_normal || force )
2310 {
2311 bool reset_name = force;
2312 for( unsigned int i = 0;
2313 i < s.length();
2314 ++i )
2315 if( !s[ i ].isPrint())
2316 s[ i ] = ' ';
2317 cap_normal = s;
2318 bool was_suffix = ( !cap_suffix.isEmpty());
2319 TQString machine_suffix;
2320 if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false )))
2321 machine_suffix = " <@" + wmClientMachine( true ) + ">";
2322 TQString shortcut_suffix = !shortcut().isNull() ? ( " {" + shortcut().toString() + "}" ) : "";
2323 cap_suffix = machine_suffix + shortcut_suffix;
2324 if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this )))
2325 {
2326 int i = 2;
2327 do
2328 {
2329 cap_suffix = machine_suffix + " <" + TQString::number(i) + ">" + shortcut_suffix;
2330 i++;
2331 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
2332 info->setVisibleName( caption().utf8() );
2333 reset_name = false;
2334 }
2335 if(( (was_suffix && cap_suffix.isEmpty())
2336 || reset_name )) // if it was new window, it may have old value still set, if the window is reused
2337 {
2338 info->setVisibleName( "" ); // remove
2339 info->setVisibleIconName( "" ); // remove
2340 }
2341 else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
2342 info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() );
2343
2344 if( isManaged() && decoration != NULL )
2345 decoration->captionChange();
2346 }
2347 }
2348
2349void Client::updateCaption()
2350 {
2351 setCaption( cap_normal, true );
2352 }
2353
2354void Client::fetchIconicName()
2355 {
2356 TQString s;
2357 if ( info->iconName() && info->iconName()[ 0 ] != '\0' )
2358 s = TQString::fromUtf8( info->iconName() );
2359 else
2360 s = KWin::readNameProperty( window(), XA_WM_ICON_NAME );
2361 if ( s != cap_iconic )
2362 {
2363 bool was_set = !cap_iconic.isEmpty();
2364 cap_iconic = s;
2365 if( !cap_suffix.isEmpty())
2366 {
2367 if( !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
2368 info->setVisibleIconName( ( s + cap_suffix ).utf8() );
2369 else if( was_set )
2370 info->setVisibleIconName( "" ); //remove
2371 }
2372 }
2373 }
2374
2377TQString Client::caption( bool full ) const
2378 {
2379 return full ? cap_normal + cap_suffix : cap_normal;
2380 }
2381
2382void Client::getWMHints()
2383 {
2384 XWMHints *hints = XGetWMHints(tqt_xdisplay(), window() );
2385 input = true;
2386 window_group = None;
2387 urgency = false;
2388 if ( hints )
2389 {
2390 if( hints->flags & InputHint )
2391 input = hints->input;
2392 if( hints->flags & WindowGroupHint )
2393 window_group = hints->window_group;
2394 urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield
2395 XFree( (char*)hints );
2396 }
2397 checkGroup();
2398 updateUrgency();
2399 updateAllowedActions(); // group affects isMinimizable()
2400 }
2401
2402void Client::getMotifHints()
2403 {
2404 bool mnoborder, mresize, mmove, mminimize, mmaximize, mclose;
2405 Motif::readFlags( client, mnoborder, mresize, mmove, mminimize, mmaximize, mclose );
2406 motif_noborder = mnoborder;
2407 if( !hasNETSupport()) // NETWM apps should set type and size constraints
2408 {
2409 motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well
2410 motif_may_move = mmove;
2411 }
2412 else
2413 motif_may_resize = motif_may_move = true;
2414 // mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too
2415 // mmaximize; - ignore, bogus - maximizing is basically just resizing
2416 motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway
2417 if( isManaged())
2418 updateDecoration( true ); // check if noborder state has changed
2419 }
2420
2421void Client::readIcons( Window win, TQPixmap* icon, TQPixmap* miniicon )
2422 {
2423 // get the icons, allow scaling
2424 if( icon != NULL )
2425 *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints );
2426 if( miniicon != NULL )
2427 {
2428 if( icon == NULL || !icon->isNull())
2429 *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints );
2430 else
2431 *miniicon = TQPixmap();
2432 }
2433 }
2434
2435void Client::getIcons()
2436 {
2437 // first read icons from the window itself
2438 readIcons( window(), &icon_pix, &miniicon_pix );
2439 if( icon_pix.isNull())
2440 { // then try window group
2441 icon_pix = group()->icon();
2442 miniicon_pix = group()->miniIcon();
2443 }
2444 if( icon_pix.isNull() && isTransient())
2445 { // then mainclients
2446 ClientList mainclients = mainClients();
2447 for( ClientList::ConstIterator it = mainclients.begin();
2448 it != mainclients.end() && icon_pix.isNull();
2449 ++it )
2450 {
2451 icon_pix = (*it)->icon();
2452 miniicon_pix = (*it)->miniIcon();
2453 }
2454 }
2455 if( icon_pix.isNull())
2456 { // and if nothing else, load icon from classhint or xapp icon
2457 icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp );
2458 miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp );
2459 }
2460 if( isManaged() && decoration != NULL )
2461 decoration->iconChange();
2462 }
2463
2464void Client::getWindowProtocols()
2465 {
2466 Atom *p;
2467 int i,n;
2468
2469 Pdeletewindow = 0;
2470 Ptakefocus = 0;
2471 Ptakeactivity = 0;
2472 Pcontexthelp = 0;
2473 Pping = 0;
2474
2475 if (XGetWMProtocols(tqt_xdisplay(), window(), &p, &n))
2476 {
2477 for (i = 0; i < n; i++)
2478 if (p[i] == atoms->wm_delete_window)
2479 Pdeletewindow = 1;
2480 else if (p[i] == atoms->wm_take_focus)
2481 Ptakefocus = 1;
2482 else if (p[i] == atoms->net_wm_take_activity)
2483 Ptakeactivity = 1;
2484 else if (p[i] == atoms->net_wm_context_help)
2485 Pcontexthelp = 1;
2486 else if (p[i] == atoms->net_wm_ping)
2487 Pping = 1;
2488 if (n>0)
2489 XFree(p);
2490 }
2491 }
2492
2493static int nullErrorHandler(Display *, XErrorEvent *)
2494 {
2495 return 0;
2496 }
2497
2501TQCString Client::staticWindowRole(WId w)
2502 {
2503 return getStringProperty(w, tqt_window_role).lower();
2504 }
2505
2509TQCString Client::staticSessionId(WId w)
2510 {
2511 return getStringProperty(w, tqt_sm_client_id);
2512 }
2513
2517TQCString Client::staticWmCommand(WId w)
2518 {
2519 return getStringProperty(w, XA_WM_COMMAND, ' ');
2520 }
2521
2525Window Client::staticWmClientLeader(WId w)
2526 {
2527 Atom type;
2528 int format, status;
2529 unsigned long nitems = 0;
2530 unsigned long extra = 0;
2531 unsigned char *data = 0;
2532 Window result = w;
2533 XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler);
2534 status = XGetWindowProperty( tqt_xdisplay(), w, atoms->wm_client_leader, 0, 10000,
2535 FALSE, XA_WINDOW, &type, &format,
2536 &nitems, &extra, &data );
2537 XSetErrorHandler(oldHandler);
2538 if (status == Success )
2539 {
2540 if (data && nitems > 0)
2541 result = *((Window*) data);
2542 XFree(data);
2543 }
2544 return result;
2545 }
2546
2547
2548void Client::getWmClientLeader()
2549 {
2550 wmClientLeaderWin = staticWmClientLeader(window());
2551 }
2552
2557TQCString Client::sessionId()
2558 {
2559 TQCString result = staticSessionId(window());
2560 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
2561 result = staticSessionId(wmClientLeaderWin);
2562 return result;
2563 }
2564
2569TQCString Client::wmCommand()
2570 {
2571 TQCString result = staticWmCommand(window());
2572 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
2573 result = staticWmCommand(wmClientLeaderWin);
2574 return result;
2575 }
2576
2577void Client::getWmClientMachine()
2578 {
2579 client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE);
2580 if( client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
2581 client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE);
2582 if( client_machine.isEmpty())
2583 client_machine = "localhost";
2584 }
2585
2590TQCString Client::wmClientMachine( bool use_localhost ) const
2591 {
2592 TQCString result = client_machine;
2593 if( use_localhost )
2594 { // special name for the local machine (localhost)
2595 if( result != "localhost" && isLocalMachine( result ))
2596 result = "localhost";
2597 }
2598 return result;
2599 }
2600
2605Window Client::wmClientLeader() const
2606 {
2607 if (wmClientLeaderWin)
2608 return wmClientLeaderWin;
2609 return window();
2610 }
2611
2612bool Client::wantsTabFocus() const
2613 {
2614 return ( isNormalWindow() || isDialog()) && wantsInput() && !skip_taskbar;
2615 }
2616
2617
2618bool Client::wantsInput() const
2619 {
2620 return rules()->checkAcceptFocus( input || Ptakefocus );
2621 }
2622
2623bool Client::isDesktop() const
2624 {
2625 return windowType() == NET::Desktop;
2626 }
2627
2628bool Client::isDock() const
2629 {
2630 return windowType() == NET::Dock;
2631 }
2632
2633bool Client::isTopMenu() const
2634 {
2635 return windowType() == NET::TopMenu;
2636 }
2637
2638
2639bool Client::isMenu() const
2640 {
2641 return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp.
2642 }
2643
2644bool Client::isToolbar() const
2645 {
2646 return windowType() == NET::Toolbar;
2647 }
2648
2649bool Client::isSplash() const
2650 {
2651 return windowType() == NET::Splash;
2652 }
2653
2654bool Client::isUtility() const
2655 {
2656 return windowType() == NET::Utility;
2657 }
2658
2659bool Client::isDialog() const
2660 {
2661 return windowType() == NET::Dialog;
2662 }
2663
2664bool Client::isNormalWindow() const
2665 {
2666 return windowType() == NET::Normal;
2667 }
2668
2669bool Client::isSpecialWindow() const
2670 {
2671 return isDesktop() || isDock() || isSplash() || isTopMenu()
2672 || isToolbar(); // TODO
2673 }
2674
2675NET::WindowType Client::windowType( bool direct, int supported_types ) const
2676 {
2677 NET::WindowType wt = info->windowType( supported_types );
2678 if( direct )
2679 return wt;
2680 NET::WindowType wt2 = rules()->checkType( wt );
2681 if( wt != wt2 )
2682 {
2683 wt = wt2;
2684 info->setWindowType( wt ); // force hint change
2685 }
2686 // hacks here
2687 if( wt == NET::Menu )
2688 {
2689 // ugly hack to support the times when NET::Menu meant NET::TopMenu
2690 // if it's as wide as the screen, not very high and has its upper-left
2691 // corner a bit above the screen's upper-left cornet, it's a topmenu
2692 if( x() == 0 && y() < 0 && y() > -10 && height() < 100
2693 && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 )
2694 wt = NET::TopMenu;
2695 }
2696 // TODO change this to rule
2697 const char* const oo_prefix = "openoffice.org"; // TQCString has no startsWith()
2698 // oo_prefix is lowercase, because resourceClass() is forced to be lowercase
2699 if( tqstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog )
2700 wt = NET::Normal; // see bug #66065
2701 if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec
2702 wt = isTransient() ? NET::Dialog : NET::Normal;
2703 return wt;
2704 }
2705
2710void Client::setCursor( Position m )
2711 {
2712 if( !isResizable() || isShade())
2713 {
2714 m = PositionCenter;
2715 }
2716 switch ( m )
2717 {
2718 case PositionTopLeft:
2719 case PositionBottomRight:
2720 setCursor( TQt::sizeFDiagCursor );
2721 break;
2722 case PositionBottomLeft:
2723 case PositionTopRight:
2724 setCursor( TQt::sizeBDiagCursor );
2725 break;
2726 case PositionTop:
2727 case PositionBottom:
2728 setCursor( TQt::sizeVerCursor );
2729 break;
2730 case PositionLeft:
2731 case PositionRight:
2732 setCursor( TQt::sizeHorCursor );
2733 break;
2734 default:
2735 if( buttonDown && isMovable())
2736 setCursor( TQt::sizeAllCursor );
2737 else
2738 setCursor( TQt::arrowCursor );
2739 break;
2740 }
2741 }
2742
2743// TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit?
2744// TRANSLATION: TODO: have a checkCursor() function, which is called both in manage() and in cases where the cursor might change
2745void Client::setCursor( const TQCursor& c )
2746 {
2747 if( c.handle() == cursor.handle())
2748 return;
2749 cursor = c;
2750 if( decoration != NULL )
2751 decoration->widget()->setCursor( cursor );
2752 XDefineCursor( tqt_xdisplay(), frameId(), cursor.handle());
2753 }
2754
2755Client::Position Client::mousePosition( const TQPoint& p ) const
2756 {
2757 if( decoration != NULL )
2758 return decoration->mousePosition( p );
2759 return PositionCenter;
2760 }
2761
2762void Client::updateAllowedActions( bool force )
2763 {
2764 if( !isManaged() && !force )
2765 return;
2766 unsigned long old_allowed_actions = allowed_actions;
2767 allowed_actions = 0;
2768 if( isMovable())
2769 allowed_actions |= NET::ActionMove;
2770 if( isResizable())
2771 allowed_actions |= NET::ActionResize;
2772 if( isMinimizable())
2773 allowed_actions |= NET::ActionMinimize;
2774 if( isShadeable())
2775 allowed_actions |= NET::ActionShade;
2776 // sticky state not supported
2777 if( isMaximizable())
2778 allowed_actions |= NET::ActionMax;
2779 if( userCanSetFullScreen())
2780 allowed_actions |= NET::ActionFullScreen;
2781 allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.)
2782 if( isCloseable())
2783 allowed_actions |= NET::ActionClose;
2784 if( old_allowed_actions == allowed_actions )
2785 return;
2786 // TODO this could be delayed and compressed - it's only for pagers etc. anyway
2787 info->setAllowedActions( allowed_actions );
2788 // TODO this should also tell the decoration, so that it can update the buttons
2789 }
2790
2791void Client::autoRaise()
2792 {
2793 workspace()->raiseClient( this );
2794 cancelAutoRaise();
2795 }
2796
2797void Client::cancelAutoRaise()
2798 {
2799 delete autoRaiseTimer;
2800 autoRaiseTimer = 0;
2801 }
2802
2803void Client::setOpacity(bool translucent, uint opacity)
2804 {
2805 if (isDesktop())
2806 return; // xcompmgr does not like non solid desktops and the user could set it accidently by mouse scrolling
2807// tqWarning("setting opacity for %d",tqt_xdisplay());
2808 //rule out activated translulcency with 100% opacity
2809 if (!translucent || opacity == 0xFFFFFFFF)
2810 {
2811 opacity_ = 0xFFFFFFFF;
2812 XDeleteProperty (tqt_xdisplay(), frameId(), atoms->net_wm_window_opacity);
2813 XDeleteProperty (tqt_xdisplay(), window(), atoms->net_wm_window_opacity); // ??? frameId() is necessary for visible changes, window() is the winId() that would be set by apps - we set both to be sure the app knows what's currently displayd
2814 }
2815 else{
2816 if(opacity == opacity_)
2817 return;
2818 opacity_ = opacity;
2819 long data = opacity; // 32bit XChangeProperty needs long
2820 XChangeProperty(tqt_xdisplay(), frameId(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
2821 XChangeProperty(tqt_xdisplay(), window(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
2822 }
2823 }
2824
2825void Client::setShadowSize(uint shadowSize)
2826 {
2827 // ignoring all individual settings - if we control a window, we control it's shadow
2828 // TODO somehow handle individual settings for docks (besides custom sizes)
2829 long data = shadowSize;
2830 XChangeProperty(tqt_xdisplay(), frameId(), atoms->net_wm_window_shadow, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
2831 }
2832
2833void Client::updateOpacity()
2834// extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows)
2835 {
2836 if (!(isNormalWindow() || isDialog() || isUtility() )|| custom_opacity)
2837 return;
2838 if (isActive())
2839 {
2840 if( ruleOpacityActive() )
2841 setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active);
2842 else
2843 setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
2844 if (isBMP())
2845 // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P )
2846 {
2847 ClientList tmpGroupMembers = group()->members();
2848 ClientList activeGroupMembers;
2849 activeGroupMembers.append(this);
2850 tmpGroupMembers.remove(this);
2851 ClientList::Iterator it = tmpGroupMembers.begin();
2852 while (it != tmpGroupMembers.end())
2853 // search for next attached and not activated client and repeat if found
2854 {
2855 if ((*it) != this && (*it)->isBMP())
2856 // potential "to activate" client found
2857 {
2858// tqWarning("client found");
2859 if ((*it)->touches(this)) // first test, if the new client touches the just activated one
2860 {
2861// tqWarning("found client touches me");
2862 if( ruleOpacityActive() )
2863 (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active);
2864 else
2865 (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
2866// tqWarning("activated, search restarted (1)");
2867 (*it)->setShadowSize(options->activeWindowShadowSize);
2868 activeGroupMembers.append(*it);
2869 tmpGroupMembers.remove(it);
2870 it = tmpGroupMembers.begin(); // restart, search next client
2871 continue;
2872 }
2873 else
2874 { // pot. client does not touch c, so we have to search if it touches some other activated client
2875 bool found = false;
2876 for( ClientList::ConstIterator it2 = activeGroupMembers.begin(); it2 != activeGroupMembers.end(); it2++ )
2877 {
2878 if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2))
2879 {
2880// tqWarning("found client touches other active client");
2881 if( ruleOpacityActive() )
2882 (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active);
2883 else
2884 (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
2885 (*it)->setShadowSize(options->activeWindowShadowSize);
2886 activeGroupMembers.append(*it);
2887 tmpGroupMembers.remove(it);
2888 it = tmpGroupMembers.begin(); // reset potential client search
2889 found = true;
2890// tqWarning("activated, search restarted (2)");
2891 break; // skip this loop
2892 }
2893 }
2894 if (found) continue;
2895 }
2896 }
2897 it++;
2898 }
2899 }
2900 else if (isNormalWindow())
2901 // activate dependend minor windows as well
2902 {
2903 for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ )
2904 if ((*it)->isDialog() || (*it)->isUtility())
2905 {
2906 if( (*it)->ruleOpacityActive() )
2907 (*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive());
2908 else
2909 (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
2910 }
2911 }
2912 }
2913 else
2914 {
2915 if( ruleOpacityInactive() )
2916 setOpacity(rule_opacity_inactive < 0xFFFFFFFF, rule_opacity_inactive);
2917 else
2918 setOpacity(options->translucentInactiveWindows && !(keepAbove() && options->keepAboveAsActive),
2919 options->inactiveWindowOpacity);
2920 // deactivate dependend minor windows as well
2921 if (isBMP())
2922 // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P )
2923 {
2924 ClientList tmpGroupMembers = group()->members();
2925 ClientList inactiveGroupMembers;
2926 inactiveGroupMembers.append(this);
2927 tmpGroupMembers.remove(this);
2928 ClientList::Iterator it = tmpGroupMembers.begin();
2929 while ( it != tmpGroupMembers.end() )
2930 // search for next attached and not activated client and repeat if found
2931 {
2932 if ((*it) != this && (*it)->isBMP())
2933 // potential "to activate" client found
2934 {
2935// tqWarning("client found");
2936 if ((*it)->touches(this)) // first test, if the new client touches the just activated one
2937 {
2938// tqWarning("found client touches me");
2939 if( (*it)->ruleOpacityInactive() )
2940 (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive());
2941 else
2942 (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity);
2943 (*it)->setShadowSize(options->inactiveWindowShadowSize);
2944// tqWarning("deactivated, search restarted (1)");
2945 inactiveGroupMembers.append(*it);
2946 tmpGroupMembers.remove(it);
2947 it = tmpGroupMembers.begin(); // restart, search next client
2948 continue;
2949 }
2950 else // pot. client does not touch c, so we have to search if it touches some other activated client
2951 {
2952 bool found = false;
2953 for( ClientList::ConstIterator it2 = inactiveGroupMembers.begin(); it2 != inactiveGroupMembers.end(); it2++ )
2954 {
2955 if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2))
2956 {
2957// tqWarning("found client touches other inactive client");
2958 if( (*it)->ruleOpacityInactive() )
2959 (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive());
2960 else
2961 (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity);
2962 (*it)->setShadowSize(options->inactiveWindowShadowSize);
2963// tqWarning("deactivated, search restarted (2)");
2964 inactiveGroupMembers.append(*it);
2965 tmpGroupMembers.remove(it);
2966 it = tmpGroupMembers.begin(); // reset potential client search
2967 found = true;
2968 break; // skip this loop
2969 }
2970 }
2971 if (found) continue;
2972 }
2973 }
2974 it++;
2975 }
2976 }
2977 else if (isNormalWindow())
2978 {
2979 for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ )
2980 if ((*it)->isUtility()) //don't deactivate dialogs...
2981 {
2982 if( (*it)->ruleOpacityInactive() )
2983 (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive());
2984 else
2985 (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity);
2986 }
2987 }
2988 }
2989 }
2990
2991void Client::updateShadowSize()
2992// extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows)
2993 {
2994 if (!(isNormalWindow() || isDialog() || isUtility() ))
2995 return;
2996 if (isActive())
2997 setShadowSize(options->activeWindowShadowSize);
2998 else
2999 setShadowSize(options->inactiveWindowShadowSize);
3000 }
3001
3002uint Client::ruleOpacityInactive()
3003 {
3004 return rule_opacity_inactive;// != 0 ;
3005 }
3006
3007uint Client::ruleOpacityActive()
3008 {
3009 return rule_opacity_active;// != 0;
3010 }
3011
3012bool Client::getWindowOpacity() //query translucency settings from X, returns true if window opacity is set
3013 {
3014 unsigned char *data = 0;
3015 Atom actual;
3016 int format, result;
3017 unsigned long n, left;
3018 result = XGetWindowProperty(tqt_xdisplay(), window(), atoms->net_wm_window_opacity, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data);
3019 if (result == Success && data && format == 32 )
3020 {
3021 opacity_ = *reinterpret_cast< long* >( data );
3022 custom_opacity = true;
3023// setOpacity(opacity_ < 0xFFFFFFFF, opacity_);
3024 XFree ((char*)data);
3025 return TRUE;
3026 }
3027 return FALSE;
3028 }
3029
3030void Client::setCustomOpacityFlag(bool custom)
3031 {
3032 custom_opacity = custom;
3033 }
3034
3035uint Client::opacity()
3036 {
3037 return opacity_;
3038 }
3039
3040int Client::opacityPercentage()
3041 {
3042 return int(100*((double)opacity_/0xffffffff));
3043 }
3044
3045bool Client::touches(const Client* c)
3046// checks if this client borders c, needed to test beep media player window state
3047 {
3048 if (y() == c->y() + c->height()) // this bottom to c
3049 return TRUE;
3050 if (y() + height() == c->y()) // this top to c
3051 return TRUE;
3052 if (x() == c->x() + c->width()) // this right to c
3053 return TRUE;
3054 if (x() + width() == c->x()) // this left to c
3055 return TRUE;
3056 return FALSE;
3057 }
3058
3059void Client::setDecoHashProperty(uint topHeight, uint rightWidth, uint bottomHeight, uint leftWidth)
3060{
3061 long data = (topHeight < 255 ? topHeight : 255) << 24 |
3062 (rightWidth < 255 ? rightWidth : 255) << 16 |
3063 (bottomHeight < 255 ? bottomHeight : 255) << 8 |
3064 (leftWidth < 255 ? leftWidth : 255);
3065 XChangeProperty(tqt_xdisplay(), frameId(), atoms->net_wm_window_decohash, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
3066}
3067
3068void Client::unsetDecoHashProperty()
3069{
3070 XDeleteProperty( tqt_xdisplay(), frameId(), atoms->net_wm_window_decohash);
3071}
3072
3073#ifndef NDEBUG
3074kdbgstream& operator<<( kdbgstream& stream, const Client* cl )
3075 {
3076 if( cl == NULL )
3077 return stream << "\'NULL_CLIENT\'";
3078 return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'";
3079 }
3080kdbgstream& operator<<( kdbgstream& stream, const ClientList& list )
3081 {
3082 stream << "LIST:(";
3083 bool first = true;
3084 for( ClientList::ConstIterator it = list.begin();
3085 it != list.end();
3086 ++it )
3087 {
3088 if( !first )
3089 stream << ":";
3090 first = false;
3091 stream << *it;
3092 }
3093 stream << ")";
3094 return stream;
3095 }
3096kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list )
3097 {
3098 stream << "LIST:(";
3099 bool first = true;
3100 for( ConstClientList::ConstIterator it = list.begin();
3101 it != list.end();
3102 ++it )
3103 {
3104 if( !first )
3105 stream << ":";
3106 first = false;
3107 stream << *it;
3108 }
3109 stream << ")";
3110 return stream;
3111 }
3112#endif
3113
3114TQPixmap * twin_get_menu_pix_hack()
3115 {
3116 static TQPixmap p;
3117 if ( p.isNull() )
3118 p = SmallIcon( "bx2" );
3119 return &p;
3120 }
3121
3122} // namespace
3123
3124#include "client.moc"
KWinInternal::Client::desktop
int desktop() const
Definition: client.h:751
KWinInternal::Client::showContextHelp
void showContextHelp()
Definition: client.cpp:2278
KWinInternal::Client::staticWmClientLeader
static Window staticWmClientLeader(WId)
Definition: client.cpp:2525
KWinInternal::Client::sessionId
TQCString sessionId()
Definition: client.cpp:2557
KWinInternal::Client::providesContextHelp
bool providesContextHelp() const
Definition: client.cpp:2264
KWinInternal::Client::killWindow
void killWindow()
Definition: client.cpp:1765
KWinInternal::Client::setActive
void setActive(bool, bool updateOpacity=true)
Definition: activation.cpp:856
KWinInternal::Client::isMovable
bool isMovable() const
Definition: geometry.cpp:1649
KWinInternal::Client::isMinimizable
bool isMinimizable() const
Definition: client.cpp:630
KWinInternal::Client::keepAbove
bool keepAbove() const
Definition: client.cpp:663
KWinInternal::Client::Client
Client(Workspace *ws)
Definition: client.cpp:94
KWinInternal::Client::minimize
void minimize(bool avoid_animation=false)
Definition: client.cpp:673
KWinInternal::Client::staticSessionId
static TQCString staticSessionId(WId)
Definition: client.cpp:2509
KWinInternal::Client::wmClientMachine
TQCString wmClientMachine(bool use_localhost) const
Definition: client.cpp:2590
KWinInternal::Client::isResizable
bool isResizable() const
Definition: geometry.cpp:1665
KWinInternal::Client::staticWindowRole
static TQCString staticWindowRole(WId)
Definition: client.cpp:2501
KWinInternal::Client::staticWmCommand
static TQCString staticWmCommand(WId)
Definition: client.cpp:2517
KWinInternal::Client::updateUserTime
void updateUserTime(Time time=CurrentTime)
Definition: activation.cpp:674
KWinInternal::Client::isOnDesktop
bool isOnDesktop(int d) const
Definition: client.h:764
KWinInternal::Client::wmCommand
TQCString wmCommand()
Definition: client.cpp:2569
KWinInternal::Client::releaseWindow
void releaseWindow(bool on_shutdown=false)
Definition: client.cpp:221
KWinInternal::Client::closeWindow
void closeWindow()
Definition: client.cpp:1741
KWinInternal::Client::wmClientLeader
Window wmClientLeader() const
Definition: client.cpp:2605
KWinInternal::Client::caption
TQString caption(bool full=true) const
Definition: client.cpp:2377
KWinInternal::Client::move
void move(int x, int y, ForceGeometry_t force=NormalGeometrySet)
Definition: geometry.cpp:1831

twin

Skip menu "twin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

twin

Skip menu "twin"
  • kate
  • libkonq
  • twin
  •   lib
Generated for twin by doxygen 1.9.4
This website is maintained by Timothy Pearson.