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