kwin Library API Documentation

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 <qapplication.h> 00015 #include <qpainter.h> 00016 #include <qdatetime.h> 00017 #include <kprocess.h> 00018 #include <unistd.h> 00019 #include <kstandarddirs.h> 00020 #include <qwhatsthis.h> 00021 #include <kwin.h> 00022 #include <kiconloader.h> 00023 #include <stdlib.h> 00024 00025 #include "bridge.h" 00026 #include "group.h" 00027 #include "workspace.h" 00028 #include "atoms.h" 00029 #include "notifications.h" 00030 00031 #include <X11/extensions/shape.h> 00032 00033 // put all externs before the namespace statement to allow the linker 00034 // to resolve them properly 00035 00036 extern Atom qt_wm_state; 00037 extern Time qt_x_time; 00038 extern Atom qt_window_role; 00039 extern Atom qt_sm_client_id; 00040 00041 namespace KWinInternal 00042 { 00043 00044 /* 00045 00046 Creating a client: 00047 - only by calling Workspace::createClient() 00048 - it creates a new client and calls manage() for it 00049 00050 Destroying a client: 00051 - destroyClient() - only when the window itself has been destroyed 00052 - releaseWindow() - the window is kept, only the client itself is destroyed 00053 00054 */ 00055 00056 00068 Client::Client( Workspace *ws ) 00069 : QObject( NULL ), 00070 client( None ), 00071 wrapper( None ), 00072 frame( None ), 00073 decoration( NULL ), 00074 wspace( ws ), 00075 bridge( new Bridge( this )), 00076 move_faked_activity( false ), 00077 move_resize_grab_window( None ), 00078 transient_for( NULL ), 00079 transient_for_id( None ), 00080 original_transient_for_id( None ), 00081 in_group( NULL ), 00082 window_group( None ), 00083 in_layer( UnknownLayer ), 00084 ping_timer( NULL ), 00085 process_killer( NULL ), 00086 user_time( CurrentTime ), // not known yet 00087 allowed_actions( 0 ), 00088 block_geometry( 0 ), 00089 shade_geometry_change( false ), 00090 border_left( 0 ), 00091 border_right( 0 ), 00092 border_top( 0 ), 00093 border_bottom( 0 ) 00094 // SELI do all as initialization 00095 { 00096 autoRaiseTimer = 0; 00097 shadeHoverTimer = 0; 00098 00099 // set the initial mapping state 00100 mapping_state = WithdrawnState; 00101 desk = 0; // no desktop yet 00102 00103 mode = PositionCenter; 00104 buttonDown = FALSE; 00105 moveResizeMode = FALSE; 00106 00107 info = NULL; 00108 00109 shade_mode = ShadeNone; 00110 active = FALSE; 00111 keep_above = FALSE; 00112 keep_below = FALSE; 00113 is_shape = FALSE; 00114 motif_may_move = TRUE; 00115 motif_may_resize = TRUE; 00116 motif_may_close = TRUE; 00117 fullscreen_mode = FullScreenNone; 00118 skip_taskbar = FALSE; 00119 original_skip_taskbar = false; 00120 minimized = false; 00121 hidden = false; 00122 modal = false; 00123 noborder = false; 00124 user_noborder = false; 00125 not_obscured = false; 00126 urgency = false; 00127 ignore_focus_stealing = false; 00128 00129 Pdeletewindow = 0; 00130 Ptakefocus = 0; 00131 Pcontexthelp = 0; 00132 Pping = 0; 00133 input = FALSE; 00134 store_settings = FALSE; 00135 skip_pager = FALSE; 00136 00137 00138 max_mode = MaximizeRestore; 00139 00140 cmap = None; 00141 00142 frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) 00143 client_size = QSize( 100, 100 ); 00144 00145 // SELI initialize xsizehints?? 00146 } 00147 00151 Client::~Client() 00152 { 00153 assert(!moveResizeMode); 00154 assert( client == None ); 00155 assert( frame == None && wrapper == None ); 00156 assert( decoration == NULL ); 00157 assert( block_geometry == 0 ); 00158 delete info; 00159 delete bridge; 00160 } 00161 00162 // use destroyClient() or releaseWindow(), Client instances cannot be deleted directly 00163 void Client::deleteClient( Client* c, allowed_t ) 00164 { 00165 delete c; 00166 } 00167 00171 void Client::releaseWindow( bool on_shutdown ) 00172 { 00173 if (moveResizeMode) 00174 leaveMoveResize(); 00175 setModal( false ); // otherwise its mainwindow wouldn't get focus 00176 hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags) 00177 if( !on_shutdown ) 00178 workspace()->clientHidden( this ); 00179 XUnmapWindow( qt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect 00180 destroyDecoration(); 00181 cleanGrouping(); 00182 if( !on_shutdown ) 00183 { 00184 workspace()->removeClient( this, Allowed ); 00185 // only when the window is being unmapped, not when closing down KWin 00186 // (NETWM sections 5.5,5.7) 00187 info->setDesktop( 0 ); 00188 desk = 0; 00189 info->setState( 0, info->state()); // reset all state flags 00190 } 00191 XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_user_creation_time); 00192 // TODO remove KDEFrameStrut property 00193 XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y()); 00194 XRemoveFromSaveSet( qt_xdisplay(), client ); 00195 XSelectInput( qt_xdisplay(), client, NoEventMask ); 00196 if( on_shutdown ) 00197 { // map the window, so it can be found after another WM is started 00198 XMapWindow( qt_xdisplay(), client ); 00199 // TODO preserve minimized, shaded etc. state? 00200 } 00201 else 00202 { 00203 // Make sure it's not mapped if the app unmapped it (#65279). The app 00204 // may do map+unmap before we initially map the window by calling rawShow() from manage(). 00205 XUnmapWindow( qt_xdisplay(), client ); 00206 } 00207 setMappingState( WithdrawnState ); // after all is done, tell the app 00208 client = None; 00209 XDestroyWindow( qt_xdisplay(), wrapper ); 00210 wrapper = None; 00211 XDestroyWindow( qt_xdisplay(), frame ); 00212 frame = None; 00213 deleteClient( this, Allowed ); 00214 } 00215 00216 // like releaseWindow(), but this one is called when the window has been already destroyed 00217 // (e.g. the application closed it) 00218 void Client::destroyClient() 00219 { 00220 if (moveResizeMode) 00221 leaveMoveResize(); 00222 ++block_geometry; 00223 setModal( false ); 00224 hidden = true; // so that it's not considered visible anymore 00225 workspace()->clientHidden( this ); 00226 destroyDecoration(); 00227 cleanGrouping(); 00228 workspace()->removeClient( this, Allowed ); 00229 client = None; // invalidate 00230 XDestroyWindow( qt_xdisplay(), wrapper ); 00231 wrapper = None; 00232 XDestroyWindow( qt_xdisplay(), frame ); 00233 frame = None; 00234 --block_geometry; 00235 deleteClient( this, Allowed ); 00236 } 00237 00238 void Client::updateDecoration( bool check_workspace_pos, bool force ) 00239 { 00240 if( !force && (( decoration == NULL && noBorder()) 00241 || ( decoration != NULL && !noBorder()))) 00242 return; 00243 bool do_show = false; 00244 ++block_geometry; 00245 if( force ) 00246 destroyDecoration(); 00247 if( !noBorder()) 00248 { 00249 decoration = workspace()->createDecoration( bridge ); 00250 // TODO check decoration's minimum size? 00251 decoration->init(); 00252 decoration->widget()->installEventFilter( this ); 00253 XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 ); 00254 decoration->widget()->lower(); 00255 decoration->borders( border_left, border_right, border_top, border_bottom ); 00256 int save_workarea_diff_x = workarea_diff_x; 00257 int save_workarea_diff_y = workarea_diff_y; 00258 move( calculateGravitation( false )); 00259 if( !isShade()) 00260 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); 00261 else 00262 plainResize( sizeForClientSize( QSize( clientSize().width(), 0 ), SizemodeShaded ), ForceGeometrySet ); 00263 workarea_diff_x = save_workarea_diff_x; 00264 workarea_diff_y = save_workarea_diff_y; 00265 do_show = true; 00266 } 00267 else 00268 destroyDecoration(); 00269 if( check_workspace_pos ) 00270 checkWorkspacePosition(); 00271 --block_geometry; 00272 setGeometry( geometry(), ForceGeometrySet ); 00273 if( do_show ) 00274 decoration->widget()->show(); 00275 updateFrameStrut(); 00276 } 00277 00278 void Client::destroyDecoration() 00279 { 00280 if( decoration != NULL ) 00281 { 00282 delete decoration; 00283 decoration = NULL; 00284 QPoint grav = calculateGravitation( true ); 00285 border_left = border_right = border_top = border_bottom = 0; 00286 setMask( QRegion()); // reset shape mask 00287 int save_workarea_diff_x = workarea_diff_x; 00288 int save_workarea_diff_y = workarea_diff_y; 00289 if( !isShade()) 00290 plainResize( clientSize(), ForceGeometrySet ); 00291 else 00292 plainResize( QSize( clientSize().width(), 0 ), ForceGeometrySet ); 00293 move( grav ); 00294 workarea_diff_x = save_workarea_diff_x; 00295 workarea_diff_y = save_workarea_diff_y; 00296 } 00297 } 00298 00299 void Client::checkBorderSizes() 00300 { 00301 if( decoration == NULL ) 00302 return; 00303 int new_left, new_right, new_top, new_bottom; 00304 decoration->borders( new_left, new_right, new_top, new_bottom ); 00305 if( new_left == border_left && new_right == border_right 00306 && new_top == border_top && new_bottom == border_bottom ) 00307 return; 00308 ++block_geometry; 00309 move( calculateGravitation( true )); 00310 border_left = new_left; 00311 border_right = new_right; 00312 border_top = new_top; 00313 border_bottom = new_bottom; 00314 move( calculateGravitation( false )); 00315 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); 00316 checkWorkspacePosition(); 00317 --block_geometry; 00318 setGeometry( geometry(), ForceGeometrySet ); 00319 } 00320 00321 void Client::detectNoBorder() 00322 { 00323 if( Shape::hasShape( window()) || Motif::noBorder( window())) 00324 { 00325 noborder = true; // TODO for all window types? 00326 return; 00327 } 00328 switch( windowType()) 00329 { 00330 case NET::Desktop : 00331 case NET::Dock : 00332 case NET::TopMenu : 00333 case NET::Override : 00334 case NET::Splash : 00335 noborder = true; 00336 break; 00337 case NET::Unknown : 00338 case NET::Normal : 00339 case NET::Toolbar : 00340 case NET::Menu : 00341 case NET::Dialog : 00342 case NET::Utility : 00343 noborder = false; 00344 break; 00345 default: 00346 assert( false ); 00347 } 00348 } 00349 00350 void Client::updateFrameStrut() 00351 { 00352 // TODO KDEFrameStrut je ale pitome jmeno 00353 NETStrut strut; 00354 strut.left = border_left; 00355 strut.right = border_right; 00356 strut.top = border_top; 00357 strut.bottom = border_bottom; 00358 info->setKDEFrameStrut( strut ); 00359 } 00360 00361 // Resizes the decoration, and makes sure the decoration widget gets resize event 00362 // even if the size hasn't changed. This is needed to make sure the decoration 00363 // re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes, 00364 // the decoration may turn on/off some borders, but the actual size 00365 // of the decoration stays the same). 00366 void Client::resizeDecoration( const QSize& s ) 00367 { 00368 if( decoration == NULL ) 00369 return; 00370 QSize oldsize = decoration->widget()->size(); 00371 decoration->resize( s ); 00372 if( oldsize == s ) 00373 { 00374 QResizeEvent e( s, oldsize ); 00375 QApplication::sendEvent( decoration->widget(), &e ); 00376 } 00377 } 00378 00379 bool Client::noBorder() const 00380 { 00381 return noborder || isFullScreen() || user_noborder; 00382 } 00383 00384 bool Client::userCanSetNoBorder() const 00385 { 00386 return !noborder && !isFullScreen() && !isShade(); 00387 } 00388 00389 bool Client::isUserNoBorder() const 00390 { 00391 return user_noborder; 00392 } 00393 00394 void Client::setUserNoBorder( bool set ) 00395 { 00396 if( !userCanSetNoBorder()) 00397 return; 00398 if( user_noborder == set ) 00399 return; 00400 user_noborder = set; 00401 updateDecoration( true, false ); 00402 } 00403 00404 void Client::updateShape() 00405 { 00406 if ( shape() ) 00407 XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding, 00408 clientPos().x(), clientPos().y(), 00409 window(), ShapeBounding, ShapeSet); 00410 else 00411 XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, 00412 None, ShapeSet); 00413 // workaround for #19644 - shaped windows shouldn't have decoration 00414 if( shape() && !noBorder()) 00415 { 00416 noborder = true; 00417 updateDecoration( true ); 00418 } 00419 } 00420 00421 void Client::setMask( const QRegion& reg, int mode ) 00422 { 00423 _mask = reg; 00424 if( reg.isNull()) 00425 XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, 00426 None, ShapeSet ); 00427 else if( mode == X::Unsorted ) 00428 XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, 00429 reg.handle(), ShapeSet ); 00430 else 00431 { 00432 QMemArray< QRect > rects = reg.rects(); 00433 XRectangle* xrects = new XRectangle[ rects.count() ]; 00434 for( unsigned int i = 0; 00435 i < rects.count(); 00436 ++i ) 00437 { 00438 xrects[ i ].x = rects[ i ].x(); 00439 xrects[ i ].y = rects[ i ].y(); 00440 xrects[ i ].width = rects[ i ].width(); 00441 xrects[ i ].height = rects[ i ].height(); 00442 } 00443 XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, 00444 xrects, rects.count(), ShapeSet, mode ); 00445 delete[] xrects; 00446 } 00447 } 00448 00449 QRegion Client::mask() const 00450 { 00451 if( _mask.isEmpty()) 00452 return QRegion( 0, 0, width(), height()); 00453 return _mask; 00454 } 00455 00456 void Client::hideClient( bool hide ) 00457 { 00458 if( hidden == hide ) 00459 return; 00460 hidden = hide; 00461 info->setState( hidden ? NET::Hidden : 0, NET::Hidden ); 00462 if( hidden ) 00463 { 00464 setMappingState( IconicState ); 00465 rawHide(); 00466 setSkipTaskbar( true, false ); // also hide from taskbar 00467 } 00468 else // !hidden 00469 { 00470 setSkipTaskbar( original_skip_taskbar, false ); 00471 if( isOnCurrentDesktop()) 00472 { 00473 if( isShown( false )) 00474 setMappingState( NormalState ); 00475 rawShow(); // is either visible or shaded 00476 } 00477 } 00478 } 00479 00480 /* 00481 Returns whether the window is minimizable or not 00482 */ 00483 bool Client::isMinimizable() const 00484 { 00485 if( !wantsTabFocus() // SELI co NET::Utility? a proc wantsTabFocus() - skiptaskbar? ? 00486 || ( isSpecialWindow() && !isOverride())) 00487 return false; 00488 if( isTransient()) 00489 { // transients may be minimized only if mainwindow is not shown 00490 ClientList mainclients = mainClients(); 00491 for( ClientList::ConstIterator it = mainclients.begin(); 00492 it != mainclients.end(); 00493 ++it ) 00494 if( (*it)->isShown( true )) 00495 return false; 00496 } 00497 return true; 00498 } 00499 00503 void Client::minimize() 00504 { 00505 if ( !isMinimizable() || isMinimized()) 00506 return; 00507 00508 minimized = true; 00509 00510 Notify::raise( Notify::Minimize ); 00511 00512 // SELI mainClients().isEmpty() ??? - and in unminimize() too 00513 if ( mainClients().isEmpty() && isOnCurrentDesktop()) 00514 animateMinimizeOrUnminimize( true ); // was visible or shaded 00515 00516 setMappingState( IconicState ); 00517 info->setState( NET::Hidden, NET::Hidden ); 00518 rawHide(); 00519 updateAllowedActions(); 00520 workspace()->updateMinimizedOfTransients( this ); 00521 } 00522 00523 void Client::unminimize() 00524 { 00525 if( !isMinimized()) 00526 return; 00527 00528 Notify::raise( Notify::UnMinimize ); 00529 minimized = false; 00530 info->setState( 0, NET::Hidden ); 00531 if( isOnCurrentDesktop()) 00532 { 00533 if( mainClients().isEmpty()) 00534 animateMinimizeOrUnminimize( FALSE ); 00535 if( isShown( false )) 00536 setMappingState( NormalState ); 00537 rawShow(); // is either visible or shaded 00538 } 00539 updateAllowedActions(); 00540 workspace()->updateMinimizedOfTransients( this ); 00541 } 00542 00543 extern bool blockAnimation; 00544 00545 void Client::animateMinimizeOrUnminimize( bool minimize ) 00546 { 00547 if ( blockAnimation ) 00548 return; 00549 if ( !options->animateMinimize ) 00550 return; 00551 00552 if( decoration != NULL && decoration->animateMinimize( minimize )) 00553 return; // decoration did it 00554 00555 // the function is a bit tricky since it will ensure that an 00556 // animation action needs always the same time regardless of the 00557 // performance of the machine or the X-Server. 00558 00559 float lf,rf,tf,bf,step; 00560 00561 int speed = options->animateMinimizeSpeed; 00562 if ( speed > 10 ) 00563 speed = 10; 00564 if ( speed < 0 ) 00565 speed = 0; 00566 00567 step = 40. * (11 - speed ); 00568 00569 NETRect r = info->iconGeometry(); 00570 QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height ); 00571 if ( !icongeom.isValid() ) 00572 return; 00573 00574 QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() ); 00575 00576 QRect before, after; 00577 if ( minimize ) 00578 { 00579 before = QRect( x(), y(), width(), pm.height() ); 00580 after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); 00581 } 00582 else 00583 { 00584 before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); 00585 after = QRect( x(), y(), width(), pm.height() ); 00586 } 00587 00588 lf = (after.left() - before.left())/step; 00589 rf = (after.right() - before.right())/step; 00590 tf = (after.top() - before.top())/step; 00591 bf = (after.bottom() - before.bottom())/step; 00592 00593 grabXServer(); 00594 00595 QRect area = before; 00596 QRect area2; 00597 QPixmap pm2; 00598 00599 QTime t; 00600 t.start(); 00601 float diff; 00602 00603 QPainter p ( workspace()->desktopWidget() ); 00604 bool need_to_clear = FALSE; 00605 QPixmap pm3; 00606 do 00607 { 00608 if (area2 != area) 00609 { 00610 pm = animationPixmap( area.width() ); 00611 pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() ); 00612 p.drawPixmap( area.x(), area.y(), pm ); 00613 if ( need_to_clear ) 00614 { 00615 p.drawPixmap( area2.x(), area2.y(), pm3 ); 00616 need_to_clear = FALSE; 00617 } 00618 area2 = area; 00619 } 00620 XFlush(qt_xdisplay()); 00621 XSync( qt_xdisplay(), FALSE ); 00622 diff = t.elapsed(); 00623 if (diff > step) 00624 diff = step; 00625 area.setLeft(before.left() + int(diff*lf)); 00626 area.setRight(before.right() + int(diff*rf)); 00627 area.setTop(before.top() + int(diff*tf)); 00628 area.setBottom(before.bottom() + int(diff*bf)); 00629 if (area2 != area ) 00630 { 00631 if ( area2.intersects( area ) ) 00632 p.drawPixmap( area2.x(), area2.y(), pm2 ); 00633 else 00634 { // no overlap, we can clear later to avoid flicker 00635 pm3 = pm2; 00636 need_to_clear = TRUE; 00637 } 00638 } 00639 } while ( t.elapsed() < step); 00640 if (area2 == area || need_to_clear ) 00641 p.drawPixmap( area2.x(), area2.y(), pm2 ); 00642 00643 p.end(); 00644 ungrabXServer(); 00645 } 00646 00647 00651 QPixmap Client::animationPixmap( int w ) 00652 { 00653 QFont font = options->font(isActive()); 00654 QFontMetrics fm( font ); 00655 QPixmap pm( w, fm.lineSpacing() ); 00656 pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) ); 00657 QPainter p( &pm ); 00658 p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() )); 00659 p.setFont(options->font(isActive())); 00660 p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() ); 00661 return pm; 00662 } 00663 00664 00665 bool Client::isShadeable() const 00666 { 00667 return !isSpecialWindow() && !noBorder(); 00668 } 00669 00670 void Client::setShade( ShadeMode mode ) 00671 { 00672 if( !isShadeable()) 00673 return; 00674 if( shade_mode == mode ) 00675 return; 00676 bool was_shade = isShade(); 00677 ShadeMode was_shade_mode = shade_mode; 00678 shade_mode = mode; 00679 if( was_shade == isShade()) 00680 return; // no real change in shaded state 00681 00682 if( shade_mode == ShadeNormal ) 00683 { 00684 if ( isShown( true ) && isOnCurrentDesktop()) 00685 Notify::raise( Notify::ShadeUp ); 00686 } 00687 else if( shade_mode == ShadeNone ) 00688 { 00689 if( isShown( true ) && isOnCurrentDesktop()) 00690 Notify::raise( Notify::ShadeDown ); 00691 } 00692 00693 assert( decoration != NULL ); // noborder windows can't be shaded 00694 ++block_geometry; 00695 // decorations may turn off some borders when shaded 00696 decoration->borders( border_left, border_right, border_top, border_bottom ); 00697 00698 int as = options->animateShade? 10 : 1; 00699 // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere 00700 if ( isShade()) 00701 { // shade_mode == ShadeNormal 00702 int h = height(); 00703 shade_geometry_change = true; 00704 QSize s( sizeForClientSize( QSize( clientSize().width(), 0), SizemodeShaded ) ); 00705 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify 00706 XUnmapWindow( qt_xdisplay(), wrapper ); 00707 XUnmapWindow( qt_xdisplay(), client ); 00708 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); 00709 // FRAME repaint( FALSE ); 00710 // bool wasStaticContents = testWFlags( WStaticContents ); 00711 // setWFlags( WStaticContents ); 00712 int step = QMAX( 4, QABS( h - s.height() ) / as )+1; 00713 do 00714 { 00715 h -= step; 00716 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); 00717 resizeDecoration( QSize( s.width(), h )); 00718 QApplication::syncX(); 00719 } while ( h > s.height() + step ); 00720 // if ( !wasStaticContents ) 00721 // clearWFlags( WStaticContents ); 00722 shade_geometry_change = false; 00723 plainResize( s ); 00724 if( isActive()) 00725 { 00726 if( was_shade_mode == ShadeHover ) 00727 workspace()->activateNextClient( this ); 00728 else 00729 workspace()->focusToNull(); 00730 } 00731 } 00732 else 00733 { 00734 int h = height(); 00735 shade_geometry_change = true; 00736 QSize s( sizeForClientSize( clientSize(), SizemodeShaded )); 00737 // FRAME bool wasStaticContents = testWFlags( WStaticContents ); 00738 // setWFlags( WStaticContents ); 00739 int step = QMAX( 4, QABS( h - s.height() ) / as )+1; 00740 do 00741 { 00742 h += step; 00743 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); 00744 resizeDecoration( QSize( s.width(), h )); 00745 // assume a border 00746 // we do not have time to wait for X to send us paint events 00747 // FRAME repaint( 0, h - step-5, width(), step+5, TRUE); 00748 QApplication::syncX(); 00749 } while ( h < s.height() - step ); 00750 // if ( !wasStaticContents ) 00751 // clearWFlags( WStaticContents ); 00752 shade_geometry_change = false; 00753 plainResize( s ); 00754 if( shade_mode == ShadeHover || shade_mode == ShadeActivated ) 00755 setActive( TRUE ); 00756 XMapWindow( qt_xdisplay(), wrapperId()); 00757 XMapWindow( qt_xdisplay(), window()); 00758 if ( isActive() ) 00759 workspace()->requestFocus( this ); 00760 } 00761 --block_geometry; 00762 setGeometry( geometry(), ForceGeometrySet ); 00763 info->setState( isShade() ? NET::Shaded : 0, NET::Shaded ); 00764 info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden ); 00765 setMappingState( isShown( false ) && isOnCurrentDesktop() ? NormalState : IconicState ); 00766 updateAllowedActions(); 00767 workspace()->updateMinimizedOfTransients( this ); 00768 decoration->shadeChange(); 00769 } 00770 00771 void Client::shadeHover() 00772 { 00773 setShade( ShadeHover ); 00774 delete shadeHoverTimer; 00775 shadeHoverTimer = 0; 00776 } 00777 00778 void Client::toggleShade() 00779 { 00780 // if the mode is ShadeHover or ShadeActive, cancel shade too 00781 setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone ); 00782 } 00783 00784 void Client::virtualDesktopChange() 00785 { 00786 if( hidden || minimized ) 00787 return; // no visibility change 00788 // from here it can be only shaded or normally shown 00789 if( isOnCurrentDesktop()) 00790 { 00791 if( !isShade()) 00792 setMappingState( NormalState ); 00793 rawShow(); 00794 } 00795 else 00796 { 00797 if( !isShade()) 00798 setMappingState( IconicState ); 00799 rawHide(); 00800 } 00801 } 00802 00807 void Client::setMappingState(int s) 00808 { 00809 assert( client != None ); 00810 if( mapping_state == s ) 00811 return; 00812 bool was_unmanaged = ( mapping_state == WithdrawnState ); 00813 mapping_state = s; 00814 if( mapping_state == WithdrawnState ) 00815 { 00816 XDeleteProperty( qt_xdisplay(), window(), qt_wm_state ); 00817 return; 00818 } 00819 assert( s == NormalState || s == IconicState ); 00820 00821 unsigned long data[2]; 00822 data[0] = (unsigned long) s; 00823 data[1] = (unsigned long) None; 00824 XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32, 00825 PropModeReplace, (unsigned char *)data, 2); 00826 00827 if( was_unmanaged ) // force setting the geometry, manage() did block_geometry = 1 00828 { 00829 assert( block_geometry == 1 ); 00830 --block_geometry; 00831 setGeometry( frame_geometry, ForceGeometrySet ); 00832 } 00833 } 00834 00839 void Client::rawShow() 00840 { 00841 if( decoration != NULL ) 00842 decoration->widget()->show(); // not really necessary, but let it know the state 00843 XMapWindow( qt_xdisplay(), frame ); 00844 if( !isShade()) 00845 { 00846 XMapWindow( qt_xdisplay(), wrapper ); 00847 XMapWindow( qt_xdisplay(), client ); 00848 } 00849 } 00850 00856 void Client::rawHide() 00857 { 00858 // Here it may look like a race condition, as some other client might try to unmap 00859 // the window between these two XSelectInput() calls. However, they're supposed to 00860 // use XWithdrawWindow(), which also sends a synthetic event to the root window, 00861 // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify 00862 // will be missed is also very minimal, so I don't think it's needed to grab the server 00863 // here. 00864 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify 00865 XUnmapWindow( qt_xdisplay(), frame ); 00866 XUnmapWindow( qt_xdisplay(), wrapper ); 00867 XUnmapWindow( qt_xdisplay(), client ); 00868 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); 00869 if( decoration != NULL ) 00870 decoration->widget()->hide(); // not really necessary, but let it know the state 00871 workspace()->clientHidden( this ); 00872 } 00873 00874 static void sendClientMessage(Window w, Atom a, long x) 00875 { 00876 XEvent ev; 00877 long mask; 00878 00879 memset(&ev, 0, sizeof(ev)); 00880 ev.xclient.type = ClientMessage; 00881 ev.xclient.window = w; 00882 ev.xclient.message_type = a; 00883 ev.xclient.format = 32; 00884 ev.xclient.data.l[0] = x; 00885 ev.xclient.data.l[1] = qt_x_time; 00886 mask = 0L; 00887 if (w == qt_xrootwin()) 00888 mask = SubstructureRedirectMask; /* magic! */ 00889 XSendEvent(qt_xdisplay(), w, False, mask, &ev); 00890 } 00891 00892 /* 00893 Returns whether the window may be closed (have a close button) 00894 */ 00895 bool Client::isCloseable() const 00896 { 00897 return motif_may_close && ( !isSpecialWindow() || isOverride()); // TODO is NET::Override special? 00898 } 00899 00904 void Client::closeWindow() 00905 { 00906 if( !isCloseable()) 00907 return; 00908 // Update user time, needed for whole group, because the window may create a confirming dialog, 00909 // and this window's user time wouldn't apply to it 00910 // This is needed even for apps without support for user timestamp (e.g. nedit), so updating 00911 // user timestamp in apps on WM_DELETE_WINDOW is not an option (and I'm not sure if it would be right) 00912 group()->updateUserTime(); 00913 if ( Pdeletewindow ) 00914 { 00915 Notify::raise( Notify::Close ); 00916 sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window); 00917 pingWindow(); 00918 } 00919 else 00920 { 00921 // client will not react on wm_delete_window. We have not choice 00922 // but destroy his connection to the XServer. 00923 killWindow(); 00924 } 00925 } 00926 00927 00931 void Client::killWindow() 00932 { 00933 kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl; 00934 // not sure if we need an Notify::Kill or not.. until then, use 00935 // Notify::Close 00936 Notify::raise( Notify::Close ); 00937 00938 if( isDialog()) 00939 Notify::raise( Notify::TransDelete ); 00940 if( isNormalWindow()) 00941 Notify::raise( Notify::Delete ); 00942 killProcess( false ); 00943 // always kill this client at the server 00944 XKillClient(qt_xdisplay(), window() ); 00945 destroyClient(); 00946 } 00947 00948 // send a ping to the window using _NET_WM_PING if possible 00949 // if it doesn't respond within a reasonable time, it will be 00950 // killed 00951 void Client::pingWindow() 00952 { 00953 if( !Pping ) 00954 return; // can't ping :( 00955 if( ping_timer != NULL ) 00956 return; // pinging already 00957 ping_timer = new QTimer( this ); 00958 connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout())); 00959 ping_timer->start( 5000, true ); // give it 5 seconds 00960 ping_timestamp = qt_x_time; 00961 workspace()->sendPingToWindow( window(), ping_timestamp ); 00962 } 00963 00964 void Client::gotPing( Time timestamp ) 00965 { 00966 if( timestamp != ping_timestamp ) 00967 return; 00968 delete ping_timer; 00969 ping_timer = NULL; 00970 if( process_killer != NULL ) 00971 { 00972 process_killer->kill(); 00973 delete process_killer; 00974 process_killer = NULL; 00975 } 00976 } 00977 00978 void Client::pingTimeout() 00979 { 00980 kdDebug( 1212 ) << "Ping timeout:" << caption() << endl; 00981 delete ping_timer; 00982 ping_timer = NULL; 00983 killProcess( true, ping_timestamp ); 00984 } 00985 00986 void Client::killProcess( bool ask, Time timestamp ) 00987 { 00988 if( process_killer != NULL ) 00989 return; 00990 Q_ASSERT( !ask || timestamp != CurrentTime ); 00991 QCString machine = wmClientMachine(); 00992 pid_t pid = info->pid(); 00993 if( pid <= 0 || machine.isEmpty()) // needed properties missing 00994 return; 00995 kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl; 00996 if( !ask ) 00997 { 00998 if( machine != "localhost" ) 00999 { 01000 KProcess proc; 01001 proc << "xon" << machine << "kill" << pid; 01002 proc.start( KProcess::DontCare ); 01003 } 01004 else 01005 ::kill( pid, SIGTERM ); 01006 } 01007 else 01008 { // SELI TODO handle the window created by handler specially (on top,urgent?) 01009 process_killer = new KProcess( this ); 01010 *process_killer << KStandardDirs::findExe( "kwin_killer_helper" ) 01011 << "--pid" << QCString().setNum( pid ) << "--hostname" << machine 01012 << "--windowname" << caption().utf8() 01013 << "--applicationname" << resourceClass() 01014 << "--wid" << QCString().setNum( window()) 01015 << "--timestamp" << QCString().setNum( timestamp ); 01016 connect( process_killer, SIGNAL( processExited( KProcess* )), 01017 SLOT( processKillerExited())); 01018 if( !process_killer->start( KProcess::NotifyOnExit )) 01019 { 01020 delete process_killer; 01021 process_killer = NULL; 01022 return; 01023 } 01024 } 01025 } 01026 01027 void Client::processKillerExited() 01028 { 01029 kdDebug( 1212 ) << "Killer exited" << endl; 01030 delete process_killer; 01031 process_killer = NULL; 01032 } 01033 01034 void Client::setSkipTaskbar( bool b, bool from_outside ) 01035 { 01036 if( from_outside ) 01037 original_skip_taskbar = b; 01038 if ( b == skipTaskbar() ) 01039 return; 01040 skip_taskbar = b; 01041 info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar ); 01042 } 01043 01044 void Client::setSkipPager( bool b ) 01045 { 01046 if ( b == skipPager() ) 01047 return; 01048 skip_pager = b; 01049 info->setState( b?NET::SkipPager:0, NET::SkipPager ); 01050 } 01051 01052 void Client::setModal( bool m ) 01053 { // Qt-3.2 can have even modal normal windows :( 01054 if( modal == m ) 01055 return; 01056 modal = m; 01057 if( !modal ) 01058 return; 01059 // changing modality for a mapped window is weird (?) 01060 // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG 01061 } 01062 01063 void Client::toggleOnAllDesktops() 01064 { 01065 setOnAllDesktops( !isOnAllDesktops()); 01066 } 01067 01068 void Client::setDesktop( int desktop ) 01069 { 01070 if( desktop != NET::OnAllDesktops ) // do range check 01071 desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop )); 01072 if( desk == desktop ) 01073 return; 01074 int was_desk = desk; 01075 desk = desktop; 01076 info->setDesktop( desktop ); 01077 if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops )) 01078 { // onAllDesktops changed 01079 if ( isShown( true )) 01080 Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops ); 01081 workspace()->updateOnAllDesktopsOfTransients( this ); 01082 } 01083 if( decoration != NULL ) 01084 decoration->desktopChange(); 01085 virtualDesktopChange(); // hide/show if needed 01086 } 01087 01088 void Client::setOnAllDesktops( bool b ) 01089 { 01090 if(( b && isOnAllDesktops()) 01091 || ( !b && !isOnAllDesktops())) 01092 return; 01093 if( b ) 01094 setDesktop( NET::OnAllDesktops ); 01095 else 01096 setDesktop( workspace()->currentDesktop()); 01097 } 01098 01099 bool Client::isOnCurrentDesktop() const 01100 { 01101 return isOnDesktop( workspace()->currentDesktop()); 01102 } 01103 01108 void Client::takeFocus( bool force, allowed_t ) 01109 { 01110 if ( !force && ( isTopMenu() || isDock() || isSplash()) ) 01111 return; // toplevel menus and dock windows don't take focus if not forced 01112 01113 #ifndef NDEBUG 01114 static Time previous_focus_timestamp; 01115 static Client* previous_client; 01116 if( previous_focus_timestamp == qt_x_time && previous_client != this ) 01117 { 01118 kdWarning( 1212 ) << "Repeated use of the same X timestamp for focus" << endl; 01119 kdDebug( 1212 ) << kdBacktrace() << endl; 01120 } 01121 previous_focus_timestamp = qt_x_time; 01122 previous_client = this; 01123 #endif 01124 if ( input ) 01125 { 01126 XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, qt_x_time ); 01127 } 01128 if ( Ptakefocus ) 01129 sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus); 01130 } 01131 01139 bool Client::providesContextHelp() const 01140 { 01141 return Pcontexthelp; 01142 } 01143 01144 01151 void Client::showContextHelp() 01152 { 01153 if ( Pcontexthelp ) 01154 { 01155 sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help); 01156 QWhatsThis::enterWhatsThisMode(); // SELI? 01157 } 01158 } 01159 01160 01165 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); 01166 01167 void Client::fetchName() 01168 { 01169 QString s; 01170 01171 if ( info->name() && info->name()[ 0 ] != '\0' ) 01172 s = QString::fromUtf8( info->name() ); 01173 else 01174 s = KWin::readNameProperty( window(), XA_WM_NAME ); 01175 if ( s != cap_normal ) 01176 { 01177 bool reset_name = cap_normal.isEmpty(); 01178 for( unsigned int i = 0; 01179 i < s.length(); 01180 ++i ) 01181 if( !s[ i ].isPrint()) 01182 s[ i ] = ' '; 01183 cap_normal = s; 01184 bool was_suffix = ( !cap_suffix.isEmpty()); 01185 cap_suffix = QString::null; 01186 if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) 01187 { 01188 int i = 2; 01189 do 01190 { 01191 cap_suffix = " <" + QString::number(i) + ">"; 01192 i++; 01193 } while ( workspace()->findClient( FetchNameInternalPredicate( this ))); 01194 info->setVisibleName( caption().utf8() ); 01195 reset_name = false; 01196 } 01197 if(( was_suffix && cap_suffix.isEmpty() 01198 || reset_name )) // if it was new window, it may have old value still set, if the window is reused 01199 { 01200 info->setVisibleName( "" ); // remove 01201 info->setVisibleIconName( "" ); // remove 01202 } 01203 else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set 01204 info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() ); 01205 01206 if( isManaged() && decoration != NULL ) 01207 decoration->captionChange(); 01208 } 01209 } 01210 01211 void Client::fetchIconicName() 01212 { 01213 QString s; 01214 01215 if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) 01216 s = QString::fromUtf8( info->iconName() ); 01217 else 01218 s = KWin::readNameProperty( window(), XA_WM_ICON_NAME ); 01219 if ( s != cap_iconic ) 01220 { 01221 cap_iconic = s; 01222 if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set 01223 info->setVisibleIconName( ( s + cap_suffix ).utf8() ); 01224 } 01225 } 01226 01229 QString Client::caption() const 01230 { 01231 return cap_normal + cap_suffix; 01232 } 01233 01234 void Client::getWMHints() 01235 { 01236 XWMHints *hints = XGetWMHints(qt_xdisplay(), window() ); 01237 input = true; 01238 window_group = None; 01239 urgency = false; 01240 if ( hints ) 01241 { 01242 if( hints->flags & InputHint ) 01243 input = hints->input; 01244 if( hints->flags & WindowGroupHint ) 01245 window_group = hints->window_group; 01246 urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield 01247 XFree( (char*)hints ); 01248 } 01249 checkGroup(); 01250 updateUrgency(); 01251 updateAllowedActions(); // group affects isMinimizable() 01252 } 01253 01254 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon ) 01255 { 01256 // get the icons, allow scaling 01257 if( icon != NULL ) 01258 *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints ); 01259 if( miniicon != NULL ) 01260 if( icon == NULL || !icon->isNull()) 01261 *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints ); 01262 else 01263 *miniicon = QPixmap(); 01264 } 01265 01266 void Client::getIcons() 01267 { 01268 // first read icons from the window itself 01269 readIcons( window(), &icon_pix, &miniicon_pix ); 01270 if( icon_pix.isNull()) 01271 { // then try window group 01272 icon_pix = group()->icon(); 01273 miniicon_pix = group()->miniIcon(); 01274 } 01275 if( icon_pix.isNull() && isTransient()) 01276 { // then mainclients 01277 ClientList mainclients = mainClients(); 01278 for( ClientList::ConstIterator it = mainclients.begin(); 01279 it != mainclients.end() && icon_pix.isNull(); 01280 ++it ) 01281 { 01282 icon_pix = (*it)->icon(); 01283 miniicon_pix = (*it)->miniIcon(); 01284 } 01285 } 01286 if( icon_pix.isNull()) 01287 { // and if nothing else, load icon from classhint or xapp icon 01288 icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp ); 01289 miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp ); 01290 } 01291 if( isManaged() && decoration != NULL ) 01292 decoration->iconChange(); 01293 } 01294 01295 void Client::getWindowProtocols() 01296 { 01297 Atom *p; 01298 int i,n; 01299 01300 Pdeletewindow = 0; 01301 Ptakefocus = 0; 01302 Pcontexthelp = 0; 01303 Pping = 0; 01304 01305 if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n)) 01306 { 01307 for (i = 0; i < n; i++) 01308 if (p[i] == atoms->wm_delete_window) 01309 Pdeletewindow = 1; 01310 else if (p[i] == atoms->wm_take_focus) 01311 Ptakefocus = 1; 01312 else if (p[i] == atoms->net_wm_context_help) 01313 Pcontexthelp = 1; 01314 else if (p[i] == atoms->net_wm_ping) 01315 Pping = 1; 01316 if (n>0) 01317 XFree(p); 01318 } 01319 } 01320 01321 static int nullErrorHandler(Display *, XErrorEvent *) 01322 { 01323 return 0; 01324 } 01325 01329 QCString Client::staticWindowRole(WId w) 01330 { 01331 return getStringProperty(w, qt_window_role); 01332 } 01333 01337 QCString Client::staticSessionId(WId w) 01338 { 01339 return getStringProperty(w, qt_sm_client_id); 01340 } 01341 01345 QCString Client::staticWmCommand(WId w) 01346 { 01347 return getStringProperty(w, XA_WM_COMMAND, ' '); 01348 } 01349 01354 QCString Client::staticWmClientMachine(WId w) 01355 { 01356 QCString result = getStringProperty(w, XA_WM_CLIENT_MACHINE); 01357 if (result.isEmpty()) 01358 { 01359 result = "localhost"; 01360 } 01361 else 01362 { 01363 // special name for the local machine (localhost) 01364 char hostnamebuf[80]; 01365 if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) 01366 { 01367 hostnamebuf[sizeof(hostnamebuf)-1] = 0; 01368 if (result == hostnamebuf) 01369 result = "localhost"; 01370 char *dot = strchr(hostnamebuf, '.'); 01371 if (dot && !(*dot = 0) && result == hostnamebuf) 01372 result = "localhost"; 01373 } 01374 } 01375 return result; 01376 } 01377 01381 Window Client::staticWmClientLeader(WId w) 01382 { 01383 Atom type; 01384 int format, status; 01385 unsigned long nitems = 0; 01386 unsigned long extra = 0; 01387 unsigned char *data = 0; 01388 Window result = w; 01389 XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler); 01390 status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000, 01391 FALSE, XA_WINDOW, &type, &format, 01392 &nitems, &extra, &data ); 01393 XSetErrorHandler(oldHandler); 01394 if (status == Success ) 01395 { 01396 if (data && nitems > 0) 01397 result = *((Window*) data); 01398 XFree(data); 01399 } 01400 return result; 01401 } 01402 01403 01404 void Client::getWmClientLeader() 01405 { 01406 wmClientLeaderWin = staticWmClientLeader(window()); 01407 } 01408 01413 QCString Client::sessionId() 01414 { 01415 QCString result = staticSessionId(window()); 01416 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) 01417 result = staticSessionId(wmClientLeaderWin); 01418 return result; 01419 } 01420 01425 QCString Client::wmCommand() 01426 { 01427 QCString result = staticWmCommand(window()); 01428 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) 01429 result = staticWmCommand(wmClientLeaderWin); 01430 return result; 01431 } 01432 01437 QCString Client::wmClientMachine() const 01438 { 01439 QCString result = staticWmClientMachine(window()); 01440 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) 01441 result = staticWmClientMachine(wmClientLeaderWin); 01442 return result; 01443 } 01444 01449 Window Client::wmClientLeader() const 01450 { 01451 if (wmClientLeaderWin) 01452 return wmClientLeaderWin; 01453 return window(); 01454 } 01455 01456 bool Client::wantsTabFocus() const 01457 { 01458 return ( isNormalWindow() || isDialog() || isOverride()) 01459 && ( input || Ptakefocus ) && !skip_taskbar; 01460 } 01461 01462 01463 bool Client::wantsInput() const 01464 { 01465 return input; 01466 } 01467 01472 bool Client::isMovable() const 01473 { 01474 return motif_may_move && !isFullScreen() && 01475 ( !isSpecialWindow() || isOverride() || isSplash() || isToolbar()) && // allow moving of splashscreens :) 01476 ( maximizeMode() != MaximizeFull || options->moveResizeMaximizedWindows() ); 01477 } 01478 01479 bool Client::isDesktop() const 01480 { 01481 return windowType() == NET::Desktop; 01482 } 01483 01484 bool Client::isDock() const 01485 { 01486 return windowType() == NET::Dock; 01487 } 01488 01489 bool Client::isTopMenu() const 01490 { 01491 return windowType() == NET::TopMenu; 01492 } 01493 01494 01495 bool Client::isMenu() const 01496 { 01497 return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. 01498 } 01499 01500 bool Client::isToolbar() const 01501 { 01502 return windowType() == NET::Toolbar; 01503 } 01504 01505 bool Client::isOverride() const 01506 { 01507 return windowType() == NET::Override; 01508 } 01509 01510 bool Client::isSplash() const 01511 { 01512 return windowType() == NET::Splash; 01513 } 01514 01515 bool Client::isUtility() const 01516 { 01517 return windowType() == NET::Utility; 01518 } 01519 01520 bool Client::isDialog() const 01521 { 01522 return windowType() == NET::Dialog; 01523 } 01524 01525 bool Client::isNormalWindow() const 01526 { 01527 return windowType() == NET::Normal; 01528 } 01529 01530 bool Client::isSpecialWindow() const 01531 { 01532 return isDesktop() || isDock() || isSplash() || isTopMenu() 01533 || ( isOverride() && !isFullScreen())// SELI is NET::Override special or not? 01534 || isToolbar(); // TODO 01535 } 01536 01537 NET::WindowType Client::windowType( bool strict, int supported_types ) const 01538 { 01539 NET::WindowType wt = info->windowType( supported_types ); 01540 // TODO is this 'strict' really needed (and used?) 01541 if( !strict ) 01542 { // hacks here 01543 if( wt == NET::Menu ) 01544 { 01545 // ugly hack to support the times when NET::Menu meant NET::TopMenu 01546 // if it's as wide as the screen, not very high and has its upper-left 01547 // corner a bit above the screen's upper-left cornet, it's a topmenu 01548 if( x() == 0 && y() < 0 && y() > -10 && height() < 100 01549 && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 ) 01550 wt = NET::TopMenu; 01551 } 01552 const char* const oo_prefix = "openoffice.org"; // QCString has no startsWith() 01553 // oo_prefix is lowercase, because resourceClass() is forced to be lowercase 01554 if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog ) 01555 wt = NET::Normal; // see bug #66065 01556 if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec 01557 wt = isTransient() ? NET::Dialog : NET::Normal; 01558 } 01559 return wt; 01560 } 01561 01566 void Client::setCursor( Position m ) 01567 { 01568 if ( !isResizable() || isShade() || noBorder()) 01569 { 01570 setCursor( arrowCursor ); 01571 return; 01572 } 01573 switch ( m ) 01574 { 01575 case PositionTopLeft: 01576 case PositionBottomRight: 01577 setCursor( sizeFDiagCursor ); 01578 break; 01579 case PositionBottomLeft: 01580 case PositionTopRight: 01581 setCursor( sizeBDiagCursor ); 01582 break; 01583 case PositionTop: 01584 case PositionBottom: 01585 setCursor( sizeVerCursor ); 01586 break; 01587 case PositionLeft: 01588 case PositionRight: 01589 setCursor( sizeHorCursor ); 01590 break; 01591 default: 01592 if( buttonDown ) 01593 setCursor( sizeAllCursor ); 01594 else 01595 setCursor( arrowCursor ); 01596 break; 01597 } 01598 } 01599 01600 // TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit? 01601 void Client::setCursor( const QCursor& c ) 01602 { 01603 if( c.handle() == cursor.handle()) 01604 return; 01605 cursor = c; 01606 if( decoration != NULL ) 01607 decoration->widget()->setCursor( cursor ); 01608 XDefineCursor( qt_xdisplay(), frameId(), cursor.handle()); 01609 } 01610 01611 Client::Position Client::mousePosition( const QPoint& p ) const 01612 { 01613 if( decoration != NULL ) 01614 return decoration->mousePosition( p ); 01615 return PositionCenter; 01616 } 01617 01618 void Client::updateAllowedActions( bool force ) 01619 { 01620 if( !isManaged() && !force ) 01621 return; 01622 unsigned long old_allowed_actions = allowed_actions; 01623 allowed_actions = 0; 01624 if( isMovable()) 01625 allowed_actions |= NET::ActionMove; 01626 if( isResizable()) 01627 allowed_actions |= NET::ActionResize; 01628 if( isMinimizable()) 01629 allowed_actions |= NET::ActionMinimize; 01630 if( isShadeable()) 01631 allowed_actions |= NET::ActionShade; 01632 // sticky state not supported 01633 if( isMaximizable()) 01634 allowed_actions |= NET::ActionMax; 01635 if( userCanSetFullScreen()) 01636 allowed_actions |= NET::ActionFullScreen; 01637 allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.) 01638 if( isCloseable()) 01639 allowed_actions |= NET::ActionClose; 01640 if( old_allowed_actions == allowed_actions ) 01641 return; 01642 // TODO this could be delayed and compressed - it's only for pagers etc. anyway 01643 info->setAllowedActions( allowed_actions ); 01644 // TODO this should also tell the decoration, so that it can update the buttons 01645 } 01646 01647 void Client::autoRaise() 01648 { 01649 workspace()->raiseClient( this ); 01650 delete autoRaiseTimer; 01651 autoRaiseTimer = 0; 01652 } 01653 01654 void Client::cancelAutoRaise() 01655 { 01656 delete autoRaiseTimer; 01657 autoRaiseTimer = 0; 01658 } 01659 01660 #ifdef NDEBUG 01661 kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; } 01662 kndbgstream& operator<<( kndbgstream& stream, const ClientList& ) { return stream; } 01663 kndbgstream& operator<<( kndbgstream& stream, const ConstClientList& ) { return stream; } 01664 #else 01665 kdbgstream& operator<<( kdbgstream& stream, const Client* cl ) 01666 { 01667 if( cl == NULL ) 01668 return stream << "\'NULL_CLIENT\'"; 01669 return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'"; 01670 } 01671 kdbgstream& operator<<( kdbgstream& stream, const ClientList& list ) 01672 { 01673 stream << "LIST:("; 01674 bool first = true; 01675 for( ClientList::ConstIterator it = list.begin(); 01676 it != list.end(); 01677 ++it ) 01678 { 01679 if( !first ) 01680 stream << ":"; 01681 first = false; 01682 stream << *it; 01683 } 01684 stream << ")"; 01685 return stream; 01686 } 01687 kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list ) 01688 { 01689 stream << "LIST:("; 01690 bool first = true; 01691 for( ConstClientList::ConstIterator it = list.begin(); 01692 it != list.end(); 01693 ++it ) 01694 { 01695 if( !first ) 01696 stream << ":"; 01697 first = false; 01698 stream << *it; 01699 } 01700 stream << ")"; 01701 return stream; 01702 } 01703 #endif 01704 01705 QPixmap * kwin_get_menu_pix_hack() 01706 { 01707 static QPixmap p; 01708 if ( p.isNull() ) 01709 p = SmallIcon( "bx2" ); 01710 return &p; 01711 } 01712 01713 } // namespace 01714 01715 #include "client.moc"
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 16 15:59:31 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003