client.cpp
00001 /***************************************************************** 00002 KWin - the KDE window manager 00003 This file is part of the KDE project. 00004 00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> 00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> 00007 00008 You can Freely distribute this program under the GNU General Public 00009 License. See the file "COPYING" for the exact licensing terms. 00010 ******************************************************************/ 00011 00012 #include "client.h" 00013 00014 #include <math.h> 00015 00016 #include <tqapplication.h> 00017 #include <tqpainter.h> 00018 #include <tqdatetime.h> 00019 #include <tqimage.h> 00020 #include <kprocess.h> 00021 #include <unistd.h> 00022 #include <kstandarddirs.h> 00023 #include <tqwhatsthis.h> 00024 #include <kwin.h> 00025 #include <kiconloader.h> 00026 #include <stdlib.h> 00027 00028 #include "bridge.h" 00029 #include "group.h" 00030 #include "workspace.h" 00031 #include "atoms.h" 00032 #include "notifications.h" 00033 #include "rules.h" 00034 00035 #include <X11/extensions/shape.h> 00036 00037 // put all externs before the namespace statement to allow the linker 00038 // to resolve them properly 00039 00040 extern Atom qt_wm_state; 00041 extern Atom qt_window_role; 00042 extern Atom qt_sm_client_id; 00043 00044 // wait 200 ms before drawing shadow after move/resize 00045 static const int SHADOW_DELAY = 200; 00046 00047 namespace KWinInternal 00048 { 00049 00050 /* TODO: Remove this once X has real translucency. 00051 * 00052 * A list of the regions covered by all shadows and the Clients to which they 00053 * belong. Used to redraw shadows when a window overlapping or underlying a 00054 * shadow is moved, resized, or hidden. 00055 */ 00056 struct ShadowRegion 00057 { 00058 TQRegion region; 00059 Client *client; 00060 }; 00061 static TQValueList<ShadowRegion> shadowRegions; 00062 00063 /* 00064 00065 Creating a client: 00066 - only by calling Workspace::createClient() 00067 - it creates a new client and calls manage() for it 00068 00069 Destroying a client: 00070 - destroyClient() - only when the window itself has been destroyed 00071 - releaseWindow() - the window is kept, only the client itself is destroyed 00072 00073 */ 00074 00075 00087 Client::Client( Workspace *ws ) 00088 : TQObject( NULL ), 00089 client( None ), 00090 wrapper( None ), 00091 frame( None ), 00092 decoration( NULL ), 00093 wspace( ws ), 00094 bridge( new Bridge( this )), 00095 move_faked_activity( false ), 00096 move_resize_grab_window( None ), 00097 transient_for( NULL ), 00098 transient_for_id( None ), 00099 original_transient_for_id( None ), 00100 in_group( NULL ), 00101 window_group( None ), 00102 in_layer( UnknownLayer ), 00103 ping_timer( NULL ), 00104 process_killer( NULL ), 00105 user_time( CurrentTime ), // not known yet 00106 allowed_actions( 0 ), 00107 postpone_geometry_updates( 0 ), 00108 pending_geometry_update( false ), 00109 shade_geometry_change( false ), 00110 border_left( 0 ), 00111 border_right( 0 ), 00112 border_top( 0 ), 00113 border_bottom( 0 ), 00114 opacity_( 0 ), 00115 demandAttentionKNotifyTimer( NULL ) 00116 // SELI do all as initialization 00117 { 00118 autoRaiseTimer = 0; 00119 shadeHoverTimer = 0; 00120 00121 shadowDelayTimer = new TQTimer(this); 00122 opacityCache = &activeOpacityCache; 00123 shadowAfterClient = NULL; 00124 shadowWidget = NULL; 00125 shadowMe = true; 00126 connect(shadowDelayTimer, TQT_SIGNAL(timeout()), TQT_SLOT(drawShadow())); 00127 00128 // set the initial mapping state 00129 mapping_state = WithdrawnState; 00130 desk = 0; // no desktop yet 00131 00132 mode = PositionCenter; 00133 buttonDown = FALSE; 00134 moveResizeMode = FALSE; 00135 00136 info = NULL; 00137 00138 shade_mode = ShadeNone; 00139 active = FALSE; 00140 deleting = false; 00141 keep_above = FALSE; 00142 keep_below = FALSE; 00143 is_shape = FALSE; 00144 motif_noborder = false; 00145 motif_may_move = TRUE; 00146 motif_may_resize = TRUE; 00147 motif_may_close = TRUE; 00148 fullscreen_mode = FullScreenNone; 00149 skip_taskbar = FALSE; 00150 original_skip_taskbar = false; 00151 minimized = false; 00152 hidden = false; 00153 modal = false; 00154 noborder = false; 00155 user_noborder = false; 00156 urgency = false; 00157 ignore_focus_stealing = false; 00158 demands_attention = false; 00159 check_active_modal = false; 00160 00161 Pdeletewindow = 0; 00162 Ptakefocus = 0; 00163 Ptakeactivity = 0; 00164 Pcontexthelp = 0; 00165 Pping = 0; 00166 input = FALSE; 00167 skip_pager = FALSE; 00168 00169 max_mode = MaximizeRestore; 00170 maxmode_restore = MaximizeRestore; 00171 00172 cmap = None; 00173 00174 frame_geometry = TQRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) 00175 client_size = TQSize( 100, 100 ); 00176 custom_opacity = false; 00177 rule_opacity_active = 0;; //translucency rules 00178 rule_opacity_inactive = 0; //dito. 00179 00180 // SELI initialize xsizehints?? 00181 } 00182 00186 Client::~Client() 00187 { 00188 assert(!moveResizeMode); 00189 assert( client == None ); 00190 assert( frame == None && wrapper == None ); 00191 assert( decoration == NULL ); 00192 assert( postpone_geometry_updates == 0 ); 00193 assert( !check_active_modal ); 00194 delete info; 00195 delete bridge; 00196 } 00197 00198 // use destroyClient() or releaseWindow(), Client instances cannot be deleted directly 00199 void Client::deleteClient( Client* c, allowed_t ) 00200 { 00201 delete c; 00202 } 00203 00207 void Client::releaseWindow( bool on_shutdown ) 00208 { 00209 assert( !deleting ); 00210 deleting = true; 00211 workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules 00212 StackingUpdatesBlocker blocker( workspace()); 00213 if (!custom_opacity) setOpacity(FALSE); 00214 if (moveResizeMode) 00215 leaveMoveResize(); 00216 removeShadow(); 00217 drawIntersectingShadows(); 00218 finishWindowRules(); 00219 ++postpone_geometry_updates; 00220 // grab X during the release to make removing of properties, setting to withdrawn state 00221 // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2) 00222 grabXServer(); 00223 setMappingState( WithdrawnState ); 00224 setModal( false ); // otherwise its mainwindow wouldn't get focus 00225 hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags) 00226 if( !on_shutdown ) 00227 workspace()->clientHidden( this ); 00228 XUnmapWindow( qt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect 00229 destroyDecoration(); 00230 cleanGrouping(); 00231 if( !on_shutdown ) 00232 { 00233 workspace()->removeClient( this, Allowed ); 00234 // only when the window is being unmapped, not when closing down KWin 00235 // (NETWM sections 5.5,5.7) 00236 info->setDesktop( 0 ); 00237 desk = 0; 00238 info->setState( 0, info->state()); // reset all state flags 00239 } 00240 XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_user_creation_time); 00241 XDeleteProperty( qt_xdisplay(), client, atoms->net_frame_extents ); 00242 XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_frame_strut ); 00243 XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y()); 00244 XRemoveFromSaveSet( qt_xdisplay(), client ); 00245 XSelectInput( qt_xdisplay(), client, NoEventMask ); 00246 if( on_shutdown ) 00247 { // map the window, so it can be found after another WM is started 00248 XMapWindow( qt_xdisplay(), client ); 00249 // TODO preserve minimized, shaded etc. state? 00250 } 00251 else 00252 { 00253 // Make sure it's not mapped if the app unmapped it (#65279). The app 00254 // may do map+unmap before we initially map the window by calling rawShow() from manage(). 00255 XUnmapWindow( qt_xdisplay(), client ); 00256 } 00257 client = None; 00258 XDestroyWindow( qt_xdisplay(), wrapper ); 00259 wrapper = None; 00260 XDestroyWindow( qt_xdisplay(), frame ); 00261 frame = None; 00262 --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry 00263 checkNonExistentClients(); 00264 deleteClient( this, Allowed ); 00265 ungrabXServer(); 00266 } 00267 00268 // like releaseWindow(), but this one is called when the window has been already destroyed 00269 // (e.g. the application closed it) 00270 void Client::destroyClient() 00271 { 00272 assert( !deleting ); 00273 deleting = true; 00274 workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules 00275 StackingUpdatesBlocker blocker( workspace()); 00276 if (moveResizeMode) 00277 leaveMoveResize(); 00278 removeShadow(); 00279 drawIntersectingShadows(); 00280 finishWindowRules(); 00281 ++postpone_geometry_updates; 00282 setModal( false ); 00283 hidden = true; // so that it's not considered visible anymore 00284 workspace()->clientHidden( this ); 00285 destroyDecoration(); 00286 cleanGrouping(); 00287 workspace()->removeClient( this, Allowed ); 00288 client = None; // invalidate 00289 XDestroyWindow( qt_xdisplay(), wrapper ); 00290 wrapper = None; 00291 XDestroyWindow( qt_xdisplay(), frame ); 00292 frame = None; 00293 --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry 00294 checkNonExistentClients(); 00295 deleteClient( this, Allowed ); 00296 } 00297 00298 void Client::updateDecoration( bool check_workspace_pos, bool force ) 00299 { 00300 if( !force && (( decoration == NULL && noBorder()) 00301 || ( decoration != NULL && !noBorder()))) 00302 return; 00303 bool do_show = false; 00304 postponeGeometryUpdates( true ); 00305 if( force ) 00306 destroyDecoration(); 00307 if( !noBorder()) 00308 { 00309 setMask( TQRegion()); // reset shape mask 00310 decoration = workspace()->createDecoration( bridge ); 00311 // TODO check decoration's minimum size? 00312 decoration->init(); 00313 decoration->widget()->installEventFilter( this ); 00314 XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 ); 00315 decoration->widget()->lower(); 00316 decoration->borders( border_left, border_right, border_top, border_bottom ); 00317 options->onlyDecoTranslucent ? 00318 setDecoHashProperty(border_top, border_right, border_bottom, border_left): 00319 unsetDecoHashProperty(); 00320 int save_workarea_diff_x = workarea_diff_x; 00321 int save_workarea_diff_y = workarea_diff_y; 00322 move( calculateGravitation( false )); 00323 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); 00324 workarea_diff_x = save_workarea_diff_x; 00325 workarea_diff_y = save_workarea_diff_y; 00326 do_show = true; 00327 } 00328 else 00329 destroyDecoration(); 00330 if( check_workspace_pos ) 00331 checkWorkspacePosition(); 00332 postponeGeometryUpdates( false ); 00333 if( do_show ) 00334 decoration->widget()->show(); 00335 updateFrameExtents(); 00336 updateOpacityCache(); 00337 } 00338 00339 void Client::destroyDecoration() 00340 { 00341 if( decoration != NULL ) 00342 { 00343 delete decoration; 00344 decoration = NULL; 00345 TQPoint grav = calculateGravitation( true ); 00346 border_left = border_right = border_top = border_bottom = 0; 00347 setMask( TQRegion()); // reset shape mask 00348 int save_workarea_diff_x = workarea_diff_x; 00349 int save_workarea_diff_y = workarea_diff_y; 00350 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); 00351 move( grav ); 00352 workarea_diff_x = save_workarea_diff_x; 00353 workarea_diff_y = save_workarea_diff_y; 00354 } 00355 } 00356 00357 void Client::checkBorderSizes() 00358 { 00359 if( decoration == NULL ) 00360 return; 00361 int new_left, new_right, new_top, new_bottom; 00362 decoration->borders( new_left, new_right, new_top, new_bottom ); 00363 if( new_left == border_left && new_right == border_right 00364 && new_top == border_top && new_bottom == border_bottom ) 00365 return; 00366 GeometryUpdatesPostponer blocker( this ); 00367 move( calculateGravitation( true )); 00368 border_left = new_left; 00369 border_right = new_right; 00370 border_top = new_top; 00371 border_bottom = new_bottom; 00372 if (border_left != new_left || 00373 border_right != new_right || 00374 border_top != new_top || 00375 border_bottom != new_bottom) 00376 options->onlyDecoTranslucent ? 00377 setDecoHashProperty(new_top, new_right, new_bottom, new_left): 00378 unsetDecoHashProperty(); 00379 move( calculateGravitation( false )); 00380 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); 00381 checkWorkspacePosition(); 00382 } 00383 00384 void Client::detectNoBorder() 00385 { 00386 if( Shape::hasShape( window())) 00387 { 00388 noborder = true; 00389 return; 00390 } 00391 switch( windowType()) 00392 { 00393 case NET::Desktop : 00394 case NET::Dock : 00395 case NET::TopMenu : 00396 case NET::Splash : 00397 noborder = true; 00398 break; 00399 case NET::Unknown : 00400 case NET::Normal : 00401 case NET::Toolbar : 00402 case NET::Menu : 00403 case NET::Dialog : 00404 case NET::Utility : 00405 noborder = false; 00406 break; 00407 default: 00408 assert( false ); 00409 } 00410 // NET::Override is some strange beast without clear definition, usually 00411 // just meaning "noborder", so let's treat it only as such flag, and ignore it as 00412 // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it) 00413 if( info->windowType( SUPPORTED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override ) 00414 noborder = true; 00415 } 00416 00417 void Client::detectShapable() 00418 { 00419 if( Shape::hasShape( window())) 00420 return; 00421 switch( windowType()) 00422 { 00423 case NET::Desktop : 00424 case NET::Dock : 00425 case NET::TopMenu : 00426 case NET::Splash : 00427 break; 00428 case NET::Unknown : 00429 case NET::Normal : 00430 case NET::Toolbar : 00431 case NET::Menu : 00432 case NET::Dialog : 00433 case NET::Utility : 00434 setShapable(FALSE); 00435 break; 00436 default: 00437 assert( false ); 00438 } 00439 } 00440 00441 void Client::updateFrameExtents() 00442 { 00443 NETStrut strut; 00444 strut.left = border_left; 00445 strut.right = border_right; 00446 strut.top = border_top; 00447 strut.bottom = border_bottom; 00448 info->setFrameExtents( strut ); 00449 } 00450 00451 // Resizes the decoration, and makes sure the decoration widget gets resize event 00452 // even if the size hasn't changed. This is needed to make sure the decoration 00453 // re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes, 00454 // the decoration may turn on/off some borders, but the actual size 00455 // of the decoration stays the same). 00456 void Client::resizeDecoration( const TQSize& s ) 00457 { 00458 if( decoration == NULL ) 00459 return; 00460 TQSize oldsize = decoration->widget()->size(); 00461 decoration->resize( s ); 00462 if( oldsize == s ) 00463 { 00464 TQResizeEvent e( s, oldsize ); 00465 TQApplication::sendEvent( decoration->widget(), &e ); 00466 } 00467 if (!moveResizeMode && options->shadowEnabled(isActive())) 00468 { 00469 // If the user is manually resizing, let Client::leaveMoveResize() 00470 // decide when to redraw the shadow 00471 updateOpacityCache(); 00472 } 00473 } 00474 00475 bool Client::noBorder() const 00476 { 00477 return noborder || isFullScreen() || user_noborder || motif_noborder; 00478 } 00479 00480 bool Client::userCanSetNoBorder() const 00481 { 00482 return !noborder && !isFullScreen() && !isShade(); 00483 } 00484 00485 bool Client::isUserNoBorder() const 00486 { 00487 return user_noborder; 00488 } 00489 00490 void Client::setUserNoBorder( bool set ) 00491 { 00492 if( !userCanSetNoBorder()) 00493 return; 00494 set = rules()->checkNoBorder( set ); 00495 if( user_noborder == set ) 00496 return; 00497 user_noborder = set; 00498 updateDecoration( true, false ); 00499 updateWindowRules(); 00500 } 00501 00502 bool Client::isModalSystemNotification() const 00503 { 00504 unsigned char *data = 0; 00505 Atom actual; 00506 int format, result; 00507 unsigned long n, left; 00508 result = XGetWindowProperty(qt_xdisplay(), window(), atoms->net_wm_system_modal_notification, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data); 00509 if (result == Success && data != None && format == 32 ) 00510 { 00511 return TRUE; 00512 } 00513 return FALSE; 00514 } 00515 00516 void Client::updateShape() 00517 { 00518 // workaround for #19644 - shaped windows shouldn't have decoration 00519 if( shape() && !noBorder()) 00520 { 00521 noborder = true; 00522 updateDecoration( true ); 00523 } 00524 updateOpacityCache(); 00525 if ( shape() ) 00526 { 00527 XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding, 00528 clientPos().x(), clientPos().y(), 00529 window(), ShapeBounding, ShapeSet); 00530 setShapable(TRUE); 00531 } 00532 // !shape() mask setting is done in setMask() when the decoration 00533 // calls it or when the decoration is created/destroyed 00534 00535 if( Shape::version() >= 0x11 ) // 1.1, has input shape support 00536 { // There appears to be no way to find out if a window has input 00537 // shape set or not, so always propagate the input shape 00538 // (it's the same like the bounding shape by default). 00539 // Also, build the shape using a helper window, not directly 00540 // in the frame window, because the sequence set-shape-to-frame, 00541 // remove-shape-of-client, add-input-shape-of-client has the problem 00542 // that after the second step there's a hole in the input shape 00543 // until the real shape of the client is added and that can make 00544 // the window lose focus (which is a problem with mouse focus policies) 00545 static Window helper_window = None; 00546 if( helper_window == None ) 00547 helper_window = XCreateSimpleWindow( qt_xdisplay(), qt_xrootwin(), 00548 0, 0, 1, 1, 0, 0, 0 ); 00549 XResizeWindow( qt_xdisplay(), helper_window, width(), height()); 00550 XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, 0, 0, 00551 frameId(), ShapeBounding, ShapeSet ); 00552 XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, 00553 clientPos().x(), clientPos().y(), 00554 window(), ShapeBounding, ShapeSubtract ); 00555 XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, 00556 clientPos().x(), clientPos().y(), 00557 window(), ShapeInput, ShapeUnion ); 00558 XShapeCombineShape( qt_xdisplay(), frameId(), ShapeInput, 0, 0, 00559 helper_window, ShapeInput, ShapeSet ); 00560 } 00561 } 00562 00563 void Client::setMask( const TQRegion& reg, int mode ) 00564 { 00565 _mask = reg; 00566 if( reg.isNull()) 00567 XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, 00568 None, ShapeSet ); 00569 else if( mode == X::Unsorted ) 00570 XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, 00571 reg.handle(), ShapeSet ); 00572 else 00573 { 00574 TQMemArray< TQRect > rects = reg.rects(); 00575 XRectangle* xrects = new XRectangle[ rects.count() ]; 00576 for( unsigned int i = 0; 00577 i < rects.count(); 00578 ++i ) 00579 { 00580 xrects[ i ].x = rects[ i ].x(); 00581 xrects[ i ].y = rects[ i ].y(); 00582 xrects[ i ].width = rects[ i ].width(); 00583 xrects[ i ].height = rects[ i ].height(); 00584 } 00585 XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, 00586 xrects, rects.count(), ShapeSet, mode ); 00587 delete[] xrects; 00588 } 00589 updateShape(); 00590 } 00591 00592 TQRegion Client::mask() const 00593 { 00594 if( _mask.isEmpty()) 00595 return TQRegion( 0, 0, width(), height()); 00596 return _mask; 00597 } 00598 00599 void Client::setShapable(bool b) 00600 { 00601 long tmp = b?1:0; 00602 XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shapable, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &tmp, 1L); 00603 } 00604 00605 void Client::hideClient( bool hide ) 00606 { 00607 if( hidden == hide ) 00608 return; 00609 hidden = hide; 00610 updateVisibility(); 00611 } 00612 00616 bool Client::isMinimizable() const 00617 { 00618 if( isSpecialWindow()) 00619 return false; 00620 if( isModalSystemNotification()) 00621 return false; 00622 if( isTransient()) 00623 { // #66868 - let other xmms windows be minimized when the mainwindow is minimized 00624 bool shown_mainwindow = false; 00625 ClientList mainclients = mainClients(); 00626 for( ClientList::ConstIterator it = mainclients.begin(); 00627 it != mainclients.end(); 00628 ++it ) 00629 { 00630 if( (*it)->isShown( true )) 00631 shown_mainwindow = true; 00632 } 00633 if( !shown_mainwindow ) 00634 return true; 00635 } 00636 // this is here because kicker's taskbar doesn't provide separate entries 00637 // for windows with an explicitly given parent 00638 // TODO perhaps this should be redone 00639 if( transientFor() != NULL ) 00640 return false; 00641 if( !wantsTabFocus()) // SELI - NET::Utility? why wantsTabFocus() - skiptaskbar? ? 00642 return false; 00643 return true; 00644 } 00645 00649 bool Client::keepAbove() const 00650 { 00651 if( isModalSystemNotification()) 00652 return true; 00653 return keep_above; 00654 } 00655 00659 void Client::minimize( bool avoid_animation ) 00660 { 00661 if ( !isMinimizable() || isMinimized()) 00662 return; 00663 00664 if (isShade()) // NETWM restriction - KWindowInfo::isMinimized() == Hidden && !Shaded 00665 info->setState(0, NET::Shaded); 00666 00667 Notify::raise( Notify::Minimize ); 00668 00669 // SELI mainClients().isEmpty() ??? - and in unminimize() too 00670 if ( mainClients().isEmpty() && isOnCurrentDesktop() && isShown( true ) && !avoid_animation ) 00671 animateMinimizeOrUnminimize( true ); // was visible or shaded 00672 00673 minimized = true; 00674 00675 updateVisibility(); 00676 updateAllowedActions(); 00677 workspace()->updateMinimizedOfTransients( this ); 00678 updateWindowRules(); 00679 workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast ); 00680 } 00681 00682 void Client::unminimize( bool avoid_animation ) 00683 { 00684 if( !isMinimized()) 00685 return; 00686 00687 if (isShade()) // NETWM restriction - KWindowInfo::isMinimized() == Hidden && !Shaded 00688 info->setState(NET::Shaded, NET::Shaded); 00689 00690 Notify::raise( Notify::UnMinimize ); 00691 minimized = false; 00692 if( isOnCurrentDesktop() && isShown( true )) 00693 { 00694 if( mainClients().isEmpty() && !avoid_animation ) 00695 animateMinimizeOrUnminimize( FALSE ); 00696 } 00697 updateVisibility(); 00698 updateAllowedActions(); 00699 workspace()->updateMinimizedOfTransients( this ); 00700 updateWindowRules(); 00701 } 00702 00703 extern bool blockAnimation; 00704 00705 void Client::animateMinimizeOrUnminimize( bool minimize ) 00706 { 00707 if ( blockAnimation ) 00708 return; 00709 if ( !options->animateMinimize ) 00710 return; 00711 00712 if( decoration != NULL && decoration->animateMinimize( minimize )) 00713 return; // decoration did it 00714 00715 // the function is a bit tricky since it will ensure that an 00716 // animation action needs always the same time regardless of the 00717 // performance of the machine or the X-Server. 00718 00719 float lf,rf,tf,bf,step; 00720 00721 int speed = options->animateMinimizeSpeed; 00722 if ( speed > 10 ) 00723 speed = 10; 00724 if ( speed < 0 ) 00725 speed = 0; 00726 00727 step = 40. * (11 - speed ); 00728 00729 NETRect r = info->iconGeometry(); 00730 TQRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height ); 00731 if ( !icongeom.isValid() ) 00732 return; 00733 00734 TQPixmap pm = animationPixmap( minimize ? width() : icongeom.width() ); 00735 00736 TQRect before, after; 00737 if ( minimize ) 00738 { 00739 before = TQRect( x(), y(), width(), pm.height() ); 00740 after = TQRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); 00741 } 00742 else 00743 { 00744 before = TQRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); 00745 after = TQRect( x(), y(), width(), pm.height() ); 00746 } 00747 00748 lf = (after.left() - before.left())/step; 00749 rf = (after.right() - before.right())/step; 00750 tf = (after.top() - before.top())/step; 00751 bf = (after.bottom() - before.bottom())/step; 00752 00753 grabXServer(); 00754 00755 TQRect area = before; 00756 TQRect area2; 00757 TQPixmap pm2; 00758 00759 TQTime t; 00760 t.start(); 00761 float diff; 00762 00763 TQPainter p ( workspace()->desktopWidget() ); 00764 bool need_to_clear = FALSE; 00765 TQPixmap pm3; 00766 do 00767 { 00768 if (area2 != area) 00769 { 00770 pm = animationPixmap( area.width() ); 00771 pm2 = TQPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() ); 00772 p.drawPixmap( area.x(), area.y(), pm ); 00773 if ( need_to_clear ) 00774 { 00775 p.drawPixmap( area2.x(), area2.y(), pm3 ); 00776 need_to_clear = FALSE; 00777 } 00778 area2 = area; 00779 } 00780 XFlush(qt_xdisplay()); 00781 XSync( qt_xdisplay(), FALSE ); 00782 diff = t.elapsed(); 00783 if (diff > step) 00784 diff = step; 00785 area.setLeft(before.left() + int(diff*lf)); 00786 area.setRight(before.right() + int(diff*rf)); 00787 area.setTop(before.top() + int(diff*tf)); 00788 area.setBottom(before.bottom() + int(diff*bf)); 00789 if (area2 != area ) 00790 { 00791 if ( area2.intersects( area ) ) 00792 p.drawPixmap( area2.x(), area2.y(), pm2 ); 00793 else 00794 { // no overlap, we can clear later to avoid flicker 00795 pm3 = pm2; 00796 need_to_clear = TRUE; 00797 } 00798 } 00799 } while ( t.elapsed() < step); 00800 if (area2 == area || need_to_clear ) 00801 p.drawPixmap( area2.x(), area2.y(), pm2 ); 00802 00803 p.end(); 00804 ungrabXServer(); 00805 } 00806 00807 00811 TQPixmap Client::animationPixmap( int w ) 00812 { 00813 TQFont font = options->font(isActive()); 00814 TQFontMetrics fm( font ); 00815 TQPixmap pm( w, fm.lineSpacing() ); 00816 pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) ); 00817 TQPainter p( &pm ); 00818 p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() )); 00819 p.setFont(options->font(isActive())); 00820 p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() ); 00821 return pm; 00822 } 00823 00824 00825 bool Client::isShadeable() const 00826 { 00827 return !isSpecialWindow() && !noBorder(); 00828 } 00829 00830 void Client::setShade( ShadeMode mode ) 00831 { 00832 if( !isShadeable()) 00833 return; 00834 if( isModalSystemNotification()) 00835 return; 00836 mode = rules()->checkShade( mode ); 00837 if( shade_mode == mode ) 00838 return; 00839 bool was_shade = isShade(); 00840 ShadeMode was_shade_mode = shade_mode; 00841 shade_mode = mode; 00842 if( was_shade == isShade()) 00843 { 00844 if( decoration != NULL ) // decoration may want to update after e.g. hover-shade changes 00845 decoration->shadeChange(); 00846 return; // no real change in shaded state 00847 } 00848 00849 if( shade_mode == ShadeNormal ) 00850 { 00851 if ( isShown( true ) && isOnCurrentDesktop()) 00852 Notify::raise( Notify::ShadeUp ); 00853 } 00854 else if( shade_mode == ShadeNone ) 00855 { 00856 if( isShown( true ) && isOnCurrentDesktop()) 00857 Notify::raise( Notify::ShadeDown ); 00858 } 00859 00860 assert( decoration != NULL ); // noborder windows can't be shaded 00861 GeometryUpdatesPostponer blocker( this ); 00862 // decorations may turn off some borders when shaded 00863 decoration->borders( border_left, border_right, border_top, border_bottom ); 00864 00865 int as = options->animateShade? 10 : 1; 00866 // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere 00867 if ( isShade()) 00868 { // shade_mode == ShadeNormal 00869 // we're about to shade, texx xcompmgr to prepare 00870 long _shade = 1; 00871 XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); 00872 // shade 00873 int h = height(); 00874 shade_geometry_change = true; 00875 TQSize s( sizeForClientSize( TQSize( clientSize()))); 00876 s.setHeight( border_top + border_bottom ); 00877 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify 00878 XUnmapWindow( qt_xdisplay(), wrapper ); 00879 XUnmapWindow( qt_xdisplay(), client ); 00880 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); 00881 //as we hid the unmap event, xcompmgr didn't recognize the client wid has vanished, so we'll extra inform it 00882 //done xcompmgr workaround 00883 // FRAME repaint( FALSE ); 00884 // bool wasStaticContents = testWFlags( WStaticContents ); 00885 // setWFlags( WStaticContents ); 00886 int step = QMAX( 4, QABS( h - s.height() ) / as )+1; 00887 do 00888 { 00889 h -= step; 00890 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); 00891 resizeDecoration( TQSize( s.width(), h )); 00892 TQApplication::syncX(); 00893 } while ( h > s.height() + step ); 00894 // if ( !wasStaticContents ) 00895 // clearWFlags( WStaticContents ); 00896 plainResize( s ); 00897 shade_geometry_change = false; 00898 if( isActive()) 00899 { 00900 if( was_shade_mode == ShadeHover ) 00901 workspace()->activateNextClient( this ); 00902 else 00903 workspace()->focusToNull(); 00904 } 00905 // tell xcompmgr shade's done 00906 _shade = 2; 00907 XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); 00908 } 00909 else 00910 { 00911 int h = height(); 00912 shade_geometry_change = true; 00913 TQSize s( sizeForClientSize( clientSize())); 00914 // FRAME bool wasStaticContents = testWFlags( WStaticContents ); 00915 // setWFlags( WStaticContents ); 00916 int step = QMAX( 4, QABS( h - s.height() ) / as )+1; 00917 do 00918 { 00919 h += step; 00920 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); 00921 resizeDecoration( TQSize( s.width(), h )); 00922 // assume a border 00923 // we do not have time to wait for X to send us paint events 00924 // FRAME repaint( 0, h - step-5, width(), step+5, TRUE); 00925 TQApplication::syncX(); 00926 } while ( h < s.height() - step ); 00927 // if ( !wasStaticContents ) 00928 // clearWFlags( WStaticContents ); 00929 shade_geometry_change = false; 00930 plainResize( s ); 00931 if( shade_mode == ShadeHover || shade_mode == ShadeActivated ) 00932 setActive( TRUE ); 00933 XMapWindow( qt_xdisplay(), wrapperId()); 00934 XMapWindow( qt_xdisplay(), window()); 00935 XDeleteProperty (qt_xdisplay(), client, atoms->net_wm_window_shade); 00936 if (options->shadowEnabled(false)) 00937 { 00938 for (ClientList::ConstIterator it = transients().begin(); 00939 it != transients().end(); ++it) 00940 { 00941 (*it)->removeShadow(); 00942 (*it)->drawDelayedShadow(); 00943 } 00944 } 00945 00946 if ( isActive() ) 00947 workspace()->requestFocus( this ); 00948 } 00949 checkMaximizeGeometry(); 00950 info->setState( (isShade() && !isMinimized()) ? NET::Shaded : 0, NET::Shaded ); 00951 info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden ); 00952 updateVisibility(); 00953 updateAllowedActions(); 00954 workspace()->updateMinimizedOfTransients( this ); 00955 decoration->shadeChange(); 00956 updateWindowRules(); 00957 } 00958 00959 void Client::shadeHover() 00960 { 00961 setShade( ShadeHover ); 00962 cancelShadeHover(); 00963 } 00964 00965 void Client::cancelShadeHover() 00966 { 00967 delete shadeHoverTimer; 00968 shadeHoverTimer = 0; 00969 } 00970 00971 void Client::toggleShade() 00972 { 00973 // if the mode is ShadeHover or ShadeActive, cancel shade too 00974 setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone ); 00975 } 00976 00977 void Client::updateVisibility() 00978 { 00979 if( deleting ) 00980 return; 00981 bool show = true; 00982 if( hidden ) 00983 { 00984 setMappingState( IconicState ); 00985 info->setState( NET::Hidden, NET::Hidden ); 00986 setSkipTaskbar( true, false ); // also hide from taskbar 00987 rawHide(); 00988 show = false; 00989 } 00990 else 00991 { 00992 setSkipTaskbar( original_skip_taskbar, false ); 00993 } 00994 if( minimized ) 00995 { 00996 setMappingState( IconicState ); 00997 info->setState( NET::Hidden, NET::Hidden ); 00998 rawHide(); 00999 show = false; 01000 } 01001 if( show ) 01002 info->setState( 0, NET::Hidden ); 01003 if( !isOnCurrentDesktop()) 01004 { 01005 setMappingState( IconicState ); 01006 rawHide(); 01007 show = false; 01008 } 01009 if( show ) 01010 { 01011 bool belongs_to_desktop = false; 01012 for( ClientList::ConstIterator it = group()->members().begin(); 01013 it != group()->members().end(); 01014 ++it ) 01015 if( (*it)->isDesktop()) 01016 { 01017 belongs_to_desktop = true; 01018 break; 01019 } 01020 if( !belongs_to_desktop && workspace()->showingDesktop()) 01021 workspace()->resetShowingDesktop( true ); 01022 if( isShade()) 01023 setMappingState( IconicState ); 01024 else 01025 setMappingState( NormalState ); 01026 rawShow(); 01027 } 01028 } 01029 01030 void Client::setShadowed(bool shadowed) 01031 { 01032 bool wasShadowed; 01033 01034 wasShadowed = isShadowed(); 01035 shadowMe = options->shadowEnabled(isActive()) ? shadowed : false; 01036 01037 if (shadowMe) { 01038 if (!wasShadowed) 01039 drawShadow(); 01040 } 01041 else { 01042 if (wasShadowed) { 01043 removeShadow(); 01044 01045 if (!activeOpacityCache.isNull()) 01046 activeOpacityCache.resize(0); 01047 if (!inactiveOpacityCache.isNull()) 01048 inactiveOpacityCache.resize(0); 01049 } 01050 } 01051 } 01052 01053 void Client::updateOpacityCache() 01054 { 01055 if (!activeOpacityCache.isNull()) 01056 activeOpacityCache.resize(0); 01057 if (!inactiveOpacityCache.isNull()) 01058 inactiveOpacityCache.resize(0); 01059 01060 if (!moveResizeMode) { 01061 // If the user is manually resizing, let Client::finishMoveResize() 01062 // decide when to redraw the shadow 01063 removeShadow(); 01064 drawIntersectingShadows(); 01065 if (options->shadowEnabled(isActive())) 01066 drawDelayedShadow(); 01067 } 01068 } 01069 01074 void Client::drawIntersectingShadows() { 01075 //Client *reshadowClient; 01076 TQRegion region; 01077 //TQPtrList<Client> reshadowClients; 01078 TQValueList<Client *> reshadowClients; 01079 TQValueListIterator<ShadowRegion> it; 01080 TQValueListIterator<Client *> it2; 01081 01082 if (!options->shadowEnabled(false)) 01083 // No point in redrawing overlapping/overlapped shadows if only the 01084 // active window has a shadow. 01085 return; 01086 01087 region = shapeBoundingRegion; 01088 01089 // Generate list of Clients whose shadows need to be redrawn. That is, 01090 // those that are currently intersecting or intersected by other windows or 01091 // shadows. 01092 for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it) 01093 if ((isOnAllDesktops() || (*it).client->isOnCurrentDesktop()) && 01094 !(*it).region.intersect(region).isEmpty()) 01095 reshadowClients.append((*it).client); 01096 01097 // Redraw shadows for each of the Clients in the list generated above 01098 for (it2 = reshadowClients.begin(); it2 != reshadowClients.end(); 01099 ++it2) { 01100 (*it2)->removeShadow(); 01101 (*it2)->drawDelayedShadow(); 01102 } 01103 } 01104 01110 void Client::drawOverlappingShadows(bool waitForMe) 01111 { 01112 Client *aClient; 01113 TQRegion region; 01114 TQValueList<Client *> reshadowClients; 01115 ClientList stacking_order; 01116 ClientList::ConstIterator it; 01117 TQValueListIterator<ShadowRegion> it2; 01118 TQValueListIterator<Client *> it3; 01119 01120 if (!options->shadowEnabled(false)) 01121 // No point in redrawing overlapping/overlapped shadows if only the 01122 // active window has a shadow. 01123 return; 01124 01125 region = shapeBoundingRegion; 01126 01127 stacking_order = workspace()->stackingOrder(); 01128 for (it = stacking_order.fromLast(); it != stacking_order.end(); --it) { 01129 // Find the position of this window in the stacking order. 01130 if ((*it) == this) 01131 break; 01132 } 01133 ++it; 01134 while (it != stacking_order.end()) { 01135 if ((*it)->windowType() == NET::Dock) { 01136 // This function is only interested in windows whose shadows don't 01137 // have weird stacking rules. 01138 ++it; 01139 continue; 01140 } 01141 01142 // Generate list of Clients whose shadows need to be redrawn. That is, 01143 // those that are currently overlapping or overlapped by other windows 01144 // or shadows. The list should be in order from bottom to top in the 01145 // stacking order. 01146 for (it2 = shadowRegions.begin(); it2 != shadowRegions.end(); ++it2) { 01147 if ((*it2).client == (*it)) { 01148 if ((isOnAllDesktops() || (*it2).client->isOnCurrentDesktop()) 01149 && !(*it2).region.intersect(region).isEmpty()) 01150 reshadowClients.append((*it2).client); 01151 } 01152 } 01153 ++it; 01154 } 01155 01156 // Redraw shadows for each of the Clients in the list generated above 01157 for (it3 = reshadowClients.begin(); it3 != reshadowClients.end(); ++it3) { 01158 (*it3)->removeShadow(); 01159 if (it3 == reshadowClients.begin()) { 01160 if (waitForMe) 01161 (*it3)->drawShadowAfter(this); 01162 else 01163 (*it3)->drawDelayedShadow(); 01164 } 01165 else { 01166 --it3; 01167 aClient = (*it3); 01168 ++it3; 01169 (*it3)->drawShadowAfter(aClient); 01170 } 01171 } 01172 } 01173 01178 void Client::drawDelayedShadow() 01179 { 01180 shadowDelayTimer->stop(); 01181 shadowDelayTimer->start(SHADOW_DELAY, true); 01182 } 01183 01187 void Client::drawShadowAfter(Client *after) 01188 { 01189 shadowAfterClient = after; 01190 connect(after, TQT_SIGNAL(shadowDrawn()), TQT_SLOT(drawShadow())); 01191 } 01192 01196 void Client::drawShadow() 01197 { 01198 Window shadows[2]; 01199 XRectangle *shapes; 01200 int i, count, ordering; 01201 01202 // If we are waiting for another Client's shadow to be drawn, stop waiting now 01203 if (shadowAfterClient != NULL) { 01204 disconnect(shadowAfterClient, TQT_SIGNAL(shadowDrawn()), this, TQT_SLOT(drawShadow())); 01205 shadowAfterClient = NULL; 01206 } 01207 01208 if (!isOnCurrentDesktop()) 01209 return; 01210 01211 /* Store this window's ShapeBoundingRegion even if shadows aren't drawn for 01212 * this type of window. Otherwise, drawIntersectingShadows() won't update 01213 * properly when this window is moved/resized/hidden/closed. 01214 */ 01215 shapes = XShapeGetRectangles(qt_xdisplay(), frameId(), ShapeBounding, 01216 &count, &ordering); 01217 if (!shapes) 01218 // XShape extension not supported 01219 shapeBoundingRegion = TQRegion(x(), y(), width(), height()); 01220 else { 01221 shapeBoundingRegion = TQRegion(); 01222 for (i = 0; i < count; i++) { 01223 // Translate XShaped window into a TQRegion 01224 TQRegion shapeRectangle(shapes[i].x, shapes[i].y, shapes[i].width, 01225 shapes[i].height); 01226 shapeBoundingRegion += shapeRectangle; 01227 } 01228 if (isShade()) 01229 // Since XResize() doesn't change a window's XShape regions, ensure that 01230 // shapeBoundingRegion is not taller than the window's shaded height, 01231 // or the bottom shadow will appear to be missing 01232 shapeBoundingRegion &= TQRegion(0, 0, width(), height()); 01233 shapeBoundingRegion.translate(x(), y()); 01234 } 01235 01236 if (!isShadowed() || hidden || isMinimized() || 01237 maximizeMode() == MaximizeFull || 01238 !options->shadowWindowType(windowType())) { 01239 XFree(shapes); 01240 01241 // Tell whatever Clients are listening that this Client's shadow has been drawn. 01242 // It hasn't, but there's no sense waiting for something that won't happen. 01243 emit shadowDrawn(); 01244 01245 return; 01246 } 01247 01248 removeShadow(); 01249 01250 TQMemArray<QRgb> pixelData; 01251 TQPixmap shadowPixmap; 01252 TQRect shadow; 01253 TQRegion exposedRegion; 01254 ShadowRegion shadowRegion; 01255 int thickness, xOffset, yOffset; 01256 01257 thickness = options->shadowThickness(isActive()); 01258 xOffset = options->shadowXOffset(isActive()); 01259 yOffset = options->shadowYOffset(isActive()); 01260 opacityCache = active? &activeOpacityCache : &inactiveOpacityCache; 01261 01262 shadow.setRect(x() - thickness + xOffset, y() - thickness + yOffset, 01263 width() + thickness * 2, height() + thickness * 2); 01264 shadowPixmap.resize(shadow.size()); 01265 01266 // Create a fake drop-down shadow effect via blended Xwindows 01267 shadowWidget = new TQWidget(0, 0, (WFlags)(WStyle_Customize | WX11BypassWM)); 01268 shadowWidget->setGeometry(shadow); 01269 XSelectInput(qt_xdisplay(), shadowWidget->winId(), 01270 ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); 01271 shadowWidget->installEventFilter(this); 01272 01273 if (!shapes) { 01274 // XShape extension not supported 01275 exposedRegion = getExposedRegion(shapeBoundingRegion, shadow.x(), 01276 shadow.y(), shadow.width(), shadow.height(), thickness, 01277 xOffset, yOffset); 01278 shadowRegion.region = exposedRegion; 01279 shadowRegion.client = this; 01280 shadowRegions.append(shadowRegion); 01281 01282 if (opacityCache->isNull()) 01283 imposeRegionShadow(shadowPixmap, shapeBoundingRegion, 01284 exposedRegion, thickness, 01285 options->shadowOpacity(isActive())); 01286 else 01287 imposeCachedShadow(shadowPixmap, exposedRegion); 01288 } 01289 else { 01290 TQMemArray<TQRect> exposedRects; 01291 TQMemArray<TQRect>::Iterator it, itEnd; 01292 XRectangle *shadowShapes; 01293 01294 exposedRegion = getExposedRegion(shapeBoundingRegion, shadow.x(), 01295 shadow.y(), shadow.width(), shadow.height(), thickness, 01296 xOffset, yOffset); 01297 shadowRegion.region = exposedRegion; 01298 shadowRegion.client = this; 01299 shadowRegions.append(shadowRegion); 01300 01301 // XShape the shadow 01302 exposedRects = exposedRegion.rects(); 01303 i = 0; 01304 itEnd = exposedRects.end(); 01305 shadowShapes = new XRectangle[exposedRects.count()]; 01306 for (it = exposedRects.begin(); it != itEnd; ++it) { 01307 shadowShapes[i].x = (*it).x(); 01308 shadowShapes[i].y = (*it).y(); 01309 shadowShapes[i].width = (*it).width(); 01310 shadowShapes[i].height = (*it).height(); 01311 i++; 01312 } 01313 XShapeCombineRectangles(qt_xdisplay(), shadowWidget->winId(), 01314 ShapeBounding, -x() + thickness - xOffset, 01315 -y() + thickness - yOffset, shadowShapes, i, ShapeSet, 01316 Unsorted); 01317 delete [] shadowShapes; 01318 01319 if (opacityCache->isNull()) 01320 imposeRegionShadow(shadowPixmap, shapeBoundingRegion, 01321 exposedRegion, thickness, 01322 options->shadowOpacity(isActive())); 01323 else 01324 imposeCachedShadow(shadowPixmap, exposedRegion); 01325 } 01326 01327 XFree(shapes); 01328 01329 // Set the background pixmap 01330 //shadowPixmap.convertFromImage(shadowImage); 01331 shadowWidget->setErasePixmap(shadowPixmap); 01332 01333 // Restack shadows under this window so that shadows drawn for a newly 01334 // focused (but not raised) window don't overlap any windows above it. 01335 if (isDock()) { 01336 ClientList stacking_order = workspace()->stackingOrder(); 01337 for (ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) 01338 if ((*it)->isDesktop()) 01339 { 01340 ++it; 01341 shadows[0] = (*it)->frameId(); 01342 shadows[1] = shadowWidget->winId(); 01343 } 01344 } 01345 else { 01346 shadows[0] = frameId(); 01347 if (shadowWidget != NULL) 01348 shadows[1] = shadowWidget->winId(); 01349 } 01350 01351 XRestackWindows(qt_xdisplay(), shadows, 2); 01352 01353 // Don't use TQWidget::show() so we don't confuse QEffects, thus causing 01354 // broken focus. 01355 XMapWindow(qt_xdisplay(), shadowWidget->winId()); 01356 01357 // Tell whatever Clients are listening that this Client's shadow has been drawn. 01358 emit shadowDrawn(); 01359 } 01360 01364 void Client::removeShadow() 01365 { 01366 TQValueList<ShadowRegion>::Iterator it; 01367 01368 shadowDelayTimer->stop(); 01369 01370 if (shadowWidget != NULL) { 01371 for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it) 01372 if ((*it).client == this) { 01373 shadowRegions.remove(it); 01374 break; 01375 } 01376 delete shadowWidget; 01377 shadowWidget = NULL; 01378 } 01379 } 01380 01385 TQRegion Client::getExposedRegion(TQRegion occludedRegion, int x, int y, int w, 01386 int h, int thickness, int xOffset, int yOffset) 01387 { 01388 TQRegion exposedRegion; 01389 01390 exposedRegion = TQRegion(x, y, w, h); 01391 exposedRegion -= occludedRegion; 01392 01393 if (thickness > 0) { 01394 // Limit exposedRegion to include only where a shadow of the specified 01395 // thickness will be drawn 01396 TQMemArray<TQRect> occludedRects; 01397 TQMemArray<TQRect>::Iterator it, itEnd; 01398 TQRegion shadowRegion; 01399 01400 occludedRects = occludedRegion.rects(); 01401 itEnd = occludedRects.end(); 01402 for (it = occludedRects.begin(); it != itEnd; ++it) { 01403 // Expand each of the occluded region's shape rectangles to contain 01404 // where a shadow of the specified thickness will be drawn. Create 01405 // a new TQRegion that contains the expanded occluded region 01406 it->setTop(it->top() - thickness + yOffset); 01407 it->setLeft(it->left() - thickness + xOffset); 01408 it->setRight(it->right() + thickness + xOffset); 01409 it->setBottom(it->bottom() + thickness + yOffset); 01410 shadowRegion += TQRegion(*it); 01411 } 01412 exposedRegion -= exposedRegion - shadowRegion; 01413 } 01414 01415 return exposedRegion; 01416 } 01417 01421 void Client::imposeCachedShadow(TQPixmap &pixmap, TQRegion exposed) 01422 { 01423 QRgb pixel; 01424 double opacity; 01425 int red, green, blue, pixelRed, pixelGreen, pixelBlue; 01426 int subW, subH, w, h, x, y, zeroX, zeroY; 01427 TQImage image; 01428 TQMemArray<TQRect>::Iterator it, itEnd; 01429 TQMemArray<TQRect> rectangles; 01430 TQPixmap subPixmap; 01431 Window rootWindow; 01432 int thickness, windowX, windowY, xOffset, yOffset; 01433 01434 rectangles = exposed.rects(); 01435 rootWindow = qt_xrootwin(); 01436 thickness = options->shadowThickness(isActive()); 01437 windowX = this->x(); 01438 windowY = this->y(); 01439 xOffset = options->shadowXOffset(isActive()); 01440 yOffset = options->shadowYOffset(isActive()); 01441 options->shadowColour(isActive()).rgb(&red, &green, &blue); 01442 w = pixmap.width(); 01443 h = pixmap.height(); 01444 01445 itEnd = rectangles.end(); 01446 for (it = rectangles.begin(); it != itEnd; ++it) { 01447 subW = (*it).width(); 01448 subH = (*it).height(); 01449 subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(), 01450 subW, subH); 01451 zeroX = (*it).x() - windowX + thickness - xOffset; 01452 zeroY = (*it).y() - windowY + thickness - yOffset; 01453 image = subPixmap.convertToImage(); 01454 01455 for (x = 0; x < subW; x++) { 01456 for (y = 0; y < subH; y++) { 01457 opacity = (*(opacityCache))[(zeroY + y) * w + zeroX + x]; 01458 pixel = image.pixel(x, y); 01459 pixelRed = tqRed(pixel); 01460 pixelGreen = tqGreen(pixel); 01461 pixelBlue = tqBlue(pixel); 01462 image.setPixel(x, y, 01463 tqRgb((int)(pixelRed + (red - pixelRed) * opacity), 01464 (int)(pixelGreen + (green - pixelGreen) * opacity), 01465 (int)(pixelBlue + (blue - pixelBlue) * opacity))); 01466 } 01467 } 01468 01469 subPixmap.convertFromImage(image); 01470 bitBlt(&pixmap, zeroX, zeroY, &subPixmap); 01471 } 01472 } 01473 01477 void Client::imposeRegionShadow(TQPixmap &pixmap, TQRegion occluded, 01478 TQRegion exposed, int thickness, double maxOpacity) 01479 { 01480 register int distance, intersectCount, i, j, x, y; 01481 QRgb pixel; 01482 double decay, factor, opacity; 01483 int red, green, blue, pixelRed, pixelGreen, pixelBlue; 01484 int halfMaxIntersects, lineIntersects, maxIntersects, maxY; 01485 int irBottom, irLeft, irRight, irTop, yIncrement; 01486 int subW, subH, w, h, zeroX, zeroY; 01487 TQImage image; 01488 TQMemArray<TQRect>::Iterator it, itEnd; 01489 TQMemArray<TQRect> rectangles; 01490 TQPixmap subPixmap; 01491 Window rootWindow; 01492 int windowX, windowY, xOffset, yOffset; 01493 01494 rectangles = exposed.rects(); 01495 rootWindow = qt_xrootwin(); 01496 windowX = this->x(); 01497 windowY = this->y(); 01498 xOffset = options->shadowXOffset(isActive()); 01499 yOffset = options->shadowYOffset(isActive()); 01500 options->shadowColour(isActive()).rgb(&red, &green, &blue); 01501 maxIntersects = thickness * thickness * 4 + (thickness * 4) + 1; 01502 halfMaxIntersects = maxIntersects / 2; 01503 lineIntersects = thickness * 2 + 1; 01504 factor = maxIntersects / maxOpacity; 01505 decay = (lineIntersects / 0.0125 - factor) / pow((double)maxIntersects, 3.0); 01506 w = pixmap.width(); 01507 h = pixmap.height(); 01508 xOffset = options->shadowXOffset(isActive()); 01509 yOffset = options->shadowYOffset(isActive()); 01510 01511 opacityCache->resize(0); 01512 opacityCache->resize(w * h); 01513 occluded.translate(-windowX + thickness, -windowY + thickness); 01514 01515 itEnd = rectangles.end(); 01516 for (it = rectangles.begin(); it != itEnd; ++it) { 01517 subW = (*it).width(); 01518 subH = (*it).height(); 01519 subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(), 01520 subW, subH); 01521 maxY = subH; 01522 zeroX = (*it).x() - windowX + thickness - xOffset; 01523 zeroY = (*it).y() - windowY + thickness - yOffset; 01524 image = subPixmap.convertToImage(); 01525 01526 intersectCount = 0; 01527 opacity = -1; 01528 y = 0; 01529 yIncrement = 1; 01530 for (x = 0; x < subW; x++) { 01531 irLeft = zeroX + x - thickness; 01532 irRight = zeroX + x + thickness; 01533 01534 while (y != maxY) { 01535 // horizontal row about to leave the intersect region, not 01536 // necessarily the top row 01537 irTop = zeroY + y - thickness * yIncrement; 01538 // horizontal row that just came into the intersect region, 01539 // not necessarily the bottom row 01540 irBottom = zeroY + y + thickness * yIncrement; 01541 01542 if (opacity == -1) { 01543 // If occluded pixels caused an intersect count to be 01544 // skipped, recount it 01545 intersectCount = 0; 01546 01547 for (j = irTop; j != irBottom; j += yIncrement) { 01548 // irTop is not necessarily larger than irBottom and 01549 // yIncrement isn't necessarily positive 01550 for (i = irLeft; i <= irRight; i++) { 01551 if (occluded.contains(TQPoint(i, j))) 01552 intersectCount++; 01553 } 01554 } 01555 } 01556 else { 01557 if (intersectCount < 0) 01558 intersectCount = 0; 01559 01560 for (i = irLeft; i <= irRight; i++) { 01561 if (occluded.contains(TQPoint(i, irBottom))) 01562 intersectCount++; 01563 } 01564 } 01565 01566 distance = maxIntersects - intersectCount; 01567 opacity = intersectCount / (factor + pow((double)distance, 3.0) * decay); 01568 01569 (*(opacityCache))[(zeroY + y) * w + zeroX + x] = opacity; 01570 pixel = image.pixel(x, y); 01571 pixelRed = tqRed(pixel); 01572 pixelGreen = tqGreen(pixel); 01573 pixelBlue = tqBlue(pixel); 01574 image.setPixel(x, y, 01575 tqRgb((int)(pixelRed + (red - pixelRed) * opacity), 01576 (int)(pixelGreen + (green - pixelGreen) * opacity), 01577 (int)(pixelBlue + (blue - pixelBlue) * opacity))); 01578 01579 for (i = irLeft; i <= irRight; i++) { 01580 if (occluded.contains(TQPoint(i, irTop))) 01581 intersectCount--; 01582 } 01583 01584 y += yIncrement; 01585 } 01586 y -= yIncrement; 01587 01588 irTop += yIncrement; 01589 for (j = irTop; j != irBottom; j += yIncrement) { 01590 if (occluded.contains(TQPoint(irLeft, j))) 01591 intersectCount--; 01592 } 01593 irRight++; 01594 for (j = irTop; j != irBottom; j += yIncrement) { 01595 if (occluded.contains(TQPoint(irRight, j))) 01596 intersectCount++; 01597 } 01598 01599 yIncrement *= -1; 01600 if (yIncrement < 0) 01601 // Scan Y-axis bottom-up for next X-coordinate iteration 01602 maxY = -1; 01603 else 01604 // Scan Y-axis top-down for next X-coordinate iteration 01605 maxY = subH; 01606 } 01607 01608 subPixmap.convertFromImage(image); 01609 bitBlt(&pixmap, zeroX, zeroY, &subPixmap); 01610 } 01611 } 01612 01617 void Client::setMappingState(int s) 01618 { 01619 assert( client != None ); 01620 assert( !deleting || s == WithdrawnState ); 01621 if( mapping_state == s ) 01622 return; 01623 bool was_unmanaged = ( mapping_state == WithdrawnState ); 01624 mapping_state = s; 01625 if( mapping_state == WithdrawnState ) 01626 { 01627 XDeleteProperty( qt_xdisplay(), window(), qt_wm_state ); 01628 return; 01629 } 01630 assert( s == NormalState || s == IconicState ); 01631 01632 unsigned long data[2]; 01633 data[0] = (unsigned long) s; 01634 data[1] = (unsigned long) None; 01635 XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32, 01636 PropModeReplace, (unsigned char *)data, 2); 01637 01638 if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry 01639 postponeGeometryUpdates( false ); 01640 } 01641 01646 void Client::rawShow() 01647 { 01648 if( decoration != NULL ) 01649 decoration->widget()->show(); // not really necessary, but let it know the state 01650 XMapWindow( qt_xdisplay(), frame ); 01651 if( !isShade()) 01652 { 01653 XMapWindow( qt_xdisplay(), wrapper ); 01654 XMapWindow( qt_xdisplay(), client ); 01655 } 01656 if (options->shadowEnabled(isActive())) 01657 drawDelayedShadow(); 01658 } 01659 01665 void Client::rawHide() 01666 { 01667 // Here it may look like a race condition, as some other client might try to unmap 01668 // the window between these two XSelectInput() calls. However, they're supposed to 01669 // use XWithdrawWindow(), which also sends a synthetic event to the root window, 01670 // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify 01671 // will be missed is also very minimal, so I don't think it's needed to grab the server 01672 // here. 01673 removeShadow(); 01674 drawIntersectingShadows(); 01675 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify 01676 XUnmapWindow( qt_xdisplay(), frame ); 01677 XUnmapWindow( qt_xdisplay(), wrapper ); 01678 XUnmapWindow( qt_xdisplay(), client ); 01679 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); 01680 if( decoration != NULL ) 01681 decoration->widget()->hide(); // not really necessary, but let it know the state 01682 workspace()->clientHidden( this ); 01683 } 01684 01685 void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3) 01686 { 01687 XEvent ev; 01688 long mask; 01689 01690 memset(&ev, 0, sizeof(ev)); 01691 ev.xclient.type = ClientMessage; 01692 ev.xclient.window = w; 01693 ev.xclient.message_type = a; 01694 ev.xclient.format = 32; 01695 ev.xclient.data.l[0] = protocol; 01696 ev.xclient.data.l[1] = GET_QT_X_TIME(); 01697 ev.xclient.data.l[2] = data1; 01698 ev.xclient.data.l[3] = data2; 01699 ev.xclient.data.l[4] = data3; 01700 mask = 0L; 01701 if (w == qt_xrootwin()) 01702 mask = SubstructureRedirectMask; /* magic! */ 01703 XSendEvent(qt_xdisplay(), w, False, mask, &ev); 01704 } 01705 01706 /* 01707 Returns whether the window may be closed (have a close button) 01708 */ 01709 bool Client::isCloseable() const 01710 { 01711 if( isModalSystemNotification()) 01712 return false; 01713 return rules()->checkCloseable( motif_may_close && !isSpecialWindow()); 01714 } 01715 01720 void Client::closeWindow() 01721 { 01722 if( !isCloseable()) 01723 return; 01724 // Update user time, because the window may create a confirming dialog. 01725 updateUserTime(); 01726 if ( Pdeletewindow ) 01727 { 01728 Notify::raise( Notify::Close ); 01729 sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window); 01730 pingWindow(); 01731 } 01732 else 01733 { 01734 // client will not react on wm_delete_window. We have not choice 01735 // but destroy his connection to the XServer. 01736 killWindow(); 01737 } 01738 } 01739 01740 01744 void Client::killWindow() 01745 { 01746 kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl; 01747 // not sure if we need an Notify::Kill or not.. until then, use 01748 // Notify::Close 01749 Notify::raise( Notify::Close ); 01750 01751 if( isDialog()) 01752 Notify::raise( Notify::TransDelete ); 01753 if( isNormalWindow()) 01754 Notify::raise( Notify::Delete ); 01755 killProcess( false ); 01756 // always kill this client at the server 01757 XKillClient(qt_xdisplay(), window() ); 01758 destroyClient(); 01759 } 01760 01761 // send a ping to the window using _NET_WM_PING if possible 01762 // if it doesn't respond within a reasonable time, it will be 01763 // killed 01764 void Client::pingWindow() 01765 { 01766 if( !Pping ) 01767 return; // can't ping :( 01768 if( options->killPingTimeout == 0 ) 01769 return; // turned off 01770 if( ping_timer != NULL ) 01771 return; // pinging already 01772 ping_timer = new TQTimer( this ); 01773 connect( ping_timer, TQT_SIGNAL( timeout()), TQT_SLOT( pingTimeout())); 01774 ping_timer->start( options->killPingTimeout, true ); 01775 ping_timestamp = GET_QT_X_TIME(); 01776 workspace()->sendPingToWindow( window(), ping_timestamp ); 01777 } 01778 01779 void Client::gotPing( Time timestamp ) 01780 { 01781 // just plain compare is not good enough because of 64bit and truncating and whatnot 01782 if( NET::timestampCompare( timestamp, ping_timestamp ) != 0 ) 01783 return; 01784 delete ping_timer; 01785 ping_timer = NULL; 01786 if( process_killer != NULL ) 01787 { 01788 process_killer->kill(); 01789 delete process_killer; 01790 process_killer = NULL; 01791 } 01792 } 01793 01794 void Client::pingTimeout() 01795 { 01796 kdDebug( 1212 ) << "Ping timeout:" << caption() << endl; 01797 delete ping_timer; 01798 ping_timer = NULL; 01799 killProcess( true, ping_timestamp ); 01800 } 01801 01802 void Client::killProcess( bool ask, Time timestamp ) 01803 { 01804 if( process_killer != NULL ) 01805 return; 01806 Q_ASSERT( !ask || timestamp != CurrentTime ); 01807 TQCString machine = wmClientMachine( true ); 01808 pid_t pid = info->pid(); 01809 if( pid <= 0 || machine.isEmpty()) // needed properties missing 01810 return; 01811 kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl; 01812 if( !ask ) 01813 { 01814 if( machine != "localhost" ) 01815 { 01816 KProcess proc; 01817 proc << "xon" << machine << "kill" << pid; 01818 proc.start( KProcess::DontCare ); 01819 } 01820 else 01821 ::kill( pid, SIGTERM ); 01822 } 01823 else 01824 { // SELI TODO handle the window created by handler specially (on top,urgent?) 01825 process_killer = new KProcess( this ); 01826 *process_killer << KStandardDirs::findExe( "kwin_killer_helper" ) 01827 << "--pid" << TQCString().setNum( pid ) << "--hostname" << machine 01828 << "--windowname" << caption().utf8() 01829 << "--applicationname" << resourceClass() 01830 << "--wid" << TQCString().setNum( window()) 01831 << "--timestamp" << TQCString().setNum( timestamp ); 01832 connect( process_killer, TQT_SIGNAL( processExited( KProcess* )), 01833 TQT_SLOT( processKillerExited())); 01834 if( !process_killer->start( KProcess::NotifyOnExit )) 01835 { 01836 delete process_killer; 01837 process_killer = NULL; 01838 return; 01839 } 01840 } 01841 } 01842 01843 void Client::processKillerExited() 01844 { 01845 kdDebug( 1212 ) << "Killer exited" << endl; 01846 delete process_killer; 01847 process_killer = NULL; 01848 } 01849 01850 void Client::setSkipTaskbar( bool b, bool from_outside ) 01851 { 01852 int was_wants_tab_focus = wantsTabFocus(); 01853 if( from_outside ) 01854 { 01855 b = rules()->checkSkipTaskbar( b ); 01856 original_skip_taskbar = b; 01857 } 01858 if ( b == skipTaskbar() ) 01859 return; 01860 skip_taskbar = b; 01861 info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar ); 01862 updateWindowRules(); 01863 if( was_wants_tab_focus != wantsTabFocus()) 01864 workspace()->updateFocusChains( this, 01865 isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate ); 01866 } 01867 01868 void Client::setSkipPager( bool b ) 01869 { 01870 b = rules()->checkSkipPager( b ); 01871 if ( b == skipPager() ) 01872 return; 01873 skip_pager = b; 01874 info->setState( b?NET::SkipPager:0, NET::SkipPager ); 01875 updateWindowRules(); 01876 } 01877 01878 void Client::setModal( bool m ) 01879 { // Qt-3.2 can have even modal normal windows :( 01880 if( modal == m ) 01881 return; 01882 modal = m; 01883 if( !modal ) 01884 return; 01885 // changing modality for a mapped window is weird (?) 01886 // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG 01887 } 01888 01889 void Client::setDesktop( int desktop ) 01890 { 01891 if( desktop != NET::OnAllDesktops ) // do range check 01892 desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop )); 01893 desktop = rules()->checkDesktop( desktop ); 01894 if( desk == desktop ) 01895 return; 01896 int was_desk = desk; 01897 desk = desktop; 01898 info->setDesktop( desktop ); 01899 if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops )) 01900 { // onAllDesktops changed 01901 if ( isShown( true )) 01902 Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops ); 01903 workspace()->updateOnAllDesktopsOfTransients( this ); 01904 } 01905 if( decoration != NULL ) 01906 decoration->desktopChange(); 01907 workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst ); 01908 updateVisibility(); 01909 updateWindowRules(); 01910 } 01911 01912 void Client::setOnAllDesktops( bool b ) 01913 { 01914 if(( b && isOnAllDesktops()) 01915 || ( !b && !isOnAllDesktops())) 01916 return; 01917 if( b ) 01918 setDesktop( NET::OnAllDesktops ); 01919 else 01920 setDesktop( workspace()->currentDesktop()); 01921 } 01922 01923 bool Client::isOnCurrentDesktop() const 01924 { 01925 return isOnDesktop( workspace()->currentDesktop()); 01926 } 01927 01928 int Client::screen() const 01929 { 01930 if( !options->xineramaEnabled ) 01931 return 0; 01932 return workspace()->screenNumber( geometry().center()); 01933 } 01934 01935 bool Client::isOnScreen( int screen ) const 01936 { 01937 if( !options->xineramaEnabled ) 01938 return screen == 0; 01939 return workspace()->screenGeometry( screen ).intersects( geometry()); 01940 } 01941 01942 // performs activation and/or raising of the window 01943 void Client::takeActivity( int flags, bool handled, allowed_t ) 01944 { 01945 if( !handled || !Ptakeactivity ) 01946 { 01947 if( flags & ActivityFocus ) 01948 takeFocus( Allowed ); 01949 if( flags & ActivityRaise ) 01950 workspace()->raiseClient( this ); 01951 return; 01952 } 01953 01954 #ifndef NDEBUG 01955 static Time previous_activity_timestamp; 01956 static Client* previous_client; 01957 if( previous_activity_timestamp == GET_QT_X_TIME() && previous_client != this ) 01958 { 01959 kdDebug( 1212 ) << "Repeated use of the same X timestamp for activity" << endl; 01960 kdDebug( 1212 ) << kdBacktrace() << endl; 01961 } 01962 previous_activity_timestamp = GET_QT_X_TIME(); 01963 previous_client = this; 01964 #endif 01965 workspace()->sendTakeActivity( this, GET_QT_X_TIME(), flags ); 01966 } 01967 01968 // performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS 01969 void Client::takeFocus( allowed_t ) 01970 { 01971 #ifndef NDEBUG 01972 static Time previous_focus_timestamp; 01973 static Client* previous_client; 01974 if( previous_focus_timestamp == GET_QT_X_TIME() && previous_client != this ) 01975 { 01976 kdDebug( 1212 ) << "Repeated use of the same X timestamp for focus" << endl; 01977 kdDebug( 1212 ) << kdBacktrace() << endl; 01978 } 01979 previous_focus_timestamp = GET_QT_X_TIME(); 01980 previous_client = this; 01981 #endif 01982 if ( rules()->checkAcceptFocus( input )) 01983 { 01984 XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, GET_QT_X_TIME() ); 01985 } 01986 if ( Ptakefocus ) 01987 sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus); 01988 workspace()->setShouldGetFocus( this ); 01989 } 01990 01998 bool Client::providesContextHelp() const 01999 { 02000 if (isModalSystemNotification()) 02001 return false; 02002 return Pcontexthelp; 02003 } 02004 02005 02012 void Client::showContextHelp() 02013 { 02014 if ( Pcontexthelp ) 02015 { 02016 sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help); 02017 TQWhatsThis::enterWhatsThisMode(); // SELI? 02018 } 02019 } 02020 02021 02026 void Client::fetchName() 02027 { 02028 setCaption( readName()); 02029 } 02030 02031 TQString Client::readName() const 02032 { 02033 if ( info->name() && info->name()[ 0 ] != '\0' ) 02034 return TQString::fromUtf8( info->name() ); 02035 else 02036 return KWin::readNameProperty( window(), XA_WM_NAME ); 02037 } 02038 02039 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); 02040 02041 void Client::setCaption( const TQString& s, bool force ) 02042 { 02043 if ( s != cap_normal || force ) 02044 { 02045 bool reset_name = force; 02046 for( unsigned int i = 0; 02047 i < s.length(); 02048 ++i ) 02049 if( !s[ i ].isPrint()) 02050 s[ i ] = ' '; 02051 cap_normal = s; 02052 bool was_suffix = ( !cap_suffix.isEmpty()); 02053 TQString machine_suffix; 02054 if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false ))) 02055 machine_suffix = " <@" + wmClientMachine( true ) + ">"; 02056 TQString shortcut_suffix = !shortcut().isNull() ? ( " {" + shortcut().toString() + "}" ) : ""; 02057 cap_suffix = machine_suffix + shortcut_suffix; 02058 if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) 02059 { 02060 int i = 2; 02061 do 02062 { 02063 cap_suffix = machine_suffix + " <" + TQString::number(i) + ">" + shortcut_suffix; 02064 i++; 02065 } while ( workspace()->findClient( FetchNameInternalPredicate( this ))); 02066 info->setVisibleName( caption().utf8() ); 02067 reset_name = false; 02068 } 02069 if(( was_suffix && cap_suffix.isEmpty() 02070 || reset_name )) // if it was new window, it may have old value still set, if the window is reused 02071 { 02072 info->setVisibleName( "" ); // remove 02073 info->setVisibleIconName( "" ); // remove 02074 } 02075 else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set 02076 info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() ); 02077 02078 if( isManaged() && decoration != NULL ) 02079 decoration->captionChange(); 02080 } 02081 } 02082 02083 void Client::updateCaption() 02084 { 02085 setCaption( cap_normal, true ); 02086 } 02087 02088 void Client::fetchIconicName() 02089 { 02090 TQString s; 02091 if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) 02092 s = TQString::fromUtf8( info->iconName() ); 02093 else 02094 s = KWin::readNameProperty( window(), XA_WM_ICON_NAME ); 02095 if ( s != cap_iconic ) 02096 { 02097 bool was_set = !cap_iconic.isEmpty(); 02098 cap_iconic = s; 02099 if( !cap_suffix.isEmpty()) 02100 { 02101 if( !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set 02102 info->setVisibleIconName( ( s + cap_suffix ).utf8() ); 02103 else if( was_set ) 02104 info->setVisibleIconName( "" ); //remove 02105 } 02106 } 02107 } 02108 02111 TQString Client::caption( bool full ) const 02112 { 02113 return full ? cap_normal + cap_suffix : cap_normal; 02114 } 02115 02116 void Client::getWMHints() 02117 { 02118 XWMHints *hints = XGetWMHints(qt_xdisplay(), window() ); 02119 input = true; 02120 window_group = None; 02121 urgency = false; 02122 if ( hints ) 02123 { 02124 if( hints->flags & InputHint ) 02125 input = hints->input; 02126 if( hints->flags & WindowGroupHint ) 02127 window_group = hints->window_group; 02128 urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield 02129 XFree( (char*)hints ); 02130 } 02131 checkGroup(); 02132 updateUrgency(); 02133 updateAllowedActions(); // group affects isMinimizable() 02134 } 02135 02136 void Client::getMotifHints() 02137 { 02138 bool mnoborder, mresize, mmove, mminimize, mmaximize, mclose; 02139 Motif::readFlags( client, mnoborder, mresize, mmove, mminimize, mmaximize, mclose ); 02140 motif_noborder = mnoborder; 02141 if( !hasNETSupport()) // NETWM apps should set type and size constraints 02142 { 02143 motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well 02144 motif_may_move = mmove; 02145 } 02146 else 02147 motif_may_resize = motif_may_move = true; 02148 // mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too 02149 // mmaximize; - ignore, bogus - maximizing is basically just resizing 02150 motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway 02151 if( isManaged()) 02152 updateDecoration( true ); // check if noborder state has changed 02153 } 02154 02155 void Client::readIcons( Window win, TQPixmap* icon, TQPixmap* miniicon ) 02156 { 02157 // get the icons, allow scaling 02158 if( icon != NULL ) 02159 *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints ); 02160 if( miniicon != NULL ) 02161 if( icon == NULL || !icon->isNull()) 02162 *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints ); 02163 else 02164 *miniicon = TQPixmap(); 02165 } 02166 02167 void Client::getIcons() 02168 { 02169 // first read icons from the window itself 02170 readIcons( window(), &icon_pix, &miniicon_pix ); 02171 if( icon_pix.isNull()) 02172 { // then try window group 02173 icon_pix = group()->icon(); 02174 miniicon_pix = group()->miniIcon(); 02175 } 02176 if( icon_pix.isNull() && isTransient()) 02177 { // then mainclients 02178 ClientList mainclients = mainClients(); 02179 for( ClientList::ConstIterator it = mainclients.begin(); 02180 it != mainclients.end() && icon_pix.isNull(); 02181 ++it ) 02182 { 02183 icon_pix = (*it)->icon(); 02184 miniicon_pix = (*it)->miniIcon(); 02185 } 02186 } 02187 if( icon_pix.isNull()) 02188 { // and if nothing else, load icon from classhint or xapp icon 02189 icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp ); 02190 miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp ); 02191 } 02192 if( isManaged() && decoration != NULL ) 02193 decoration->iconChange(); 02194 } 02195 02196 void Client::getWindowProtocols() 02197 { 02198 Atom *p; 02199 int i,n; 02200 02201 Pdeletewindow = 0; 02202 Ptakefocus = 0; 02203 Ptakeactivity = 0; 02204 Pcontexthelp = 0; 02205 Pping = 0; 02206 02207 if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n)) 02208 { 02209 for (i = 0; i < n; i++) 02210 if (p[i] == atoms->wm_delete_window) 02211 Pdeletewindow = 1; 02212 else if (p[i] == atoms->wm_take_focus) 02213 Ptakefocus = 1; 02214 else if (p[i] == atoms->net_wm_take_activity) 02215 Ptakeactivity = 1; 02216 else if (p[i] == atoms->net_wm_context_help) 02217 Pcontexthelp = 1; 02218 else if (p[i] == atoms->net_wm_ping) 02219 Pping = 1; 02220 if (n>0) 02221 XFree(p); 02222 } 02223 } 02224 02225 static int nullErrorHandler(Display *, XErrorEvent *) 02226 { 02227 return 0; 02228 } 02229 02233 TQCString Client::staticWindowRole(WId w) 02234 { 02235 return getStringProperty(w, qt_window_role).lower(); 02236 } 02237 02241 TQCString Client::staticSessionId(WId w) 02242 { 02243 return getStringProperty(w, qt_sm_client_id); 02244 } 02245 02249 TQCString Client::staticWmCommand(WId w) 02250 { 02251 return getStringProperty(w, XA_WM_COMMAND, ' '); 02252 } 02253 02257 Window Client::staticWmClientLeader(WId w) 02258 { 02259 Atom type; 02260 int format, status; 02261 unsigned long nitems = 0; 02262 unsigned long extra = 0; 02263 unsigned char *data = 0; 02264 Window result = w; 02265 XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler); 02266 status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000, 02267 FALSE, XA_WINDOW, &type, &format, 02268 &nitems, &extra, &data ); 02269 XSetErrorHandler(oldHandler); 02270 if (status == Success ) 02271 { 02272 if (data && nitems > 0) 02273 result = *((Window*) data); 02274 XFree(data); 02275 } 02276 return result; 02277 } 02278 02279 02280 void Client::getWmClientLeader() 02281 { 02282 wmClientLeaderWin = staticWmClientLeader(window()); 02283 } 02284 02289 TQCString Client::sessionId() 02290 { 02291 TQCString result = staticSessionId(window()); 02292 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) 02293 result = staticSessionId(wmClientLeaderWin); 02294 return result; 02295 } 02296 02301 TQCString Client::wmCommand() 02302 { 02303 TQCString result = staticWmCommand(window()); 02304 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) 02305 result = staticWmCommand(wmClientLeaderWin); 02306 return result; 02307 } 02308 02309 void Client::getWmClientMachine() 02310 { 02311 client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE); 02312 if( client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) 02313 client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE); 02314 if( client_machine.isEmpty()) 02315 client_machine = "localhost"; 02316 } 02317 02322 TQCString Client::wmClientMachine( bool use_localhost ) const 02323 { 02324 TQCString result = client_machine; 02325 if( use_localhost ) 02326 { // special name for the local machine (localhost) 02327 if( result != "localhost" && isLocalMachine( result )) 02328 result = "localhost"; 02329 } 02330 return result; 02331 } 02332 02337 Window Client::wmClientLeader() const 02338 { 02339 if (wmClientLeaderWin) 02340 return wmClientLeaderWin; 02341 return window(); 02342 } 02343 02344 bool Client::wantsTabFocus() const 02345 { 02346 return ( isNormalWindow() || isDialog()) && wantsInput() && !skip_taskbar; 02347 } 02348 02349 02350 bool Client::wantsInput() const 02351 { 02352 return rules()->checkAcceptFocus( input || Ptakefocus ); 02353 } 02354 02355 bool Client::isDesktop() const 02356 { 02357 return windowType() == NET::Desktop; 02358 } 02359 02360 bool Client::isDock() const 02361 { 02362 return windowType() == NET::Dock; 02363 } 02364 02365 bool Client::isTopMenu() const 02366 { 02367 return windowType() == NET::TopMenu; 02368 } 02369 02370 02371 bool Client::isMenu() const 02372 { 02373 return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. 02374 } 02375 02376 bool Client::isToolbar() const 02377 { 02378 return windowType() == NET::Toolbar; 02379 } 02380 02381 bool Client::isSplash() const 02382 { 02383 return windowType() == NET::Splash; 02384 } 02385 02386 bool Client::isUtility() const 02387 { 02388 return windowType() == NET::Utility; 02389 } 02390 02391 bool Client::isDialog() const 02392 { 02393 return windowType() == NET::Dialog; 02394 } 02395 02396 bool Client::isNormalWindow() const 02397 { 02398 return windowType() == NET::Normal; 02399 } 02400 02401 bool Client::isSpecialWindow() const 02402 { 02403 return isDesktop() || isDock() || isSplash() || isTopMenu() 02404 || isToolbar(); // TODO 02405 } 02406 02407 NET::WindowType Client::windowType( bool direct, int supported_types ) const 02408 { 02409 NET::WindowType wt = info->windowType( supported_types ); 02410 if( direct ) 02411 return wt; 02412 NET::WindowType wt2 = rules()->checkType( wt ); 02413 if( wt != wt2 ) 02414 { 02415 wt = wt2; 02416 info->setWindowType( wt ); // force hint change 02417 } 02418 // hacks here 02419 if( wt == NET::Menu ) 02420 { 02421 // ugly hack to support the times when NET::Menu meant NET::TopMenu 02422 // if it's as wide as the screen, not very high and has its upper-left 02423 // corner a bit above the screen's upper-left cornet, it's a topmenu 02424 if( x() == 0 && y() < 0 && y() > -10 && height() < 100 02425 && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 ) 02426 wt = NET::TopMenu; 02427 } 02428 // TODO change this to rule 02429 const char* const oo_prefix = "openoffice.org"; // TQCString has no startsWith() 02430 // oo_prefix is lowercase, because resourceClass() is forced to be lowercase 02431 if( tqstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog ) 02432 wt = NET::Normal; // see bug #66065 02433 if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec 02434 wt = isTransient() ? NET::Dialog : NET::Normal; 02435 return wt; 02436 } 02437 02442 void Client::setCursor( Position m ) 02443 { 02444 if( !isResizable() || isShade()) 02445 { 02446 m = PositionCenter; 02447 } 02448 switch ( m ) 02449 { 02450 case PositionTopLeft: 02451 case PositionBottomRight: 02452 setCursor( tqsizeFDiagCursor ); 02453 break; 02454 case PositionBottomLeft: 02455 case PositionTopRight: 02456 setCursor( tqsizeBDiagCursor ); 02457 break; 02458 case PositionTop: 02459 case PositionBottom: 02460 setCursor( tqsizeVerCursor ); 02461 break; 02462 case PositionLeft: 02463 case PositionRight: 02464 setCursor( tqsizeHorCursor ); 02465 break; 02466 default: 02467 if( buttonDown && isMovable()) 02468 setCursor( tqsizeAllCursor ); 02469 else 02470 setCursor( tqarrowCursor ); 02471 break; 02472 } 02473 } 02474 02475 // TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit? 02476 void Client::setCursor( const TQCursor& c ) 02477 { 02478 if( c.handle() == cursor.handle()) 02479 return; 02480 cursor = c; 02481 if( decoration != NULL ) 02482 decoration->widget()->setCursor( cursor ); 02483 XDefineCursor( qt_xdisplay(), frameId(), cursor.handle()); 02484 } 02485 02486 Client::Position Client::mousePosition( const TQPoint& p ) const 02487 { 02488 if( decoration != NULL ) 02489 return decoration->mousePosition( p ); 02490 return PositionCenter; 02491 } 02492 02493 void Client::updateAllowedActions( bool force ) 02494 { 02495 if( !isManaged() && !force ) 02496 return; 02497 unsigned long old_allowed_actions = allowed_actions; 02498 allowed_actions = 0; 02499 if( isMovable()) 02500 allowed_actions |= NET::ActionMove; 02501 if( isResizable()) 02502 allowed_actions |= NET::ActionResize; 02503 if( isMinimizable()) 02504 allowed_actions |= NET::ActionMinimize; 02505 if( isShadeable()) 02506 allowed_actions |= NET::ActionShade; 02507 // sticky state not supported 02508 if( isMaximizable()) 02509 allowed_actions |= NET::ActionMax; 02510 if( userCanSetFullScreen()) 02511 allowed_actions |= NET::ActionFullScreen; 02512 allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.) 02513 if( isCloseable()) 02514 allowed_actions |= NET::ActionClose; 02515 if( old_allowed_actions == allowed_actions ) 02516 return; 02517 // TODO this could be delayed and compressed - it's only for pagers etc. anyway 02518 info->setAllowedActions( allowed_actions ); 02519 // TODO this should also tell the decoration, so that it can update the buttons 02520 } 02521 02522 void Client::autoRaise() 02523 { 02524 workspace()->raiseClient( this ); 02525 cancelAutoRaise(); 02526 } 02527 02528 void Client::cancelAutoRaise() 02529 { 02530 delete autoRaiseTimer; 02531 autoRaiseTimer = 0; 02532 } 02533 02534 void Client::setOpacity(bool translucent, uint opacity) 02535 { 02536 if (isDesktop()) 02537 return; // xcompmgr does not like non solid desktops and the user could set it accidently by mouse scrolling 02538 // qWarning("setting opacity for %d",qt_xdisplay()); 02539 //rule out activated translulcency with 100% opacity 02540 if (!translucent || opacity == 0xFFFFFFFF) 02541 { 02542 opacity_ = 0xFFFFFFFF; 02543 XDeleteProperty (qt_xdisplay(), frameId(), atoms->net_wm_window_opacity); 02544 XDeleteProperty (qt_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 02545 } 02546 else{ 02547 if(opacity == opacity_) 02548 return; 02549 opacity_ = opacity; 02550 long data = opacity; // 32bit XChangeProperty needs long 02551 XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); 02552 XChangeProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); 02553 } 02554 } 02555 02556 void Client::setShadowSize(uint shadowSize) 02557 { 02558 // ignoring all individual settings - if we control a window, we control it's shadow 02559 // TODO somehow handle individual settings for docks (besides custom sizes) 02560 long data = shadowSize; 02561 XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shadow, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); 02562 } 02563 02564 void Client::updateOpacity() 02565 // extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows) 02566 { 02567 if (!(isNormalWindow() || isDialog() || isUtility() )|| custom_opacity) 02568 return; 02569 if (isActive()) 02570 { 02571 if( ruleOpacityActive() ) 02572 setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); 02573 else 02574 setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); 02575 if (isBMP()) 02576 // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P ) 02577 { 02578 ClientList tmpGroupMembers = group()->members(); 02579 ClientList activeGroupMembers; 02580 activeGroupMembers.append(this); 02581 tmpGroupMembers.remove(this); 02582 ClientList::Iterator it = tmpGroupMembers.begin(); 02583 while (it != tmpGroupMembers.end()) 02584 // search for next attached and not activated client and repeat if found 02585 { 02586 if ((*it) != this && (*it)->isBMP()) 02587 // potential "to activate" client found 02588 { 02589 // qWarning("client found"); 02590 if ((*it)->touches(this)) // first test, if the new client touches the just activated one 02591 { 02592 // qWarning("found client touches me"); 02593 if( ruleOpacityActive() ) 02594 (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); 02595 else 02596 (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); 02597 // qWarning("activated, search restarted (1)"); 02598 (*it)->setShadowSize(options->activeWindowShadowSize); 02599 activeGroupMembers.append(*it); 02600 tmpGroupMembers.remove(it); 02601 it = tmpGroupMembers.begin(); // restart, search next client 02602 continue; 02603 } 02604 else 02605 { // pot. client does not touch c, so we have to search if it touches some other activated client 02606 bool found = false; 02607 for( ClientList::ConstIterator it2 = activeGroupMembers.begin(); it2 != activeGroupMembers.end(); it2++ ) 02608 { 02609 if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2)) 02610 { 02611 // qWarning("found client touches other active client"); 02612 if( ruleOpacityActive() ) 02613 (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); 02614 else 02615 (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); 02616 (*it)->setShadowSize(options->activeWindowShadowSize); 02617 activeGroupMembers.append(*it); 02618 tmpGroupMembers.remove(it); 02619 it = tmpGroupMembers.begin(); // reset potential client search 02620 found = true; 02621 // qWarning("activated, search restarted (2)"); 02622 break; // skip this loop 02623 } 02624 } 02625 if (found) continue; 02626 } 02627 } 02628 it++; 02629 } 02630 } 02631 else if (isNormalWindow()) 02632 // activate dependend minor windows as well 02633 { 02634 for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) 02635 if ((*it)->isDialog() || (*it)->isUtility()) 02636 if( (*it)->ruleOpacityActive() ) 02637 (*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive()); 02638 else 02639 (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); 02640 } 02641 } 02642 else 02643 { 02644 if( ruleOpacityInactive() ) 02645 setOpacity(rule_opacity_inactive < 0xFFFFFFFF, rule_opacity_inactive); 02646 else 02647 setOpacity(options->translucentInactiveWindows && !(keepAbove() && options->keepAboveAsActive), 02648 options->inactiveWindowOpacity); 02649 // deactivate dependend minor windows as well 02650 if (isBMP()) 02651 // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P ) 02652 { 02653 ClientList tmpGroupMembers = group()->members(); 02654 ClientList inactiveGroupMembers; 02655 inactiveGroupMembers.append(this); 02656 tmpGroupMembers.remove(this); 02657 ClientList::Iterator it = tmpGroupMembers.begin(); 02658 while ( it != tmpGroupMembers.end() ) 02659 // search for next attached and not activated client and repeat if found 02660 { 02661 if ((*it) != this && (*it)->isBMP()) 02662 // potential "to activate" client found 02663 { 02664 // qWarning("client found"); 02665 if ((*it)->touches(this)) // first test, if the new client touches the just activated one 02666 { 02667 // qWarning("found client touches me"); 02668 if( (*it)->ruleOpacityInactive() ) 02669 (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); 02670 else 02671 (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); 02672 (*it)->setShadowSize(options->inactiveWindowShadowSize); 02673 // qWarning("deactivated, search restarted (1)"); 02674 inactiveGroupMembers.append(*it); 02675 tmpGroupMembers.remove(it); 02676 it = tmpGroupMembers.begin(); // restart, search next client 02677 continue; 02678 } 02679 else // pot. client does not touch c, so we have to search if it touches some other activated client 02680 { 02681 bool found = false; 02682 for( ClientList::ConstIterator it2 = inactiveGroupMembers.begin(); it2 != inactiveGroupMembers.end(); it2++ ) 02683 { 02684 if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2)) 02685 { 02686 // qWarning("found client touches other inactive client"); 02687 if( (*it)->ruleOpacityInactive() ) 02688 (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); 02689 else 02690 (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); 02691 (*it)->setShadowSize(options->inactiveWindowShadowSize); 02692 // qWarning("deactivated, search restarted (2)"); 02693 inactiveGroupMembers.append(*it); 02694 tmpGroupMembers.remove(it); 02695 it = tmpGroupMembers.begin(); // reset potential client search 02696 found = true; 02697 break; // skip this loop 02698 } 02699 } 02700 if (found) continue; 02701 } 02702 } 02703 it++; 02704 } 02705 } 02706 else if (isNormalWindow()) 02707 { 02708 for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) 02709 if ((*it)->isUtility()) //don't deactivate dialogs... 02710 if( (*it)->ruleOpacityInactive() ) 02711 (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); 02712 else 02713 (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); 02714 } 02715 } 02716 } 02717 02718 void Client::updateShadowSize() 02719 // extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows) 02720 { 02721 if (!(isNormalWindow() || isDialog() || isUtility() )) 02722 return; 02723 if (isActive()) 02724 setShadowSize(options->activeWindowShadowSize); 02725 else 02726 setShadowSize(options->inactiveWindowShadowSize); 02727 } 02728 02729 uint Client::ruleOpacityInactive() 02730 { 02731 return rule_opacity_inactive;// != 0 ; 02732 } 02733 02734 uint Client::ruleOpacityActive() 02735 { 02736 return rule_opacity_active;// != 0; 02737 } 02738 02739 bool Client::getWindowOpacity() //query translucency settings from X, returns true if window opacity is set 02740 { 02741 unsigned char *data = 0; 02742 Atom actual; 02743 int format, result; 02744 unsigned long n, left; 02745 result = XGetWindowProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data); 02746 if (result == Success && data != None && format == 32 ) 02747 { 02748 opacity_ = *reinterpret_cast< long* >( data ); 02749 custom_opacity = true; 02750 // setOpacity(opacity_ < 0xFFFFFFFF, opacity_); 02751 XFree ((char*)data); 02752 return TRUE; 02753 } 02754 return FALSE; 02755 } 02756 02757 void Client::setCustomOpacityFlag(bool custom) 02758 { 02759 custom_opacity = custom; 02760 } 02761 02762 uint Client::opacity() 02763 { 02764 return opacity_; 02765 } 02766 02767 int Client::opacityPercentage() 02768 { 02769 return int(100*((double)opacity_/0xffffffff)); 02770 } 02771 02772 bool Client::touches(const Client* c) 02773 // checks if this client borders c, needed to test beep media player window state 02774 { 02775 if (y() == c->y() + c->height()) // this bottom to c 02776 return TRUE; 02777 if (y() + height() == c->y()) // this top to c 02778 return TRUE; 02779 if (x() == c->x() + c->width()) // this right to c 02780 return TRUE; 02781 if (x() + width() == c->x()) // this left to c 02782 return TRUE; 02783 return FALSE; 02784 } 02785 02786 void Client::setDecoHashProperty(uint topHeight, uint rightWidth, uint bottomHeight, uint leftWidth) 02787 { 02788 long data = (topHeight < 255 ? topHeight : 255) << 24 | 02789 (rightWidth < 255 ? rightWidth : 255) << 16 | 02790 (bottomHeight < 255 ? bottomHeight : 255) << 8 | 02791 (leftWidth < 255 ? leftWidth : 255); 02792 XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_decohash, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); 02793 } 02794 02795 void Client::unsetDecoHashProperty() 02796 { 02797 XDeleteProperty( qt_xdisplay(), frameId(), atoms->net_wm_window_decohash); 02798 } 02799 02800 #ifndef NDEBUG 02801 kdbgstream& operator<<( kdbgstream& stream, const Client* cl ) 02802 { 02803 if( cl == NULL ) 02804 return stream << "\'NULL_CLIENT\'"; 02805 return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'"; 02806 } 02807 kdbgstream& operator<<( kdbgstream& stream, const ClientList& list ) 02808 { 02809 stream << "LIST:("; 02810 bool first = true; 02811 for( ClientList::ConstIterator it = list.begin(); 02812 it != list.end(); 02813 ++it ) 02814 { 02815 if( !first ) 02816 stream << ":"; 02817 first = false; 02818 stream << *it; 02819 } 02820 stream << ")"; 02821 return stream; 02822 } 02823 kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list ) 02824 { 02825 stream << "LIST:("; 02826 bool first = true; 02827 for( ConstClientList::ConstIterator it = list.begin(); 02828 it != list.end(); 02829 ++it ) 02830 { 02831 if( !first ) 02832 stream << ":"; 02833 first = false; 02834 stream << *it; 02835 } 02836 stream << ")"; 02837 return stream; 02838 } 02839 #endif 02840 02841 TQPixmap * kwin_get_menu_pix_hack() 02842 { 02843 static TQPixmap p; 02844 if ( p.isNull() ) 02845 p = SmallIcon( "bx2" ); 02846 return &p; 02847 } 02848 02849 } // namespace 02850 02851 #include "client.moc"