00001
00002
00003
00004
00005
00006
00007
00008
00009
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
00034
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
00047
00048
00049
00050
00051
00052
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 transient_for( NULL ),
00078 transient_for_id( None ),
00079 original_transient_for_id( None ),
00080 in_group( NULL ),
00081 window_group( None ),
00082 in_layer( UnknownLayer ),
00083 ping_timer( NULL ),
00084 process_killer( NULL ),
00085 user_time( CurrentTime ),
00086 allowed_actions( 0 ),
00087 block_geometry( 0 ),
00088 shade_geometry_change( false ),
00089 border_left( 0 ),
00090 border_right( 0 ),
00091 border_top( 0 ),
00092 border_bottom( 0 )
00093
00094 {
00095 autoRaiseTimer = 0;
00096 shadeHoverTimer = 0;
00097
00098
00099 mapping_state = WithdrawnState;
00100 desk = 0;
00101
00102 mode = PositionCenter;
00103 buttonDown = FALSE;
00104 moveResizeMode = FALSE;
00105
00106 info = NULL;
00107
00108 shade_mode = ShadeNone;
00109 active = FALSE;
00110 keep_above = FALSE;
00111 keep_below = FALSE;
00112 is_shape = FALSE;
00113 motif_may_move = TRUE;
00114 motif_may_resize = TRUE;
00115 motif_may_close = TRUE;
00116 fullscreen_mode = FullScreenNone;
00117 skip_taskbar = FALSE;
00118 original_skip_taskbar = false;
00119 minimized = false;
00120 hidden = false;
00121 modal = false;
00122 noborder = false;
00123 user_noborder = false;
00124 not_obscured = false;
00125 urgency = false;
00126
00127 Pdeletewindow = 0;
00128 Ptakefocus = 0;
00129 Pcontexthelp = 0;
00130 Pping = 0;
00131 input = FALSE;
00132 store_settings = FALSE;
00133 skip_pager = FALSE;
00134
00135
00136 max_mode = MaximizeRestore;
00137
00138 cmap = None;
00139
00140 frame_geometry = QRect( 0, 0, 100, 100 );
00141 client_size = QSize( 100, 100 );
00142
00143
00144 }
00145
00149 Client::~Client()
00150 {
00151 assert(!moveResizeMode);
00152 assert( client == None );
00153 assert( frame == None && wrapper == None );
00154 assert( decoration == NULL );
00155 assert( block_geometry == 0 );
00156 delete info;
00157 delete bridge;
00158 }
00159
00160
00161 void Client::deleteClient( Client* c, allowed_t )
00162 {
00163 delete c;
00164 }
00165
00169 void Client::releaseWindow( bool on_shutdown )
00170 {
00171 if (moveResizeMode)
00172 leaveMoveResize();
00173 setModal( false );
00174 hidden = true;
00175 if( !on_shutdown )
00176 workspace()->clientHidden( this );
00177 XUnmapWindow( qt_xdisplay(), frameId());
00178 destroyDecoration();
00179 cleanGrouping();
00180 if( !on_shutdown )
00181 {
00182 workspace()->removeClient( this, Allowed );
00183
00184
00185 info->setDesktop( 0 );
00186 desk = 0;
00187 info->setState( 0, info->state());
00188 }
00189 XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_user_creation_time);
00190
00191 XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y());
00192 XRemoveFromSaveSet( qt_xdisplay(), client );
00193 XSelectInput( qt_xdisplay(), client, NoEventMask );
00194 if( on_shutdown )
00195 {
00196 XMapWindow( qt_xdisplay(), client );
00197
00198 }
00199 else
00200 {
00201
00202
00203 XUnmapWindow( qt_xdisplay(), client );
00204 }
00205 setMappingState( WithdrawnState );
00206 client = None;
00207 XDestroyWindow( qt_xdisplay(), wrapper );
00208 wrapper = None;
00209 XDestroyWindow( qt_xdisplay(), frame );
00210 frame = None;
00211 deleteClient( this, Allowed );
00212 }
00213
00214
00215
00216 void Client::destroyClient()
00217 {
00218 if (moveResizeMode)
00219 leaveMoveResize();
00220 ++block_geometry;
00221 setModal( false );
00222 hidden = true;
00223 workspace()->clientHidden( this );
00224 destroyDecoration();
00225 cleanGrouping();
00226 workspace()->removeClient( this, Allowed );
00227 client = None;
00228 XDestroyWindow( qt_xdisplay(), wrapper );
00229 wrapper = None;
00230 XDestroyWindow( qt_xdisplay(), frame );
00231 frame = None;
00232 --block_geometry;
00233 deleteClient( this, Allowed );
00234 }
00235
00236 void Client::updateDecoration( bool check_workspace_pos, bool force )
00237 {
00238 if( !force && (( decoration == NULL && noBorder())
00239 || ( decoration != NULL && !noBorder())))
00240 return;
00241 bool do_show = false;
00242 ++block_geometry;
00243 if( force )
00244 destroyDecoration();
00245 if( !noBorder())
00246 {
00247 decoration = workspace()->createDecoration( bridge );
00248
00249 decoration->init();
00250 decoration->widget()->installEventFilter( this );
00251 XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 );
00252 decoration->widget()->lower();
00253 decoration->borders( border_left, border_right, border_top, border_bottom );
00254 int save_workarea_diff_x = workarea_diff_x;
00255 int save_workarea_diff_y = workarea_diff_y;
00256 move( calculateGravitation( false ));
00257 if( !isShade())
00258 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00259 else
00260 plainResize( sizeForClientSize( QSize( clientSize().width(), 0 ), SizemodeShaded ), ForceGeometrySet );
00261 workarea_diff_x = save_workarea_diff_x;
00262 workarea_diff_y = save_workarea_diff_y;
00263 do_show = true;
00264 }
00265 else
00266 destroyDecoration();
00267 if( check_workspace_pos )
00268 checkWorkspacePosition();
00269 --block_geometry;
00270 setGeometry( geometry(), ForceGeometrySet );
00271 if( do_show )
00272 decoration->widget()->show();
00273 updateFrameStrut();
00274 }
00275
00276 void Client::destroyDecoration()
00277 {
00278 if( decoration != NULL )
00279 {
00280 delete decoration;
00281 decoration = NULL;
00282 QPoint grav = calculateGravitation( true );
00283 border_left = border_right = border_top = border_bottom = 0;
00284 setMask( QRegion());
00285 int save_workarea_diff_x = workarea_diff_x;
00286 int save_workarea_diff_y = workarea_diff_y;
00287 if( !isShade())
00288 plainResize( clientSize(), ForceGeometrySet );
00289 else
00290 plainResize( QSize( clientSize().width(), 0 ), ForceGeometrySet );
00291 move( grav );
00292 workarea_diff_x = save_workarea_diff_x;
00293 workarea_diff_y = save_workarea_diff_y;
00294 }
00295 }
00296
00297 void Client::checkBorderSizes()
00298 {
00299 if( decoration == NULL )
00300 return;
00301 int new_left, new_right, new_top, new_bottom;
00302 decoration->borders( new_left, new_right, new_top, new_bottom );
00303 if( new_left == border_left && new_right == border_right
00304 && new_top == border_top && new_bottom == border_bottom )
00305 return;
00306 ++block_geometry;
00307 move( calculateGravitation( true ));
00308 border_left = new_left;
00309 border_right = new_right;
00310 border_top = new_top;
00311 border_bottom = new_bottom;
00312 move( calculateGravitation( false ));
00313 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00314 checkWorkspacePosition();
00315 --block_geometry;
00316 setGeometry( geometry(), ForceGeometrySet );
00317 }
00318
00319 void Client::detectNoBorder()
00320 {
00321 if( Shape::hasShape( window()) || Motif::noBorder( window()))
00322 {
00323 noborder = true;
00324 return;
00325 }
00326 switch( windowType())
00327 {
00328 case NET::Desktop :
00329 case NET::Dock :
00330 case NET::TopMenu :
00331 case NET::Override :
00332 case NET::Splash :
00333 noborder = true;
00334 break;
00335 case NET::Unknown :
00336 case NET::Normal :
00337 case NET::Toolbar :
00338 case NET::Menu :
00339 case NET::Dialog :
00340 case NET::Utility :
00341 noborder = false;
00342 break;
00343 default:
00344 assert( false );
00345 }
00346 }
00347
00348 void Client::updateFrameStrut()
00349 {
00350
00351 NETStrut strut;
00352 strut.left = border_left;
00353 strut.right = border_right;
00354 strut.top = border_top;
00355 strut.bottom = border_bottom;
00356 info->setKDEFrameStrut( strut );
00357 }
00358
00359
00360
00361
00362
00363
00364 void Client::resizeDecoration( const QSize& s )
00365 {
00366 if( decoration == NULL )
00367 return;
00368 QSize oldsize = decoration->widget()->size();
00369 decoration->resize( s );
00370 if( oldsize == s )
00371 {
00372 QResizeEvent e( s, oldsize );
00373 QApplication::sendEvent( decoration->widget(), &e );
00374 }
00375 }
00376
00377 bool Client::noBorder() const
00378 {
00379 return noborder || isFullScreen() || user_noborder;
00380 }
00381
00382 bool Client::userCanSetNoBorder() const
00383 {
00384 return !noborder && !isFullScreen() && !isShade();
00385 }
00386
00387 bool Client::isUserNoBorder() const
00388 {
00389 return user_noborder;
00390 }
00391
00392 void Client::setUserNoBorder( bool set )
00393 {
00394 if( !userCanSetNoBorder())
00395 return;
00396 if( user_noborder == set )
00397 return;
00398 user_noborder = set;
00399 updateDecoration( true, false );
00400 }
00401
00402 void Client::updateShape()
00403 {
00404 if ( shape() )
00405 XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding,
00406 clientPos().x(), clientPos().y(),
00407 window(), ShapeBounding, ShapeSet);
00408 else
00409 XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00410 None, ShapeSet);
00411
00412 if( shape() && !noBorder())
00413 {
00414 noborder = true;
00415 updateDecoration( true );
00416 }
00417 }
00418
00419 void Client::setMask( const QRegion& reg, int mode )
00420 {
00421 _mask = reg;
00422 if( reg.isNull())
00423 XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00424 None, ShapeSet );
00425 else if( mode == X::Unsorted )
00426 XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00427 reg.handle(), ShapeSet );
00428 else
00429 {
00430 QMemArray< QRect > rects = reg.rects();
00431 XRectangle* xrects = new XRectangle[ rects.count() ];
00432 for( unsigned int i = 0;
00433 i < rects.count();
00434 ++i )
00435 {
00436 xrects[ i ].x = rects[ i ].x();
00437 xrects[ i ].y = rects[ i ].y();
00438 xrects[ i ].width = rects[ i ].width();
00439 xrects[ i ].height = rects[ i ].height();
00440 }
00441 XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00442 xrects, rects.count(), ShapeSet, mode );
00443 delete[] xrects;
00444 }
00445 }
00446
00447 QRegion Client::mask() const
00448 {
00449 if( _mask.isEmpty())
00450 return QRegion( 0, 0, width(), height());
00451 return _mask;
00452 }
00453
00454 void Client::hideClient( bool hide )
00455 {
00456 if( hidden == hide )
00457 return;
00458 hidden = hide;
00459 info->setState( hidden ? NET::Hidden : 0, NET::Hidden );
00460 if( hidden )
00461 {
00462 setMappingState( IconicState );
00463 rawHide();
00464 setSkipTaskbar( true, false );
00465 }
00466 else
00467 {
00468 setSkipTaskbar( original_skip_taskbar, false );
00469 if( isOnCurrentDesktop())
00470 {
00471 if( isShown( false ))
00472 setMappingState( NormalState );
00473 rawShow();
00474 }
00475 }
00476 }
00477
00478
00479
00480
00481 bool Client::isMinimizable() const
00482 {
00483 if( !wantsTabFocus()
00484 || ( isSpecialWindow() && !isOverride()))
00485 return false;
00486 if( isTransient())
00487 {
00488 ClientList mainclients = mainClients();
00489 for( ClientList::ConstIterator it = mainclients.begin();
00490 it != mainclients.end();
00491 ++it )
00492 if( (*it)->isShown( true ))
00493 return false;
00494 }
00495 return true;
00496 }
00497
00501 void Client::minimize()
00502 {
00503 if ( !isMinimizable() || isMinimized())
00504 return;
00505
00506 minimized = true;
00507
00508 Notify::raise( Notify::Minimize );
00509
00510
00511 if ( mainClients().isEmpty() && isOnCurrentDesktop())
00512 animateMinimizeOrUnminimize( true );
00513
00514 setMappingState( IconicState );
00515 info->setState( NET::Hidden, NET::Hidden );
00516 rawHide();
00517 updateAllowedActions();
00518 workspace()->updateMinimizedOfTransients( this );
00519 }
00520
00521 void Client::unminimize()
00522 {
00523 if( !isMinimized())
00524 return;
00525
00526 Notify::raise( Notify::UnMinimize );
00527 minimized = false;
00528 info->setState( 0, NET::Hidden );
00529 if( isOnCurrentDesktop())
00530 {
00531 if( mainClients().isEmpty())
00532 animateMinimizeOrUnminimize( FALSE );
00533 if( isShown( false ))
00534 setMappingState( NormalState );
00535 rawShow();
00536 }
00537 updateAllowedActions();
00538 workspace()->updateMinimizedOfTransients( this );
00539 }
00540
00541 extern bool blockAnimation;
00542
00543 void Client::animateMinimizeOrUnminimize( bool minimize )
00544 {
00545 if ( blockAnimation )
00546 return;
00547 if ( !options->animateMinimize )
00548 return;
00549
00550 if( decoration != NULL && decoration->animateMinimize( minimize ))
00551 return;
00552
00553
00554
00555
00556
00557 float lf,rf,tf,bf,step;
00558
00559 int speed = options->animateMinimizeSpeed;
00560 if ( speed > 10 )
00561 speed = 10;
00562 if ( speed < 0 )
00563 speed = 0;
00564
00565 step = 40. * (11 - speed );
00566
00567 NETRect r = info->iconGeometry();
00568 QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height );
00569 if ( !icongeom.isValid() )
00570 return;
00571
00572 QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() );
00573
00574 QRect before, after;
00575 if ( minimize )
00576 {
00577 before = QRect( x(), y(), width(), pm.height() );
00578 after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00579 }
00580 else
00581 {
00582 before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00583 after = QRect( x(), y(), width(), pm.height() );
00584 }
00585
00586 lf = (after.left() - before.left())/step;
00587 rf = (after.right() - before.right())/step;
00588 tf = (after.top() - before.top())/step;
00589 bf = (after.bottom() - before.bottom())/step;
00590
00591 grabXServer();
00592
00593 QRect area = before;
00594 QRect area2;
00595 QPixmap pm2;
00596
00597 QTime t;
00598 t.start();
00599 float diff;
00600
00601 QPainter p ( workspace()->desktopWidget() );
00602 bool need_to_clear = FALSE;
00603 QPixmap pm3;
00604 do
00605 {
00606 if (area2 != area)
00607 {
00608 pm = animationPixmap( area.width() );
00609 pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() );
00610 p.drawPixmap( area.x(), area.y(), pm );
00611 if ( need_to_clear )
00612 {
00613 p.drawPixmap( area2.x(), area2.y(), pm3 );
00614 need_to_clear = FALSE;
00615 }
00616 area2 = area;
00617 }
00618 XFlush(qt_xdisplay());
00619 XSync( qt_xdisplay(), FALSE );
00620 diff = t.elapsed();
00621 if (diff > step)
00622 diff = step;
00623 area.setLeft(before.left() + int(diff*lf));
00624 area.setRight(before.right() + int(diff*rf));
00625 area.setTop(before.top() + int(diff*tf));
00626 area.setBottom(before.bottom() + int(diff*bf));
00627 if (area2 != area )
00628 {
00629 if ( area2.intersects( area ) )
00630 p.drawPixmap( area2.x(), area2.y(), pm2 );
00631 else
00632 {
00633 pm3 = pm2;
00634 need_to_clear = TRUE;
00635 }
00636 }
00637 } while ( t.elapsed() < step);
00638 if (area2 == area || need_to_clear )
00639 p.drawPixmap( area2.x(), area2.y(), pm2 );
00640
00641 p.end();
00642 ungrabXServer();
00643 }
00644
00645
00649 QPixmap Client::animationPixmap( int w )
00650 {
00651 QFont font = options->font(isActive());
00652 QFontMetrics fm( font );
00653 QPixmap pm( w, fm.lineSpacing() );
00654 pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) );
00655 QPainter p( &pm );
00656 p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() ));
00657 p.setFont(options->font(isActive()));
00658 p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() );
00659 return pm;
00660 }
00661
00662
00663 bool Client::isShadeable() const
00664 {
00665 return !isSpecialWindow() && !noBorder();
00666 }
00667
00668 void Client::setShade( ShadeMode mode )
00669 {
00670 if( !isShadeable())
00671 return;
00672 if( shade_mode == mode )
00673 return;
00674 bool was_shade = isShade();
00675 ShadeMode was_shade_mode = shade_mode;
00676 shade_mode = mode;
00677 if( was_shade == isShade())
00678 return;
00679
00680 if( shade_mode == ShadeNormal )
00681 {
00682 if ( isShown( true ) && isOnCurrentDesktop())
00683 Notify::raise( Notify::ShadeUp );
00684 }
00685 else if( shade_mode == ShadeNone )
00686 {
00687 if( isShown( true ) && isOnCurrentDesktop())
00688 Notify::raise( Notify::ShadeDown );
00689 }
00690
00691 assert( decoration != NULL );
00692 ++block_geometry;
00693
00694 decoration->borders( border_left, border_right, border_top, border_bottom );
00695
00696 int as = options->animateShade? 10 : 1;
00697
00698 if ( isShade())
00699 {
00700 int h = height();
00701 shade_geometry_change = true;
00702 QSize s( sizeForClientSize( QSize( clientSize().width(), 0), SizemodeShaded ) );
00703 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask );
00704 XUnmapWindow( qt_xdisplay(), wrapper );
00705 XUnmapWindow( qt_xdisplay(), client );
00706 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00707
00708
00709
00710 int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00711 do
00712 {
00713 h -= step;
00714 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00715 resizeDecoration( QSize( s.width(), h ));
00716 QApplication::syncX();
00717 } while ( h > s.height() + step );
00718
00719
00720 shade_geometry_change = false;
00721 plainResize( s );
00722 if( isActive())
00723 {
00724 if( was_shade_mode == ShadeHover )
00725 workspace()->activateNextClient( this );
00726 else
00727 workspace()->focusToNull();
00728 }
00729 }
00730 else
00731 {
00732 int h = height();
00733 shade_geometry_change = true;
00734 QSize s( sizeForClientSize( clientSize(), SizemodeShaded ));
00735
00736
00737 int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00738 do
00739 {
00740 h += step;
00741 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00742 resizeDecoration( QSize( s.width(), h ));
00743
00744
00745
00746 QApplication::syncX();
00747 } while ( h < s.height() - step );
00748
00749
00750 shade_geometry_change = false;
00751 plainResize( s );
00752 if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
00753 setActive( TRUE );
00754 XMapWindow( qt_xdisplay(), wrapperId());
00755 XMapWindow( qt_xdisplay(), window());
00756 if ( isActive() )
00757 workspace()->requestFocus( this );
00758 }
00759 --block_geometry;
00760 setGeometry( geometry(), ForceGeometrySet );
00761 info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
00762 info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
00763 setMappingState( isShown( false ) && isOnCurrentDesktop() ? NormalState : IconicState );
00764 updateAllowedActions();
00765 workspace()->updateMinimizedOfTransients( this );
00766 decoration->shadeChange();
00767 }
00768
00769 void Client::shadeHover()
00770 {
00771 setShade( ShadeHover );
00772 delete shadeHoverTimer;
00773 shadeHoverTimer = 0;
00774 }
00775
00776 void Client::toggleShade()
00777 {
00778
00779 setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
00780 }
00781
00782 void Client::virtualDesktopChange()
00783 {
00784 if( hidden || minimized )
00785 return;
00786
00787 if( isOnCurrentDesktop())
00788 {
00789 if( !isShade())
00790 setMappingState( NormalState );
00791 rawShow();
00792 }
00793 else
00794 {
00795 if( !isShade())
00796 setMappingState( IconicState );
00797 rawHide();
00798 }
00799 }
00800
00805 void Client::setMappingState(int s)
00806 {
00807 assert( client != None );
00808 if( mapping_state == s )
00809 return;
00810 bool was_unmanaged = ( mapping_state == WithdrawnState );
00811 mapping_state = s;
00812 if( mapping_state == WithdrawnState )
00813 {
00814 XDeleteProperty( qt_xdisplay(), window(), qt_wm_state );
00815 return;
00816 }
00817 assert( s == NormalState || s == IconicState );
00818
00819 unsigned long data[2];
00820 data[0] = (unsigned long) s;
00821 data[1] = (unsigned long) None;
00822 XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32,
00823 PropModeReplace, (unsigned char *)data, 2);
00824
00825 if( was_unmanaged )
00826 {
00827 assert( block_geometry == 1 );
00828 --block_geometry;
00829 setGeometry( frame_geometry, ForceGeometrySet );
00830 }
00831 }
00832
00837 void Client::rawShow()
00838 {
00839 if( decoration != NULL )
00840 decoration->widget()->show();
00841 XMapWindow( qt_xdisplay(), frame );
00842 if( !isShade())
00843 {
00844 XMapWindow( qt_xdisplay(), wrapper );
00845 XMapWindow( qt_xdisplay(), client );
00846 }
00847 }
00848
00854 void Client::rawHide()
00855 {
00856
00857
00858
00859
00860
00861
00862 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask );
00863 XUnmapWindow( qt_xdisplay(), frame );
00864 XUnmapWindow( qt_xdisplay(), wrapper );
00865 XUnmapWindow( qt_xdisplay(), client );
00866 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00867 if( decoration != NULL )
00868 decoration->widget()->hide();
00869 workspace()->clientHidden( this );
00870 }
00871
00872 static void sendClientMessage(Window w, Atom a, long x)
00873 {
00874 XEvent ev;
00875 long mask;
00876
00877 memset(&ev, 0, sizeof(ev));
00878 ev.xclient.type = ClientMessage;
00879 ev.xclient.window = w;
00880 ev.xclient.message_type = a;
00881 ev.xclient.format = 32;
00882 ev.xclient.data.l[0] = x;
00883 ev.xclient.data.l[1] = qt_x_time;
00884 mask = 0L;
00885 if (w == qt_xrootwin())
00886 mask = SubstructureRedirectMask;
00887 XSendEvent(qt_xdisplay(), w, False, mask, &ev);
00888 }
00889
00890
00891
00892
00893 bool Client::isCloseable() const
00894 {
00895 return motif_may_close && ( !isSpecialWindow() || isOverride());
00896 }
00897
00902 void Client::closeWindow()
00903 {
00904 if( !isCloseable())
00905 return;
00906
00907
00908
00909
00910 group()->updateUserTime();
00911 if ( Pdeletewindow )
00912 {
00913 Notify::raise( Notify::Close );
00914 sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
00915 pingWindow();
00916 }
00917 else
00918 {
00919
00920
00921 killWindow();
00922 }
00923 }
00924
00925
00929 void Client::killWindow()
00930 {
00931 kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl;
00932
00933
00934 Notify::raise( Notify::Close );
00935
00936 if( isDialog())
00937 Notify::raise( Notify::TransDelete );
00938 if( isNormalWindow())
00939 Notify::raise( Notify::Delete );
00940 killProcess( false );
00941
00942 XKillClient(qt_xdisplay(), window() );
00943 destroyClient();
00944 }
00945
00946
00947
00948
00949 void Client::pingWindow()
00950 {
00951 if( !Pping )
00952 return;
00953 if( ping_timer != NULL )
00954 return;
00955 ping_timer = new QTimer( this );
00956 connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout()));
00957 ping_timer->start( 5000, true );
00958 ping_timestamp = qt_x_time;
00959 workspace()->sendPingToWindow( window(), ping_timestamp );
00960 }
00961
00962 void Client::gotPing( Time timestamp )
00963 {
00964 if( timestamp != ping_timestamp )
00965 return;
00966 delete ping_timer;
00967 ping_timer = NULL;
00968 if( process_killer != NULL )
00969 {
00970 process_killer->kill();
00971 delete process_killer;
00972 process_killer = NULL;
00973 }
00974 }
00975
00976 void Client::pingTimeout()
00977 {
00978 kdDebug( 1212 ) << "Ping timeout:" << caption() << endl;
00979 delete ping_timer;
00980 ping_timer = NULL;
00981 killProcess( true, ping_timestamp );
00982 }
00983
00984 void Client::killProcess( bool ask, Time timestamp )
00985 {
00986 if( process_killer != NULL )
00987 return;
00988 Q_ASSERT( !ask || timestamp != CurrentTime );
00989 QCString machine = wmClientMachine();
00990 pid_t pid = info->pid();
00991 if( pid <= 0 || machine.isEmpty())
00992 return;
00993 kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl;
00994 if( !ask )
00995 {
00996 if( machine != "localhost" )
00997 {
00998 KProcess proc;
00999 proc << "xon" << machine << "kill" << pid;
01000 proc.start( KProcess::DontCare );
01001 }
01002 else
01003 ::kill( pid, SIGTERM );
01004 }
01005 else
01006 {
01007 process_killer = new KProcess( this );
01008 *process_killer << KStandardDirs::findExe( "kwin_killer_helper" )
01009 << "--pid" << QCString().setNum( pid ) << "--hostname" << machine
01010 << "--windowname" << caption().utf8()
01011 << "--applicationname" << resourceClass()
01012 << "--wid" << QCString().setNum( window())
01013 << "--timestamp" << QCString().setNum( timestamp );
01014 connect( process_killer, SIGNAL( processExited( KProcess* )),
01015 SLOT( processKillerExited()));
01016 if( !process_killer->start( KProcess::NotifyOnExit ))
01017 {
01018 delete process_killer;
01019 process_killer = NULL;
01020 return;
01021 }
01022 }
01023 }
01024
01025 void Client::processKillerExited()
01026 {
01027 kdDebug( 1212 ) << "Killer exited" << endl;
01028 delete process_killer;
01029 process_killer = NULL;
01030 }
01031
01032 void Client::setSkipTaskbar( bool b, bool from_outside )
01033 {
01034 if( from_outside )
01035 original_skip_taskbar = b;
01036 if ( b == skipTaskbar() )
01037 return;
01038 skip_taskbar = b;
01039 info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar );
01040 }
01041
01042 void Client::setSkipPager( bool b )
01043 {
01044 if ( b == skipPager() )
01045 return;
01046 skip_pager = b;
01047 info->setState( b?NET::SkipPager:0, NET::SkipPager );
01048 }
01049
01050 void Client::setModal( bool m )
01051 {
01052 if( modal == m )
01053 return;
01054 modal = m;
01055 if( !modal )
01056 return;
01057
01058
01059 }
01060
01061 void Client::toggleOnAllDesktops()
01062 {
01063 setOnAllDesktops( !isOnAllDesktops());
01064 }
01065
01066 void Client::setDesktop( int desktop )
01067 {
01068 if( desktop != NET::OnAllDesktops )
01069 desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop ));
01070 if( desk == desktop )
01071 return;
01072 int was_desk = desk;
01073 desk = desktop;
01074 info->setDesktop( desktop );
01075 if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
01076 {
01077 if ( isShown( true ))
01078 Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
01079 workspace()->updateOnAllDesktopsOfTransients( this );
01080 }
01081 if( decoration != NULL )
01082 decoration->desktopChange();
01083 virtualDesktopChange();
01084 }
01085
01086 void Client::setOnAllDesktops( bool b )
01087 {
01088 if(( b && isOnAllDesktops())
01089 || ( !b && !isOnAllDesktops()))
01090 return;
01091 if( b )
01092 setDesktop( NET::OnAllDesktops );
01093 else
01094 setDesktop( workspace()->currentDesktop());
01095 }
01096
01097 bool Client::isOnCurrentDesktop() const
01098 {
01099 return isOnDesktop( workspace()->currentDesktop());
01100 }
01101
01106 void Client::takeFocus( bool force, allowed_t )
01107 {
01108 if ( !force && ( isTopMenu() || isDock() || isSplash()) )
01109 return;
01110
01111 #ifndef NDEBUG
01112 static Time previous_focus_timestamp;
01113 static Client* previous_client;
01114 if( previous_focus_timestamp == qt_x_time && previous_client != this )
01115 {
01116 kdWarning( 1212 ) << "Repeated use of the same X timestamp for focus" << endl;
01117 kdDebug( 1212 ) << kdBacktrace() << endl;
01118 }
01119 previous_focus_timestamp = qt_x_time;
01120 previous_client = this;
01121 #endif
01122 if ( input )
01123 {
01124 XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, qt_x_time );
01125 }
01126 if ( Ptakefocus )
01127 sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
01128 }
01129
01137 bool Client::providesContextHelp() const
01138 {
01139 return Pcontexthelp;
01140 }
01141
01142
01149 void Client::showContextHelp()
01150 {
01151 if ( Pcontexthelp )
01152 {
01153 sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
01154 QWhatsThis::enterWhatsThisMode();
01155 }
01156 }
01157
01158
01163 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
01164
01165 void Client::fetchName()
01166 {
01167 QString s;
01168
01169 if ( info->name() && info->name()[ 0 ] != '\0' )
01170 s = QString::fromUtf8( info->name() );
01171 else
01172 s = KWin::readNameProperty( window(), XA_WM_NAME );
01173 if ( s != cap_normal )
01174 {
01175 bool reset_name = cap_normal.isEmpty();
01176 for( unsigned int i = 0;
01177 i < s.length();
01178 ++i )
01179 if( !s[ i ].isPrint())
01180 s[ i ] = ' ';
01181 cap_normal = s;
01182 bool was_suffix = ( !cap_suffix.isEmpty());
01183 cap_suffix = QString::null;
01184 if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this )))
01185 {
01186 int i = 2;
01187 do
01188 {
01189 cap_suffix = " <" + QString::number(i) + ">";
01190 i++;
01191 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
01192 info->setVisibleName( caption().utf8() );
01193 reset_name = false;
01194 }
01195 if(( was_suffix && cap_suffix.isEmpty()
01196 || reset_name ))
01197 {
01198 info->setVisibleName( "" );
01199 info->setVisibleIconName( "" );
01200 }
01201 else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty())
01202 info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() );
01203
01204 if( isManaged() && decoration != NULL )
01205 decoration->captionChange();
01206 }
01207 }
01208
01209 void Client::fetchIconicName()
01210 {
01211 QString s;
01212
01213 if ( info->iconName() && info->iconName()[ 0 ] != '\0' )
01214 s = QString::fromUtf8( info->iconName() );
01215 else
01216 s = KWin::readNameProperty( window(), XA_WM_ICON_NAME );
01217 if ( s != cap_iconic )
01218 {
01219 cap_iconic = s;
01220 if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty())
01221 info->setVisibleIconName( ( s + cap_suffix ).utf8() );
01222 }
01223 }
01224
01227 QString Client::caption() const
01228 {
01229 return cap_normal + cap_suffix;
01230 }
01231
01232 void Client::getWMHints()
01233 {
01234 XWMHints *hints = XGetWMHints(qt_xdisplay(), window() );
01235 input = true;
01236 window_group = None;
01237 urgency = false;
01238 if ( hints )
01239 {
01240 if( hints->flags & InputHint )
01241 input = hints->input;
01242 if( hints->flags & WindowGroupHint )
01243 window_group = hints->window_group;
01244 urgency = ( hints->flags & UrgencyHint ) ? true : false;
01245 XFree( (char*)hints );
01246 }
01247 checkGroup();
01248 updateUrgency();
01249 updateAllowedActions();
01250 }
01251
01252 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon )
01253 {
01254
01255 if( icon != NULL )
01256 *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints );
01257 if( miniicon != NULL )
01258 if( icon == NULL || !icon->isNull())
01259 *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints );
01260 else
01261 *miniicon = QPixmap();
01262 }
01263
01264 void Client::getIcons()
01265 {
01266
01267 readIcons( window(), &icon_pix, &miniicon_pix );
01268 if( icon_pix.isNull())
01269 {
01270 icon_pix = group()->icon();
01271 miniicon_pix = group()->miniIcon();
01272 }
01273 if( icon_pix.isNull() && isTransient())
01274 {
01275 ClientList mainclients = mainClients();
01276 for( ClientList::ConstIterator it = mainclients.begin();
01277 it != mainclients.end() && icon_pix.isNull();
01278 ++it )
01279 {
01280 icon_pix = (*it)->icon();
01281 miniicon_pix = (*it)->miniIcon();
01282 }
01283 }
01284 if( icon_pix.isNull())
01285 {
01286 icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp );
01287 miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp );
01288 }
01289 if( isManaged() && decoration != NULL )
01290 decoration->iconChange();
01291 }
01292
01293 void Client::getWindowProtocols()
01294 {
01295 Atom *p;
01296 int i,n;
01297
01298 Pdeletewindow = 0;
01299 Ptakefocus = 0;
01300 Pcontexthelp = 0;
01301 Pping = 0;
01302
01303 if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n))
01304 {
01305 for (i = 0; i < n; i++)
01306 if (p[i] == atoms->wm_delete_window)
01307 Pdeletewindow = 1;
01308 else if (p[i] == atoms->wm_take_focus)
01309 Ptakefocus = 1;
01310 else if (p[i] == atoms->net_wm_context_help)
01311 Pcontexthelp = 1;
01312 else if (p[i] == atoms->net_wm_ping)
01313 Pping = 1;
01314 if (n>0)
01315 XFree(p);
01316 }
01317 }
01318
01319 static int nullErrorHandler(Display *, XErrorEvent *)
01320 {
01321 return 0;
01322 }
01323
01327 QCString Client::staticWindowRole(WId w)
01328 {
01329 return getStringProperty(w, qt_window_role);
01330 }
01331
01335 QCString Client::staticSessionId(WId w)
01336 {
01337 return getStringProperty(w, qt_sm_client_id);
01338 }
01339
01343 QCString Client::staticWmCommand(WId w)
01344 {
01345 return getStringProperty(w, XA_WM_COMMAND, ' ');
01346 }
01347
01352 QCString Client::staticWmClientMachine(WId w)
01353 {
01354 QCString result = getStringProperty(w, XA_WM_CLIENT_MACHINE);
01355 if (result.isEmpty())
01356 {
01357 result = "localhost";
01358 }
01359 else
01360 {
01361
01362 char hostnamebuf[80];
01363 if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0)
01364 {
01365 hostnamebuf[sizeof(hostnamebuf)-1] = 0;
01366 if (result == hostnamebuf)
01367 result = "localhost";
01368 char *dot = strchr(hostnamebuf, '.');
01369 if (dot && !(*dot = 0) && result == hostnamebuf)
01370 result = "localhost";
01371 }
01372 }
01373 return result;
01374 }
01375
01379 Window Client::staticWmClientLeader(WId w)
01380 {
01381 Atom type;
01382 int format, status;
01383 unsigned long nitems = 0;
01384 unsigned long extra = 0;
01385 unsigned char *data = 0;
01386 Window result = w;
01387 XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler);
01388 status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000,
01389 FALSE, XA_WINDOW, &type, &format,
01390 &nitems, &extra, &data );
01391 XSetErrorHandler(oldHandler);
01392 if (status == Success )
01393 {
01394 if (data && nitems > 0)
01395 result = *((Window*) data);
01396 XFree(data);
01397 }
01398 return result;
01399 }
01400
01401
01402 void Client::getWmClientLeader()
01403 {
01404 wmClientLeaderWin = staticWmClientLeader(window());
01405 }
01406
01411 QCString Client::sessionId()
01412 {
01413 QCString result = staticSessionId(window());
01414 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01415 result = staticSessionId(wmClientLeaderWin);
01416 return result;
01417 }
01418
01423 QCString Client::wmCommand()
01424 {
01425 QCString result = staticWmCommand(window());
01426 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01427 result = staticWmCommand(wmClientLeaderWin);
01428 return result;
01429 }
01430
01435 QCString Client::wmClientMachine() const
01436 {
01437 QCString result = staticWmClientMachine(window());
01438 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01439 result = staticWmClientMachine(wmClientLeaderWin);
01440 return result;
01441 }
01442
01447 Window Client::wmClientLeader() const
01448 {
01449 if (wmClientLeaderWin)
01450 return wmClientLeaderWin;
01451 return window();
01452 }
01453
01454 bool Client::wantsTabFocus() const
01455 {
01456 return ( isNormalWindow() || isDialog() || isOverride())
01457 && ( input || Ptakefocus ) && !skip_taskbar;
01458 }
01459
01460
01461 bool Client::wantsInput() const
01462 {
01463 return input;
01464 }
01465
01470 bool Client::isMovable() const
01471 {
01472 return motif_may_move && !isFullScreen() &&
01473 ( !isSpecialWindow() || isOverride() || isSplash() || isToolbar()) &&
01474 ( maximizeMode() != MaximizeFull || options->moveResizeMaximizedWindows() );
01475 }
01476
01477 bool Client::isDesktop() const
01478 {
01479 return windowType() == NET::Desktop;
01480 }
01481
01482 bool Client::isDock() const
01483 {
01484 return windowType() == NET::Dock;
01485 }
01486
01487 bool Client::isTopMenu() const
01488 {
01489 return windowType() == NET::TopMenu;
01490 }
01491
01492
01493 bool Client::isMenu() const
01494 {
01495 return windowType() == NET::Menu && !isTopMenu();
01496 }
01497
01498 bool Client::isToolbar() const
01499 {
01500 return windowType() == NET::Toolbar;
01501 }
01502
01503 bool Client::isOverride() const
01504 {
01505 return windowType() == NET::Override;
01506 }
01507
01508 bool Client::isSplash() const
01509 {
01510 return windowType() == NET::Splash;
01511 }
01512
01513 bool Client::isUtility() const
01514 {
01515 return windowType() == NET::Utility;
01516 }
01517
01518 bool Client::isDialog() const
01519 {
01520 return windowType() == NET::Dialog;
01521 }
01522
01523 bool Client::isNormalWindow() const
01524 {
01525 return windowType() == NET::Normal;
01526 }
01527
01528 bool Client::isSpecialWindow() const
01529 {
01530 return isDesktop() || isDock() || isSplash() || isTopMenu()
01531 || ( isOverride() && !isFullScreen())
01532 || isToolbar();
01533 }
01534
01535 NET::WindowType Client::windowType( bool strict, int supported_types ) const
01536 {
01537 NET::WindowType wt = info->windowType( supported_types );
01538
01539 if( !strict )
01540 {
01541 if( wt == NET::Menu )
01542 {
01543
01544
01545
01546 if( x() == 0 && y() < 0 && y() > -10 && height() < 100
01547 && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 )
01548 wt = NET::TopMenu;
01549 }
01550 const char* const oo_prefix = "openoffice.org";
01551
01552 if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog )
01553 wt = NET::Normal;
01554 if( wt == NET::Unknown )
01555 wt = isTransient() ? NET::Dialog : NET::Normal;
01556 }
01557 return wt;
01558 }
01559
01564 void Client::setCursor( Position m )
01565 {
01566 if ( !isResizable() || isShade() || noBorder())
01567 {
01568 setCursor( arrowCursor );
01569 return;
01570 }
01571 switch ( m )
01572 {
01573 case PositionTopLeft:
01574 case PositionBottomRight:
01575 setCursor( sizeFDiagCursor );
01576 break;
01577 case PositionBottomLeft:
01578 case PositionTopRight:
01579 setCursor( sizeBDiagCursor );
01580 break;
01581 case PositionTop:
01582 case PositionBottom:
01583 setCursor( sizeVerCursor );
01584 break;
01585 case PositionLeft:
01586 case PositionRight:
01587 setCursor( sizeHorCursor );
01588 break;
01589 default:
01590 setCursor( arrowCursor );
01591 break;
01592 }
01593 }
01594
01595
01596 void Client::setCursor( const QCursor& c )
01597 {
01598 if( c.handle() == cursor.handle())
01599 return;
01600 cursor = c;
01601 if( decoration != NULL )
01602 decoration->widget()->setCursor( cursor );
01603 XDefineCursor( qt_xdisplay(), frameId(), cursor.handle());
01604 }
01605
01606 Client::Position Client::mousePosition( const QPoint& p ) const
01607 {
01608 if( decoration != NULL )
01609 return decoration->mousePosition( p );
01610 return PositionCenter;
01611 }
01612
01613 void Client::updateAllowedActions( bool force )
01614 {
01615 if( !isManaged() && !force )
01616 return;
01617 unsigned long old_allowed_actions = allowed_actions;
01618 allowed_actions = 0;
01619 if( isMovable())
01620 allowed_actions |= NET::ActionMove;
01621 if( isResizable())
01622 allowed_actions |= NET::ActionResize;
01623 if( isMinimizable())
01624 allowed_actions |= NET::ActionMinimize;
01625 if( isShadeable())
01626 allowed_actions |= NET::ActionShade;
01627
01628 if( isMaximizable())
01629 allowed_actions |= NET::ActionMax;
01630 if( userCanSetFullScreen())
01631 allowed_actions |= NET::ActionFullScreen;
01632 allowed_actions |= NET::ActionChangeDesktop;
01633 if( isCloseable())
01634 allowed_actions |= NET::ActionClose;
01635 if( old_allowed_actions == allowed_actions )
01636 return;
01637
01638 info->setAllowedActions( allowed_actions );
01639
01640 }
01641
01642 void Client::autoRaise()
01643 {
01644 workspace()->raiseClient( this );
01645 delete autoRaiseTimer;
01646 autoRaiseTimer = 0;
01647 }
01648
01649 void Client::cancelAutoRaise()
01650 {
01651 delete autoRaiseTimer;
01652 autoRaiseTimer = 0;
01653 }
01654
01655 #ifdef NDEBUG
01656 kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; }
01657 kndbgstream& operator<<( kndbgstream& stream, const ClientList& ) { return stream; }
01658 kndbgstream& operator<<( kndbgstream& stream, const ConstClientList& ) { return stream; }
01659 #else
01660 kdbgstream& operator<<( kdbgstream& stream, const Client* cl )
01661 {
01662 if( cl == NULL )
01663 return stream << "\'NULL_CLIENT\'";
01664 return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'";
01665 }
01666 kdbgstream& operator<<( kdbgstream& stream, const ClientList& list )
01667 {
01668 stream << "LIST:(";
01669 bool first = true;
01670 for( ClientList::ConstIterator it = list.begin();
01671 it != list.end();
01672 ++it )
01673 {
01674 if( !first )
01675 stream << ":";
01676 first = false;
01677 stream << *it;
01678 }
01679 stream << ")";
01680 return stream;
01681 }
01682 kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list )
01683 {
01684 stream << "LIST:(";
01685 bool first = true;
01686 for( ConstClientList::ConstIterator it = list.begin();
01687 it != list.end();
01688 ++it )
01689 {
01690 if( !first )
01691 stream << ":";
01692 first = false;
01693 stream << *it;
01694 }
01695 stream << ")";
01696 return stream;
01697 }
01698 #endif
01699
01700 QPixmap * kwin_get_menu_pix_hack()
01701 {
01702 static QPixmap p;
01703 if ( p.isNull() )
01704 p = SmallIcon( "bx2" );
01705 return &p;
01706 }
01707
01708 }
01709
01710 #include "client.moc"