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