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