workspace.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 //#define QT_CLEAN_NAMESPACE
00013 
00014 #include "workspace.h"
00015 
00016 #include <kapplication.h>
00017 #include <kstartupinfo.h>
00018 #include <fixx11h.h>
00019 #include <kconfig.h>
00020 #include <kglobal.h>
00021 #include <qpopupmenu.h>
00022 #include <klocale.h>
00023 #include <qregexp.h>
00024 #include <qpainter.h>
00025 #include <qbitmap.h>
00026 #include <qclipboard.h>
00027 #include <kmenubar.h>
00028 #include <kprocess.h>
00029 #include <kglobalaccel.h>
00030 #include <dcopclient.h>
00031 #include <kipc.h>
00032 
00033 #include "plugins.h"
00034 #include "client.h"
00035 #include "popupinfo.h"
00036 #include "tabbox.h"
00037 #include "atoms.h"
00038 #include "placement.h"
00039 #include "notifications.h"
00040 #include "group.h"
00041 #include "rules.h"
00042 
00043 #include <X11/extensions/shape.h>
00044 #include <X11/keysym.h>
00045 #include <X11/keysymdef.h>
00046 #include <X11/cursorfont.h>
00047 
00048 extern Time qt_x_time;
00049 
00050 namespace KWinInternal
00051 {
00052 
00053 extern int screen_number;
00054 
00055 Workspace *Workspace::_self = 0;
00056 
00057 KProcess* kompmgr = 0;
00058 KSelectionOwner* kompmgr_selection;
00059 
00060 bool allowKompmgrRestart = TRUE;
00061 
00062 // Rikkus: This class is too complex. It needs splitting further.
00063 // It's a nightmare to understand, especially with so few comments :(
00064 
00065 // Matthias: Feel free to ask me questions about it. Feel free to add
00066 // comments. I disagree that further splittings makes it easier. 2500
00067 // lines are not too much. It's the task that is complex, not the
00068 // code.
00069 Workspace::Workspace( bool restore )
00070   : DCOPObject        ("KWinInterface"),
00071     QObject           (0, "workspace"),
00072     current_desktop   (0),
00073     number_of_desktops(0),
00074     active_screen     (0),
00075     active_popup( NULL ),
00076     active_popup_client( NULL ),
00077     desktop_widget    (0),
00078     temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ),
00079     rules_updates_disabled( false ),
00080     active_client     (0),
00081     last_active_client     (0),
00082     most_recently_raised (0),
00083     movingClient(0),
00084     pending_take_activity ( NULL ),
00085     delayfocus_client (0),
00086     showing_desktop( false ),
00087     block_showing_desktop( 0 ),
00088     was_user_interaction (false),
00089     session_saving    (false),
00090     control_grab      (false),
00091     tab_grab          (false),
00092     mouse_emulation   (false),
00093     block_focus       (0),
00094     tab_box           (0),
00095     popupinfo         (0),
00096     popup             (0),
00097     advanced_popup    (0),
00098     desk_popup        (0),
00099     desk_popup_index  (0),
00100     keys              (0),
00101     client_keys       ( NULL ),
00102     client_keys_dialog ( NULL ),
00103     client_keys_client ( NULL ),
00104     disable_shortcuts_keys ( NULL ),
00105     global_shortcuts_disabled( false ),
00106     global_shortcuts_disabled_for_client( false ),
00107     root              (0),
00108     workspaceInit     (true),
00109     startup(0), electric_have_borders(false),
00110     electric_current_border(0),
00111     electric_top_border(None),
00112     electric_bottom_border(None),
00113     electric_left_border(None),
00114     electric_right_border(None),
00115     layoutOrientation(Qt::Vertical),
00116     layoutX(-1),
00117     layoutY(2),
00118     workarea(NULL),
00119     screenarea(NULL),
00120     managing_topmenus( false ),
00121     topmenu_selection( NULL ),
00122     topmenu_watcher( NULL ),
00123     topmenu_height( 0 ),
00124     topmenu_space( NULL ),
00125     set_active_client_recursion( 0 ),
00126     block_stacking_updates( 0 ),
00127     forced_global_mouse_grab( false )
00128     {
00129     _self = this;
00130     mgr = new PluginMgr;
00131     root = qt_xrootwin();
00132     default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() );
00133     installed_colormap = default_colormap;
00134     session.setAutoDelete( TRUE );
00135 
00136     connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )),
00137         this, SLOT( gotTemporaryRulesMessage( const QString& )));
00138     connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules()));
00139 
00140     updateXTime(); // needed for proper initialization of user_time in Client ctor
00141 
00142     delayFocusTimer = 0; 
00143     
00144     electric_time_first = qt_x_time;
00145     electric_time_last = qt_x_time;
00146 
00147     if ( restore )
00148       loadSessionInfo();
00149 
00150     loadWindowRules();
00151 
00152     (void) QApplication::desktop(); // trigger creation of desktop widget
00153 
00154     desktop_widget =
00155       new QWidget(
00156         0,
00157         "desktop_widget",
00158         Qt::WType_Desktop | Qt::WPaintUnclipped
00159     );
00160 
00161     kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later
00162     // call this before XSelectInput() on the root window
00163     startup = new KStartupInfo(
00164         KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this );
00165 
00166     // select windowmanager privileges
00167     XSelectInput(qt_xdisplay(), root,
00168                  KeyPressMask |
00169                  PropertyChangeMask |
00170                  ColormapChangeMask |
00171                  SubstructureRedirectMask |
00172                  SubstructureNotifyMask |
00173                  FocusChangeMask // for NotifyDetailNone
00174                  );
00175 
00176     Shape::init();
00177 
00178     // compatibility
00179     long data = 1;
00180 
00181     XChangeProperty(
00182       qt_xdisplay(),
00183       qt_xrootwin(),
00184       atoms->kwin_running,
00185       atoms->kwin_running,
00186       32,
00187       PropModeAppend,
00188       (unsigned char*) &data,
00189       1
00190     );
00191 
00192     client_keys = new KGlobalAccel( this );
00193     initShortcuts();
00194     tab_box = new TabBox( this );
00195     popupinfo = new PopupInfo( this );
00196 
00197     init();
00198 
00199 #if (QT_VERSION-0 >= 0x030200) // XRANDR support
00200     connect( kapp->desktop(), SIGNAL( resized( int )), SLOT( desktopResized()));
00201 #endif
00202 
00203     // start kompmgr - i wanted to put this into main.cpp, but that would prevent dcop support, as long as Application was no dcop_object
00204     if (options->useTranslucency)
00205         {
00206         kompmgr = new KProcess;
00207         connect(kompmgr, SIGNAL(receivedStderr(KProcess*, char*, int)), SLOT(handleKompmgrOutput(KProcess*, char*, int)));
00208         *kompmgr << "kompmgr";
00209         startKompmgr();
00210         }
00211     }
00212 
00213 
00214 void Workspace::init()
00215     {
00216     checkElectricBorders();
00217 
00218 // not used yet
00219 //     topDock = 0L;
00220 //     maximizedWindowCounter = 0;
00221     
00222     supportWindow = new QWidget;
00223     XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp
00224 
00225     XSetWindowAttributes attr;
00226     attr.override_redirect = 1;
00227     null_focus_window = XCreateWindow( qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent,
00228         InputOnly, CopyFromParent, CWOverrideRedirect, &attr );
00229     XMapWindow(qt_xdisplay(), null_focus_window);
00230 
00231     unsigned long protocols[ 5 ] =
00232         {
00233         NET::Supported |
00234         NET::SupportingWMCheck |
00235         NET::ClientList |
00236         NET::ClientListStacking |
00237         NET::DesktopGeometry |
00238         NET::NumberOfDesktops |
00239         NET::CurrentDesktop |
00240         NET::ActiveWindow |
00241         NET::WorkArea |
00242         NET::CloseWindow |
00243         NET::DesktopNames |
00244         NET::KDESystemTrayWindows |
00245         NET::WMName |
00246         NET::WMVisibleName |
00247         NET::WMDesktop |
00248         NET::WMWindowType |
00249         NET::WMState |
00250         NET::WMStrut |
00251         NET::WMIconGeometry |
00252         NET::WMIcon |
00253         NET::WMPid |
00254         NET::WMMoveResize |
00255         NET::WMKDESystemTrayWinFor |
00256         NET::WMFrameExtents |
00257         NET::WMPing
00258         ,
00259         NET::NormalMask |
00260         NET::DesktopMask |
00261         NET::DockMask |
00262         NET::ToolbarMask |
00263         NET::MenuMask |
00264         NET::DialogMask |
00265         NET::OverrideMask |
00266         NET::TopMenuMask |
00267         NET::UtilityMask |
00268         NET::SplashMask |
00269         0
00270         ,
00271         NET::Modal |
00272 //        NET::Sticky |  // large desktops not supported (and probably never will be)
00273         NET::MaxVert |
00274         NET::MaxHoriz |
00275         NET::Shaded |
00276         NET::SkipTaskbar |
00277         NET::KeepAbove |
00278 //        NET::StaysOnTop |  the same like KeepAbove
00279         NET::SkipPager |
00280         NET::Hidden |
00281         NET::FullScreen |
00282         NET::KeepBelow |
00283         NET::DemandsAttention |
00284         0
00285         ,
00286         NET::WM2UserTime |
00287         NET::WM2StartupId |
00288         NET::WM2AllowedActions |
00289         NET::WM2RestackWindow |
00290         NET::WM2MoveResizeWindow |
00291         NET::WM2ExtendedStrut |
00292         NET::WM2KDETemporaryRules |
00293         NET::WM2ShowingDesktop |
00294         NET::WM2FullPlacement |
00295         0
00296         ,
00297         NET::ActionMove |
00298         NET::ActionResize |
00299         NET::ActionMinimize |
00300         NET::ActionShade |
00301 //        NET::ActionStick | // Sticky state is not supported
00302         NET::ActionMaxVert |
00303         NET::ActionMaxHoriz |
00304         NET::ActionFullScreen |
00305         NET::ActionChangeDesktop |
00306         NET::ActionClose |
00307         0
00308         ,
00309         };
00310 
00311     rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin",
00312         protocols, 5, qt_xscreen() );
00313 
00314     loadDesktopSettings();
00315     // extra NETRootInfo instance in Client mode is needed to get the values of the properties
00316     NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop );
00317     int initial_desktop;
00318     if( !kapp->isSessionRestored())
00319         initial_desktop = client_info.currentDesktop();
00320     else
00321         {
00322         KConfigGroupSaver saver( kapp->sessionConfig(), "Session" );
00323         initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 );
00324         }
00325     if( !setCurrentDesktop( initial_desktop ))
00326         setCurrentDesktop( 1 );
00327 
00328     // now we know how many desktops we'll, thus, we initialise the positioning object
00329     initPositioning = new Placement(this);
00330 
00331     connect(&reconfigureTimer, SIGNAL(timeout()), this,
00332             SLOT(slotReconfigure()));
00333     connect( &updateToolWindowsTimer, SIGNAL( timeout()), this, SLOT( slotUpdateToolWindows()));
00334 
00335     connect(kapp, SIGNAL(appearanceChanged()), this,
00336             SLOT(slotReconfigure()));
00337     connect(kapp, SIGNAL(settingsChanged(int)), this,
00338             SLOT(slotSettingsChanged(int)));
00339     connect(kapp, SIGNAL( kipcMessage( int, int )), this, SLOT( kipcMessage( int, int )));
00340 
00341     active_client = NULL;
00342     rootInfo->setActiveWindow( None );
00343     focusToNull();
00344     if( !kapp->isSessionRestored())
00345         ++block_focus; // because it will be set below
00346 
00347     char nm[ 100 ];
00348     sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay()));
00349     Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False );
00350     topmenu_selection = new KSelectionOwner( topmenu_atom );
00351     topmenu_watcher = new KSelectionWatcher( topmenu_atom );
00352 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before
00353 
00354         { // begin updates blocker block
00355         StackingUpdatesBlocker blocker( this );
00356 
00357         if( options->topMenuEnabled() && topmenu_selection->claim( false ))
00358             setupTopMenuHandling(); // this can call updateStackingOrder()
00359         else
00360             lostTopMenuSelection();
00361 
00362         unsigned int i, nwins;
00363         Window root_return, parent_return, *wins;
00364         XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins);
00365         for (i = 0; i < nwins; i++) 
00366             {
00367             XWindowAttributes attr;
00368             XGetWindowAttributes(qt_xdisplay(), wins[i], &attr);
00369             if (attr.override_redirect )
00370                 continue;
00371             if( topmenu_space && topmenu_space->winId() == wins[ i ] )
00372                 continue;
00373             if (attr.map_state != IsUnmapped) 
00374                 {
00375                 if ( addSystemTrayWin( wins[i] ) )
00376                     continue;
00377                 Client* c = createClient( wins[i], true );
00378                 if ( c != NULL && root != qt_xrootwin() ) 
00379                     { // TODO what is this?
00380                 // TODO may use QWidget:.create
00381                     XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 );
00382                     c->move(0,0);
00383                     }
00384                 }
00385             }
00386         if ( wins )
00387             XFree((void *) wins);
00388     // propagate clients, will really happen at the end of the updates blocker block
00389         updateStackingOrder( true );
00390 
00391         updateClientArea();
00392         raiseElectricBorders();
00393 
00394     // NETWM spec says we have to set it to (0,0) if we don't support it
00395         NETPoint* viewports = new NETPoint[ number_of_desktops ];
00396         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
00397         delete[] viewports;
00398         QRect geom = QApplication::desktop()->geometry();
00399         NETSize desktop_geometry;
00400         desktop_geometry.width = geom.width();
00401         desktop_geometry.height = geom.height();
00402         rootInfo->setDesktopGeometry( -1, desktop_geometry );
00403         setShowingDesktop( false );
00404 
00405         } // end updates blocker block
00406 
00407     Client* new_active_client = NULL;
00408     if( !kapp->isSessionRestored())
00409         {
00410         --block_focus;
00411         new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow()));
00412         }
00413     if( new_active_client == NULL
00414         && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage()
00415         {
00416         if( new_active_client == NULL )
00417             new_active_client = topClientOnDesktop( currentDesktop());
00418         if( new_active_client == NULL && !desktops.isEmpty() )
00419             new_active_client = findDesktop( true, currentDesktop());
00420         }
00421     if( new_active_client != NULL )
00422         activateClient( new_active_client );
00423     // SELI TODO this won't work with unreasonable focus policies,
00424     // and maybe in rare cases also if the selected client doesn't
00425     // want focus
00426     workspaceInit = false;
00427 // TODO ungrabXServer()
00428     }
00429 
00430 Workspace::~Workspace()
00431     {
00432     if (kompmgr)
00433         delete kompmgr;
00434     blockStackingUpdates( true );
00435 // TODO    grabXServer();
00436     // use stacking_order, so that kwin --replace keeps stacking order
00437     for( ClientList::ConstIterator it = stacking_order.begin();
00438          it != stacking_order.end();
00439          ++it )
00440         {
00441     // only release the window
00442         (*it)->releaseWindow( true );
00443         // No removeClient() is called, it does more than just removing.
00444         // However, remove from some lists to e.g. prevent performTransiencyCheck()
00445         // from crashing.
00446         clients.remove( *it );
00447         desktops.remove( *it );
00448         }
00449     delete desktop_widget;
00450     delete tab_box;
00451     delete popupinfo;
00452     delete popup;
00453     if ( root == qt_xrootwin() )
00454         XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running);
00455 
00456     writeWindowRules();
00457     KGlobal::config()->sync();
00458 
00459     delete rootInfo;
00460     delete supportWindow;
00461     delete mgr;
00462     delete[] workarea;
00463     delete[] screenarea;
00464     delete startup;
00465     delete initPositioning;
00466     delete topmenu_watcher;
00467     delete topmenu_selection;
00468     delete topmenu_space;
00469     delete client_keys_dialog;
00470     while( !rules.isEmpty())
00471         {
00472         delete rules.front();
00473         rules.pop_front();
00474         }
00475     XDestroyWindow( qt_xdisplay(), null_focus_window );
00476 // TODO    ungrabXServer();
00477     _self = 0;
00478     }
00479 
00480 Client* Workspace::createClient( Window w, bool is_mapped )
00481     {
00482     StackingUpdatesBlocker blocker( this );
00483     Client* c = new Client( this );
00484     if( !c->manage( w, is_mapped ))
00485         {
00486         Client::deleteClient( c, Allowed );
00487         return NULL;
00488         }
00489     addClient( c, Allowed );
00490     return c;
00491     }
00492 
00493 void Workspace::addClient( Client* c, allowed_t )
00494     {
00495     // waited with trans settings until window figured out if active or not ;)
00496 //     qWarning("%s", (const char*)(c->resourceClass()));
00497     c->setBMP(c->resourceName() == "beep-media-player" || c->decorationId() == None);
00498     // first check if the window has it's own opinion of it's translucency ;)
00499     c->getWindowOpacity();
00500     if (c->isDock())
00501         {
00502 //         if (c->x() == 0 && c->y() == 0 && c->width() > c->height()) topDock = c;
00503         if (!c->hasCustomOpacity()) // this xould be done slightly more efficient, but we want to support the topDock in future
00504             {
00505             c->setShadowSize(options->dockShadowSize);
00506             c->setOpacity(options->translucentDocks, options->dockOpacity);
00507             }
00508         }
00509 //------------------------------------------------        
00510     Group* grp = findGroup( c->window());
00511     if( grp != NULL )
00512         grp->gotLeader( c );
00513 
00514     if ( c->isDesktop() )
00515         {
00516         desktops.append( c );
00517         if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
00518             requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active
00519         }
00520     else
00521         {
00522         updateFocusChains( c, FocusChainUpdate ); // add to focus chain if not already there
00523         clients.append( c );
00524         }
00525     if( !unconstrained_stacking_order.contains( c ))
00526         unconstrained_stacking_order.append( c );
00527     if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires
00528         stacking_order.append( c );    // c to be in stacking_order
00529     if( c->isTopMenu())
00530         addTopMenu( c );
00531     updateClientArea(); // this cannot be in manage(), because the client got added only now
00532     updateClientLayer( c );
00533     if( c->isDesktop())
00534         {
00535         raiseClient( c );
00536     // if there's no active client, make this desktop the active one
00537         if( activeClient() == NULL && should_get_focus.count() == 0 )
00538             activateClient( findDesktop( true, currentDesktop()));
00539         }
00540     c->checkActiveModal();
00541     checkTransients( c->window()); // SELI does this really belong here?
00542     updateStackingOrder( true ); // propagate new client
00543     if( c->isUtility() || c->isMenu() || c->isToolbar())
00544         updateToolWindows( true );
00545     checkNonExistentClients();
00546     }
00547 
00548 /*
00549   Destroys the client \a c
00550  */
00551 void Workspace::removeClient( Client* c, allowed_t )
00552     {
00553     if (c == active_popup_client)
00554         closeActivePopup();
00555 
00556     if( client_keys_client == c )
00557         setupWindowShortcutDone( false );
00558     if( !c->shortcut().isNull())
00559         c->setShortcut( QString::null ); // remove from client_keys
00560 
00561     if( c->isDialog())
00562         Notify::raise( Notify::TransDelete );
00563     if( c->isNormalWindow())
00564         Notify::raise( Notify::Delete );
00565 
00566     Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
00567     clients.remove( c );
00568     desktops.remove( c );
00569     unconstrained_stacking_order.remove( c );
00570     stacking_order.remove( c );
00571     for( int i = 1;
00572          i <= numberOfDesktops();
00573          ++i )
00574         focus_chain[ i ].remove( c );
00575     global_focus_chain.remove( c );
00576     attention_chain.remove( c );
00577     showing_desktop_clients.remove( c );
00578     if( c->isTopMenu())
00579         removeTopMenu( c );
00580     Group* group = findGroup( c->window());
00581     if( group != NULL )
00582         group->lostLeader();
00583 
00584     if ( c == most_recently_raised )
00585         most_recently_raised = 0;
00586     should_get_focus.remove( c );
00587     Q_ASSERT( c != active_client );
00588     if ( c == last_active_client )
00589         last_active_client = 0;
00590     if( c == pending_take_activity )
00591         pending_take_activity = NULL;
00592     if( c == delayfocus_client )
00593         cancelDelayFocus();
00594 
00595     updateStackingOrder( true );
00596 
00597     if (tab_grab)
00598        tab_box->repaint();
00599 
00600     updateClientArea();
00601     }
00602 
00603 void Workspace::updateFocusChains( Client* c, FocusChainChange change )
00604     {
00605     if( !c->wantsTabFocus()) // doesn't want tab focus, remove
00606         {
00607         for( int i=1;
00608              i<= numberOfDesktops();
00609              ++i )
00610             focus_chain[i].remove(c);
00611         global_focus_chain.remove( c );
00612         return;
00613         }
00614     if(c->desktop() == NET::OnAllDesktops)
00615         { //now on all desktops, add it to focus_chains it is not already in
00616         for( int i=1; i<= numberOfDesktops(); i++)
00617             { // making first/last works only on current desktop, don't affect all desktops
00618             if( i == currentDesktop()
00619                 && ( change == FocusChainMakeFirst || change == FocusChainMakeLast ))
00620                 {
00621                 focus_chain[ i ].remove( c );
00622                 if( change == FocusChainMakeFirst )
00623                     focus_chain[ i ].append( c );
00624                 else
00625                     focus_chain[ i ].prepend( c );
00626                 }
00627             else if( !focus_chain[ i ].contains( c ))
00628                 { // add it after the active one
00629                 if( active_client != NULL && active_client != c
00630                     && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
00631                     focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c );
00632                 else
00633                     focus_chain[ i ].append( c ); // otherwise add as the first one
00634                 }
00635             }
00636         }
00637     else    //now only on desktop, remove it anywhere else
00638         {
00639         for( int i=1; i<= numberOfDesktops(); i++)
00640             {
00641             if( i == c->desktop())
00642                 {
00643                 if( change == FocusChainMakeFirst )
00644                     {
00645                     focus_chain[ i ].remove( c );
00646                     focus_chain[ i ].append( c );
00647                     }
00648                 else if( change == FocusChainMakeLast )
00649                     {
00650                     focus_chain[ i ].remove( c );
00651                     focus_chain[ i ].prepend( c );
00652                     }
00653                 else if( !focus_chain[ i ].contains( c ))
00654                     {
00655                     if( active_client != NULL && active_client != c
00656                         && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
00657                         focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c );
00658                     else
00659                         focus_chain[ i ].append( c ); // otherwise add as the first one
00660                     }
00661                 }
00662             else
00663                 focus_chain[ i ].remove( c );
00664             }
00665         }
00666     if( change == FocusChainMakeFirst )
00667         {
00668         global_focus_chain.remove( c );
00669         global_focus_chain.append( c );
00670         }
00671     else if( change == FocusChainMakeLast )
00672         {
00673         global_focus_chain.remove( c );
00674         global_focus_chain.prepend( c );
00675         }
00676     else if( !global_focus_chain.contains( c ))
00677         {
00678         if( active_client != NULL && active_client != c
00679             && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client )
00680             global_focus_chain.insert( global_focus_chain.fromLast(), c );
00681         else
00682             global_focus_chain.append( c ); // otherwise add as the first one
00683         }
00684     }
00685 
00686 void Workspace::updateCurrentTopMenu()
00687     {
00688     if( !managingTopMenus())
00689         return;
00690     // toplevel menubar handling
00691     Client* menubar = 0;
00692     bool block_desktop_menubar = false;
00693     if( active_client )
00694         {
00695         // show the new menu bar first...
00696         Client* menu_client = active_client;
00697         for(;;)
00698             {
00699             if( menu_client->isFullScreen())
00700                 block_desktop_menubar = true;
00701             for( ClientList::ConstIterator it = menu_client->transients().begin();
00702                  it != menu_client->transients().end();
00703                  ++it )
00704                 if( (*it)->isTopMenu())
00705                     {
00706                     menubar = *it;
00707                     break;
00708                     }
00709             if( menubar != NULL || !menu_client->isTransient())
00710                 break;
00711             if( menu_client->isModal() || menu_client->transientFor() == NULL )
00712                 break; // don't use mainwindow's menu if this is modal or group transient
00713             menu_client = menu_client->transientFor();
00714             }
00715         if( !menubar )
00716             { // try to find any topmenu from the application (#72113)
00717             for( ClientList::ConstIterator it = active_client->group()->members().begin();
00718                  it != active_client->group()->members().end();
00719                  ++it )
00720                 if( (*it)->isTopMenu())
00721                     {
00722                     menubar = *it;
00723                     break;
00724                     }
00725             }
00726         }
00727     if( !menubar && !block_desktop_menubar && options->desktopTopMenu())
00728         {
00729         // Find the menubar of the desktop
00730         Client* desktop = findDesktop( true, currentDesktop());
00731         if( desktop != NULL )
00732             {
00733             for( ClientList::ConstIterator it = desktop->transients().begin();
00734                  it != desktop->transients().end();
00735                  ++it )
00736                 if( (*it)->isTopMenu())
00737                     {
00738                     menubar = *it;
00739                     break;
00740                     }
00741             }
00742         // TODO to be cleaned app with window grouping
00743         // Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
00744         // thus the topmenu is not transient for it :-/.
00745         if( menubar == NULL )
00746             {
00747             for( ClientList::ConstIterator it = topmenus.begin();
00748                  it != topmenus.end();
00749                  ++it )
00750                 if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR
00751                     {                                     // set pointing to the root window
00752                     menubar = *it;                        // to recognize it here
00753                     break;                                // Also, with the xroot hack in kdesktop,
00754                     }                                     // there's no NET::Desktop window to be transient for
00755             }
00756         }
00757 
00758 //    kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl;
00759     if ( menubar )
00760         {
00761         if( active_client && !menubar->isOnDesktop( active_client->desktop()))
00762             menubar->setDesktop( active_client->desktop());
00763         menubar->hideClient( false );
00764         topmenu_space->hide();
00765         // make it appear like it's been raised manually - it's in the Dock layer anyway,
00766         // and not raising it could mess up stacking order of topmenus within one application,
00767         // and thus break raising of mainclients in raiseClient()
00768         unconstrained_stacking_order.remove( menubar );
00769         unconstrained_stacking_order.append( menubar );
00770         }
00771     else if( !block_desktop_menubar )
00772         { // no topmenu active - show the space window, so that there's not empty space
00773         topmenu_space->show();
00774         }
00775 
00776     // ... then hide the other ones. Avoids flickers.
00777     for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 
00778         {
00779         if( (*it)->isTopMenu() && (*it) != menubar )
00780             (*it)->hideClient( true );
00781         }
00782     }
00783 
00784 
00785 void Workspace::updateToolWindows( bool also_hide )
00786     {
00787     // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
00788     if( !options->hideUtilityWindowsForInactive )
00789         {
00790         for( ClientList::ConstIterator it = clients.begin();
00791              it != clients.end();
00792              ++it )
00793             (*it)->hideClient( false );
00794         return;
00795         }
00796     const Group* group = NULL;
00797     const Client* client = active_client;
00798 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
00799 // will be shown; if a group transient is group, all tools in the group will be shown
00800     while( client != NULL )
00801         {
00802         if( !client->isTransient())
00803             break;
00804         if( client->groupTransient())
00805             {
00806             group = client->group();
00807             break;
00808             }
00809         client = client->transientFor();
00810         }
00811     // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
00812     // i.e. if it's not up to date
00813 
00814     // SELI but maybe it should - what if a new client has been added that's not in stacking order yet?
00815     ClientList to_show, to_hide;
00816     for( ClientList::ConstIterator it = stacking_order.begin();
00817          it != stacking_order.end();
00818          ++it )
00819         {
00820         if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar())
00821             {
00822             bool show = true;
00823             if( !(*it)->isTransient())
00824                 {
00825                 if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible
00826                     show = true;
00827                 else if( client != NULL && (*it)->group() == client->group())
00828                     show = true;
00829                 else
00830                     show = false;
00831                 }
00832             else
00833                 {
00834                 if( group != NULL && (*it)->group() == group )
00835                     show = true;
00836                 else if( client != NULL && client->hasTransient( (*it), true ))
00837                     show = true;
00838                 else
00839                     show = false;
00840                 }
00841             if( !show && also_hide )
00842                 {
00843                 const ClientList mainclients = (*it)->mainClients();
00844                 // don't hide utility windows which are standalone(?) or
00845                 // have e.g. kicker as mainwindow
00846                 if( mainclients.isEmpty())
00847                     show = true;
00848                 for( ClientList::ConstIterator it2 = mainclients.begin();
00849                      it2 != mainclients.end();
00850                      ++it2 )
00851                     {
00852                     if( (*it2)->isSpecialWindow())
00853                         show = true;
00854                     }
00855                 if( !show )
00856                     to_hide.append( *it );
00857                 }
00858             if( show )
00859                 to_show.append( *it );
00860             }
00861         } // first show new ones, then hide
00862     for( ClientList::ConstIterator it = to_show.fromLast();
00863          it != to_show.end();
00864          --it ) // from topmost
00865         // TODO since this is in stacking order, the order of taskbar entries changes :(
00866         (*it)->hideClient( false );
00867     if( also_hide )
00868         {
00869         for( ClientList::ConstIterator it = to_hide.begin();
00870              it != to_hide.end();
00871              ++it ) // from bottommost
00872             (*it)->hideClient( true );
00873         updateToolWindowsTimer.stop();
00874         }
00875     else // setActiveClient() is after called with NULL client, quickly followed
00876         {    // by setting a new client, which would result in flickering
00877         updateToolWindowsTimer.start( 50, true );
00878         }
00879     }
00880 
00881 void Workspace::slotUpdateToolWindows()
00882     {
00883     updateToolWindows( true );
00884     }
00885 
00889 void Workspace::updateColormap()
00890     {
00891     Colormap cmap = default_colormap;
00892     if ( activeClient() && activeClient()->colormap() != None )
00893         cmap = activeClient()->colormap();
00894     if ( cmap != installed_colormap ) 
00895         {
00896         XInstallColormap(qt_xdisplay(), cmap );
00897         installed_colormap = cmap;
00898         }
00899     }
00900 
00901 void Workspace::reconfigure()
00902     {
00903     reconfigureTimer.start(200, true);
00904     }
00905 
00906 
00907 void Workspace::slotSettingsChanged(int category)
00908     {
00909     kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl;
00910     if( category == (int) KApplication::SETTINGS_SHORTCUTS )
00911         readShortcuts();
00912     }
00913 
00917 KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() );
00918 
00919 void Workspace::slotReconfigure()
00920     {
00921     kdDebug(1212) << "Workspace::slotReconfigure()" << endl;
00922     reconfigureTimer.stop();
00923 
00924     KGlobal::config()->reparseConfiguration();
00925     unsigned long changed = options->updateSettings();
00926     tab_box->reconfigure();
00927     popupinfo->reconfigure();
00928     initPositioning->reinitCascading( 0 );
00929     readShortcuts();
00930     forEachClient( CheckIgnoreFocusStealingProcedure());
00931     updateToolWindows( true );
00932 
00933     if( mgr->reset( changed ))
00934         { // decorations need to be recreated
00935 #if 0 // This actually seems to make things worse now
00936         QWidget curtain;
00937         curtain.setBackgroundMode( NoBackground );
00938         curtain.setGeometry( QApplication::desktop()->geometry() );
00939         curtain.show();
00940 #endif
00941         for( ClientList::ConstIterator it = clients.begin();
00942                 it != clients.end();
00943                 ++it )
00944             {
00945             (*it)->updateDecoration( true, true );
00946             }
00947         mgr->destroyPreviousPlugin();
00948         }
00949     else
00950         {
00951         forEachClient( CheckBorderSizesProcedure());
00952         }
00953 
00954     checkElectricBorders();
00955 
00956     if( options->topMenuEnabled() && !managingTopMenus())
00957         {
00958         if( topmenu_selection->claim( false ))
00959             setupTopMenuHandling();
00960         else
00961             lostTopMenuSelection();
00962         }
00963     else if( !options->topMenuEnabled() && managingTopMenus())
00964         {
00965         topmenu_selection->release();
00966         lostTopMenuSelection();
00967         }
00968     topmenu_height = 0; // invalidate used menu height
00969     if( managingTopMenus())
00970         {
00971         updateTopMenuGeometry();
00972         updateCurrentTopMenu();
00973         }
00974 
00975     loadWindowRules();
00976     for( ClientList::Iterator it = clients.begin();
00977          it != clients.end();
00978          ++it )
00979         {
00980         (*it)->setupWindowRules( true );
00981         (*it)->applyWindowRules();
00982         discardUsedWindowRules( *it, false );
00983         }
00984 
00985     if (options->resetKompmgr) // need restart
00986         {
00987         bool tmp = options->useTranslucency;
00988         stopKompmgr();
00989         if (tmp)
00990             QTimer::singleShot( 200, this, SLOT(startKompmgr()) ); // wait some time to ensure system's ready for restart
00991         }
00992     }
00993 
00994 void Workspace::loadDesktopSettings()
00995     {
00996     KConfig* c = KGlobal::config();
00997     QCString groupname;
00998     if (screen_number == 0)
00999         groupname = "Desktops";
01000     else
01001         groupname.sprintf("Desktops-screen-%d", screen_number);
01002     KConfigGroupSaver saver(c,groupname);
01003 
01004     int n = c->readNumEntry("Number", 4);
01005     number_of_desktops = n;
01006     delete workarea;
01007     workarea = new QRect[ n + 1 ];
01008     delete screenarea;
01009     screenarea = NULL;
01010     rootInfo->setNumberOfDesktops( number_of_desktops );
01011     desktop_focus_chain.resize( n );
01012     // make it +1, so that it can be accessed as [1..numberofdesktops]
01013     focus_chain.resize( n + 1 );
01014     for(int i = 1; i <= n; i++) 
01015         {
01016         QString s = c->readEntry(QString("Name_%1").arg(i),
01017                                 i18n("Desktop %1").arg(i));
01018         rootInfo->setDesktopName( i, s.utf8().data() );
01019         desktop_focus_chain[i-1] = i;
01020         }
01021     }
01022 
01023 void Workspace::saveDesktopSettings()
01024     {
01025     KConfig* c = KGlobal::config();
01026     QCString groupname;
01027     if (screen_number == 0)
01028         groupname = "Desktops";
01029     else
01030         groupname.sprintf("Desktops-screen-%d", screen_number);
01031     KConfigGroupSaver saver(c,groupname);
01032 
01033     c->writeEntry("Number", number_of_desktops );
01034     for(int i = 1; i <= number_of_desktops; i++) 
01035         {
01036         QString s = desktopName( i );
01037         QString defaultvalue = i18n("Desktop %1").arg(i);
01038         if ( s.isEmpty() ) 
01039             {
01040             s = defaultvalue;
01041             rootInfo->setDesktopName( i, s.utf8().data() );
01042             }
01043 
01044         if (s != defaultvalue) 
01045             {
01046             c->writeEntry( QString("Name_%1").arg(i), s );
01047             }
01048         else 
01049             {
01050             QString currentvalue = c->readEntry(QString("Name_%1").arg(i));
01051             if (currentvalue != defaultvalue)
01052                 c->writeEntry( QString("Name_%1").arg(i), "" );
01053             }
01054         }
01055     }
01056 
01057 QStringList Workspace::configModules(bool controlCenter)
01058     {
01059     QStringList args;
01060     args <<  "kde-kwindecoration.desktop";
01061     if (controlCenter)
01062         args << "kde-kwinoptions.desktop";
01063     else if (kapp->authorizeControlModule("kde-kwinoptions.desktop"))
01064         args  << "kwinactions" << "kwinfocus" <<  "kwinmoving" << "kwinadvanced" << "kwinrules" << "kwintranslucency";
01065     return args;
01066     }
01067 
01068 void Workspace::configureWM()
01069     {
01070     KApplication::kdeinitExec( "kcmshell", configModules(false) );
01071     }
01072 
01076 void Workspace::doNotManage( QString title )
01077     {
01078     doNotManageList.append( title );
01079     }
01080 
01084 bool Workspace::isNotManaged( const QString& title )
01085     {
01086     for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) 
01087         {
01088         QRegExp r( (*it) );
01089         if (r.search(title) != -1) 
01090             {
01091             doNotManageList.remove( it );
01092             return TRUE;
01093             }
01094         }
01095     return FALSE;
01096     }
01097 
01101 void Workspace::refresh() 
01102     {
01103     QWidget w;
01104     w.setGeometry( QApplication::desktop()->geometry() );
01105     w.show();
01106     w.hide();
01107     QApplication::flushX();
01108     }
01109 
01117 class ObscuringWindows
01118     {
01119     public:
01120         ~ObscuringWindows();
01121         void create( Client* c );
01122     private:
01123         QValueList<Window> obscuring_windows;
01124         static QValueList<Window>* cached;
01125         static unsigned int max_cache_size;
01126     };
01127 
01128 QValueList<Window>* ObscuringWindows::cached = 0;
01129 unsigned int ObscuringWindows::max_cache_size = 0;
01130 
01131 void ObscuringWindows::create( Client* c )
01132     {
01133     if( cached == 0 )
01134         cached = new QValueList<Window>;
01135     Window obs_win;
01136     XWindowChanges chngs;
01137     int mask = CWSibling | CWStackMode;
01138     if( cached->count() > 0 ) 
01139         {
01140         cached->remove( obs_win = cached->first());
01141         chngs.x = c->x();
01142         chngs.y = c->y();
01143         chngs.width = c->width();
01144         chngs.height = c->height();
01145         mask |= CWX | CWY | CWWidth | CWHeight;
01146         }
01147     else 
01148         {
01149         XSetWindowAttributes a;
01150         a.background_pixmap = None;
01151         a.override_redirect = True;
01152         obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(),
01153             c->width(), c->height(), 0, CopyFromParent, InputOutput,
01154             CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a );
01155         }
01156     chngs.sibling = c->frameId();
01157     chngs.stack_mode = Below;
01158     XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs );
01159     XMapWindow( qt_xdisplay(), obs_win );
01160     obscuring_windows.append( obs_win );
01161     }
01162 
01163 ObscuringWindows::~ObscuringWindows()
01164     {
01165     max_cache_size = QMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1;
01166     for( QValueList<Window>::ConstIterator it = obscuring_windows.begin();
01167          it != obscuring_windows.end();
01168          ++it ) 
01169         {
01170         XUnmapWindow( qt_xdisplay(), *it );
01171         if( cached->count() < max_cache_size )
01172             cached->prepend( *it );
01173         else
01174             XDestroyWindow( qt_xdisplay(), *it );
01175         }
01176     }
01177 
01178 
01185 bool Workspace::setCurrentDesktop( int new_desktop )
01186     {
01187     if (new_desktop < 1 || new_desktop > number_of_desktops )
01188         return false;
01189 
01190     closeActivePopup();
01191     ++block_focus;
01192 // TODO    Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date
01193     StackingUpdatesBlocker blocker( this );
01194 
01195     int old_desktop = current_desktop;
01196     if (new_desktop != current_desktop) 
01197         {
01198         ++block_showing_desktop;
01199         /*
01200           optimized Desktop switching: unmapping done from back to front
01201           mapping done from front to back => less exposure events
01202         */
01203         Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
01204 
01205         ObscuringWindows obs_wins;
01206 
01207         current_desktop = new_desktop; // change the desktop (so that Client::updateVisibility() works)
01208 
01209         for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
01210             if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
01211                 {
01212                 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop ))
01213                     obs_wins.create( *it );
01214                 (*it)->updateVisibility();
01215                 }
01216 
01217         rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing
01218 
01219         if( movingClient && !movingClient->isOnDesktop( new_desktop ))
01220             movingClient->setDesktop( new_desktop );
01221 
01222         for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it)
01223             if ( (*it)->isOnDesktop( new_desktop ) )
01224                 (*it)->updateVisibility();
01225 
01226         --block_showing_desktop;
01227         if( showingDesktop()) // do this only after desktop change to avoid flicker
01228             resetShowingDesktop( false );
01229         }
01230 
01231     // restore the focus on this desktop
01232     --block_focus;
01233     Client* c = 0;
01234 
01235     if ( options->focusPolicyIsReasonable()) 
01236         {
01237         // Search in focus chain
01238         if ( movingClient != NULL && active_client == movingClient
01239             && focus_chain[currentDesktop()].contains( active_client )
01240             && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
01241             {
01242             c = active_client; // the requestFocus below will fail, as the client is already active
01243             }
01244         if ( !c ) 
01245             {
01246             for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
01247                  it != focus_chain[currentDesktop()].end();
01248                  --it )
01249                 {
01250                 if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop())
01251                     {
01252                     c = *it;
01253                     break;
01254                     }
01255                 }
01256             }
01257         }
01258 
01259     //if "unreasonable focus policy"
01260     // and active_client is on_all_desktops and under mouse (hence == old_active_client),
01261     // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
01262     else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
01263       c = active_client;
01264 
01265     if( c == NULL && !desktops.isEmpty())
01266         c = findDesktop( true, currentDesktop());
01267 
01268     if( c != active_client )
01269         setActiveClient( NULL, Allowed );
01270 
01271     if ( c ) 
01272         requestFocus( c );
01273     else 
01274         focusToNull();
01275 
01276     updateCurrentTopMenu();
01277 
01278     // Update focus chain:
01279     //  If input: chain = { 1, 2, 3, 4 } and current_desktop = 3,
01280     //   Output: chain = { 3, 1, 2, 4 }.
01281 //    kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain[currentDesktop()] index %2\n")
01282 //      .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
01283     for( int i = desktop_focus_chain.find( currentDesktop() ); i > 0; i-- )
01284         desktop_focus_chain[i] = desktop_focus_chain[i-1];
01285     desktop_focus_chain[0] = currentDesktop();
01286 
01287 //    QString s = "desktop_focus_chain[] = { ";
01288 //    for( uint i = 0; i < desktop_focus_chain.size(); i++ )
01289 //        s += QString::number(desktop_focus_chain[i]) + ", ";
01290 //    kdDebug(1212) << s << "}\n";
01291 
01292     if( old_desktop != 0 )  // not for the very first time
01293         popupinfo->showInfo( desktopName(currentDesktop()) );
01294     return true;
01295     }
01296 
01297 // called only from DCOP
01298 void Workspace::nextDesktop()
01299     {
01300     int desktop = currentDesktop() + 1;
01301     setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
01302     }
01303 
01304 // called only from DCOP
01305 void Workspace::previousDesktop()
01306     {
01307     int desktop = currentDesktop() - 1;
01308     setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
01309     }
01310 
01311 int Workspace::desktopToRight( int desktop ) const
01312     {
01313     int x,y;
01314     calcDesktopLayout(x,y);
01315     int dt = desktop-1;
01316     if (layoutOrientation == Qt::Vertical)
01317         {
01318         dt += y;
01319         if ( dt >= numberOfDesktops() ) 
01320             {
01321             if ( options->rollOverDesktops )
01322               dt -= numberOfDesktops();
01323             else
01324               return desktop;
01325             }
01326         }
01327     else
01328         {
01329         int d = (dt % x) + 1;
01330         if ( d >= x ) 
01331             {
01332             if ( options->rollOverDesktops )
01333               d -= x;
01334             else
01335               return desktop;
01336             }
01337         dt = dt - (dt % x) + d;
01338         }
01339     return dt+1;
01340     }
01341 
01342 int Workspace::desktopToLeft( int desktop ) const
01343     {
01344     int x,y;
01345     calcDesktopLayout(x,y);
01346     int dt = desktop-1;
01347     if (layoutOrientation == Qt::Vertical)
01348         {
01349         dt -= y;
01350         if ( dt < 0 ) 
01351             {
01352             if ( options->rollOverDesktops )
01353               dt += numberOfDesktops();
01354             else
01355               return desktop;
01356             }
01357         }
01358     else
01359         {
01360         int d = (dt % x) - 1;
01361         if ( d < 0 ) 
01362             {
01363             if ( options->rollOverDesktops )
01364               d += x;
01365             else
01366               return desktop;
01367             }
01368         dt = dt - (dt % x) + d;
01369         }
01370     return dt+1;
01371     }
01372 
01373 int Workspace::desktopUp( int desktop ) const
01374     {
01375     int x,y;
01376     calcDesktopLayout(x,y);
01377     int dt = desktop-1;
01378     if (layoutOrientation == Qt::Horizontal)
01379         {
01380         dt -= x;
01381         if ( dt < 0 ) 
01382             {
01383             if ( options->rollOverDesktops )
01384               dt += numberOfDesktops();
01385             else
01386               return desktop;
01387             }
01388         }
01389     else
01390         {
01391         int d = (dt % y) - 1;
01392         if ( d < 0 ) 
01393             {
01394             if ( options->rollOverDesktops )
01395               d += y;
01396             else
01397               return desktop;
01398             }
01399         dt = dt - (dt % y) + d;
01400         }
01401     return dt+1;
01402     }
01403 
01404 int Workspace::desktopDown( int desktop ) const
01405     {
01406     int x,y;
01407     calcDesktopLayout(x,y);
01408     int dt = desktop-1;
01409     if (layoutOrientation == Qt::Horizontal)
01410         {
01411         dt += x;
01412         if ( dt >= numberOfDesktops() ) 
01413             {
01414             if ( options->rollOverDesktops )
01415               dt -= numberOfDesktops();
01416             else
01417               return desktop;
01418             }
01419         }
01420     else
01421         {
01422         int d = (dt % y) + 1;
01423         if ( d >= y ) 
01424             {
01425             if ( options->rollOverDesktops )
01426               d -= y;
01427             else
01428               return desktop;
01429             }
01430         dt = dt - (dt % y) + d;
01431         }
01432     return dt+1;
01433     }
01434 
01435 
01439 void Workspace::setNumberOfDesktops( int n )
01440     {
01441     if ( n == number_of_desktops )
01442         return;
01443     int old_number_of_desktops = number_of_desktops;
01444     number_of_desktops = n;
01445 
01446     if( currentDesktop() > numberOfDesktops())
01447         setCurrentDesktop( numberOfDesktops());
01448 
01449     // if increasing the number, do the resizing now,
01450     // otherwise after the moving of windows to still existing desktops
01451     if( old_number_of_desktops < number_of_desktops ) 
01452         {
01453         rootInfo->setNumberOfDesktops( number_of_desktops );
01454         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01455         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01456         delete[] viewports;
01457         updateClientArea( true );
01458         focus_chain.resize( number_of_desktops + 1 );
01459         }
01460 
01461     // if the number of desktops decreased, move all
01462     // windows that would be hidden to the last visible desktop
01463     if( old_number_of_desktops > number_of_desktops ) 
01464         {
01465         for( ClientList::ConstIterator it = clients.begin();
01466               it != clients.end();
01467               ++it) 
01468             {
01469             if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops())
01470                 sendClientToDesktop( *it, numberOfDesktops(), true );
01471             }
01472         }
01473     if( old_number_of_desktops > number_of_desktops ) 
01474         {
01475         rootInfo->setNumberOfDesktops( number_of_desktops );
01476         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01477         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01478         delete[] viewports;
01479         updateClientArea( true );
01480         focus_chain.resize( number_of_desktops + 1 );
01481         }
01482 
01483     saveDesktopSettings();
01484 
01485     // Resize and reset the desktop focus chain.
01486     desktop_focus_chain.resize( n );
01487     for( int i = 0; i < (int)desktop_focus_chain.size(); i++ )
01488         desktop_focus_chain[i] = i+1;
01489     }
01490 
01496 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
01497     {
01498     bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops();
01499     c->setDesktop( desk );
01500     if ( c->desktop() != desk ) // no change or desktop forced
01501         return;
01502     desk = c->desktop(); // Client did range checking
01503 
01504     if ( c->isOnDesktop( currentDesktop() ) )
01505         {
01506         if ( c->wantsTabFocus() && options->focusPolicyIsReasonable()
01507             && !was_on_desktop // for stickyness changes
01508             && !dont_activate )
01509             requestFocus( c );
01510         else
01511             restackClientUnderActive( c );
01512         }
01513     else 
01514         {
01515         raiseClient( c );
01516         }
01517 
01518     ClientList transients_stacking_order = ensureStackingOrder( c->transients());
01519     for( ClientList::ConstIterator it = transients_stacking_order.begin();
01520          it != transients_stacking_order.end();
01521          ++it )
01522         sendClientToDesktop( *it, desk, dont_activate );
01523     updateClientArea();
01524     }
01525 
01526 int Workspace::numScreens() const
01527     {
01528     if( !options->xineramaEnabled )
01529         return 0;
01530     return qApp->desktop()->numScreens();
01531     }
01532 
01533 int Workspace::activeScreen() const
01534     {
01535     if( !options->xineramaEnabled )
01536         return 0;
01537     if( !options->activeMouseScreen )
01538         {
01539         if( activeClient() != NULL && !activeClient()->isOnScreen( active_screen ))
01540             return qApp->desktop()->screenNumber( activeClient()->geometry().center());
01541         return active_screen;
01542         }
01543     return qApp->desktop()->screenNumber( QCursor::pos());
01544     }
01545 
01546 // check whether a client moved completely out of what's considered the active screen,
01547 // if yes, set a new active screen
01548 void Workspace::checkActiveScreen( const Client* c )
01549     {
01550     if( !options->xineramaEnabled )
01551         return;
01552     if( !c->isActive())
01553         return;
01554     if( !c->isOnScreen( active_screen ))
01555         active_screen = c->screen();
01556     }
01557 
01558 // called e.g. when a user clicks on a window, set active screen to be the screen
01559 // where the click occured
01560 void Workspace::setActiveScreenMouse( QPoint mousepos )
01561     {
01562     if( !options->xineramaEnabled )
01563         return;
01564     active_screen = qApp->desktop()->screenNumber( mousepos );
01565     }
01566 
01567 QRect Workspace::screenGeometry( int screen ) const
01568     {
01569     if( !options->xineramaEnabled )
01570         return qApp->desktop()->geometry();
01571     return qApp->desktop()->screenGeometry( screen );
01572     }
01573 
01574 int Workspace::screenNumber( QPoint pos ) const
01575     {
01576     if( !options->xineramaEnabled )
01577         return 0;
01578     return qApp->desktop()->screenNumber( pos );
01579     }
01580 
01581 void Workspace::sendClientToScreen( Client* c, int screen )
01582     {
01583     if( c->screen() == screen ) // don't use isOnScreen(), that's true even when only partially
01584         return;
01585     GeometryUpdatesPostponer blocker( c );
01586     QRect old_sarea = clientArea( MaximizeArea, c );
01587     QRect sarea = clientArea( MaximizeArea, screen, c->desktop());
01588     c->setGeometry( sarea.x() - old_sarea.x() + c->x(), sarea.y() - old_sarea.y() + c->y(),
01589         c->size().width(), c->size().height());
01590     c->checkWorkspacePosition();
01591     ClientList transients_stacking_order = ensureStackingOrder( c->transients());
01592     for( ClientList::ConstIterator it = transients_stacking_order.begin();
01593          it != transients_stacking_order.end();
01594          ++it )
01595         sendClientToScreen( *it, screen );
01596     if( c->isActive())
01597         active_screen = screen;
01598     }
01599 
01600 void Workspace::setDesktopLayout(int o, int x, int y)
01601     {
01602     layoutOrientation = (Qt::Orientation) o;
01603     layoutX = x;
01604     layoutY = y;
01605     }
01606 
01607 void Workspace::calcDesktopLayout(int &x, int &y) const
01608     {
01609     x = layoutX;
01610     y = layoutY;
01611     if ((x == -1) && (y > 0))
01612        x = (numberOfDesktops()+y-1) / y;
01613     else if ((y == -1) && (x > 0))
01614        y = (numberOfDesktops()+x-1) / x;
01615 
01616     if (x == -1)
01617        x = 1;
01618     if (y == -1)
01619        y = 1;
01620     }
01621 
01626 bool Workspace::addSystemTrayWin( WId w )
01627     {
01628     if ( systemTrayWins.contains( w ) )
01629         return TRUE;
01630 
01631     NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor );
01632     WId trayWinFor = ni.kdeSystemTrayWinFor();
01633     if ( !trayWinFor )
01634         return FALSE;
01635     systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) );
01636     XSelectInput( qt_xdisplay(), w,
01637                   StructureNotifyMask
01638                   );
01639     XAddToSaveSet( qt_xdisplay(), w );
01640     propagateSystemTrayWins();
01641     return TRUE;
01642     }
01643 
01648 bool Workspace::removeSystemTrayWin( WId w, bool check )
01649     {
01650     if ( !systemTrayWins.contains( w ) )
01651         return FALSE;
01652     if( check )
01653         {
01654     // When getting UnmapNotify, it's not clear if it's the systray
01655     // reparenting the window into itself, or if it's the window
01656     // going away. This is obviously a flaw in the design, and we were
01657     // just lucky it worked for so long. Kicker's systray temporarily
01658     // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while
01659     // embedding it, allowing KWin to figure out. Kicker just mustn't
01660     // crash before removing it again ... *shrug* .
01661         int num_props;
01662         Atom* props = XListProperties( qt_xdisplay(), w, &num_props );
01663         if( props != NULL )
01664             {
01665             for( int i = 0;
01666                  i < num_props;
01667                  ++i )
01668                 if( props[ i ] == atoms->kde_system_tray_embedding )
01669                     {
01670                     XFree( props );
01671                     return false;
01672                     }
01673             XFree( props );
01674             }
01675         }
01676     systemTrayWins.remove( w );
01677     propagateSystemTrayWins();
01678     return TRUE;
01679     }
01680 
01681 
01685 void Workspace::propagateSystemTrayWins()
01686     {
01687     Window *cl = new Window[ systemTrayWins.count()];
01688 
01689     int i = 0;
01690     for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) 
01691         {
01692         cl[i++] =  (*it).win;
01693         }
01694 
01695     rootInfo->setKDESystemTrayWindows( cl, i );
01696     delete [] cl;
01697     }
01698 
01699 
01700 void Workspace::killWindowId( Window window_to_kill )
01701     {
01702     if( window_to_kill == None )
01703         return;
01704     Window window = window_to_kill;
01705     Client* client = NULL;
01706     for(;;) 
01707         {
01708         client = findClient( FrameIdMatchPredicate( window ));
01709         if( client != NULL ) // found the client
01710             break;
01711         Window parent, root;
01712         Window* children;
01713         unsigned int children_count;
01714         XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count );
01715         if( children != NULL )
01716             XFree( children );
01717         if( window == root ) // we didn't find the client, probably an override-redirect window
01718             break;
01719         window = parent; // go up
01720         }
01721     if( client != NULL )
01722         client->killWindow();
01723     else
01724         XKillClient( qt_xdisplay(), window_to_kill );
01725     }
01726 
01727 
01728 void Workspace::sendPingToWindow( Window window, Time timestamp )
01729     {
01730     rootInfo->sendPing( window, timestamp );
01731     }
01732 
01733 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags )
01734     {
01735     rootInfo->takeActivity( c->window(), timestamp, flags );
01736     pending_take_activity = c;
01737     }
01738 
01739 
01743 void Workspace::slotGrabWindow()
01744     {
01745     if ( active_client ) 
01746         {
01747         QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() );
01748 
01749     //No XShape - no work.
01750         if( Shape::available()) 
01751             {
01752         //As the first step, get the mask from XShape.
01753             int count, order;
01754             XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(),
01755                                                      ShapeBounding, &count, &order);
01756         //The ShapeBounding region is the outermost shape of the window;
01757         //ShapeBounding - ShapeClipping is defined to be the border.
01758         //Since the border area is part of the window, we use bounding
01759         // to limit our work region
01760             if (rects) 
01761                 {
01762         //Create a QRegion from the rectangles describing the bounding mask.
01763                 QRegion contents;
01764                 for (int pos = 0; pos < count; pos++)
01765                     contents += QRegion(rects[pos].x, rects[pos].y,
01766                                         rects[pos].width, rects[pos].height);
01767                 XFree(rects);
01768 
01769         //Create the bounding box.
01770                 QRegion bbox(0, 0, snapshot.width(), snapshot.height());
01771 
01772         //Get the masked away area.
01773                 QRegion maskedAway = bbox - contents;
01774                 QMemArray<QRect> maskedAwayRects = maskedAway.rects();
01775 
01776         //Construct a bitmap mask from the rectangles
01777                 QBitmap mask( snapshot.width(), snapshot.height());
01778                 QPainter p(&mask);
01779                 p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1);
01780                 for (uint pos = 0; pos < maskedAwayRects.count(); pos++)
01781                     p.fillRect(maskedAwayRects[pos], Qt::color0);
01782                 p.end();
01783                 snapshot.setMask(mask);
01784                 }
01785             }
01786 
01787         QClipboard *cb = QApplication::clipboard();
01788         cb->setPixmap( snapshot );
01789         }
01790     else
01791         slotGrabDesktop();
01792     }
01793 
01797 void Workspace::slotGrabDesktop()
01798     {
01799     QPixmap p = QPixmap::grabWindow( qt_xrootwin() );
01800     QClipboard *cb = QApplication::clipboard();
01801     cb->setPixmap( p );
01802     }
01803 
01804 
01808 void Workspace::slotMouseEmulation()
01809     {
01810 
01811     if ( mouse_emulation ) 
01812         {
01813         XUngrabKeyboard(qt_xdisplay(), qt_x_time);
01814         mouse_emulation = FALSE;
01815         return;
01816         }
01817 
01818     if ( XGrabKeyboard(qt_xdisplay(),
01819                        root, FALSE,
01820                        GrabModeAsync, GrabModeAsync,
01821                        qt_x_time) == GrabSuccess ) 
01822         {
01823         mouse_emulation = TRUE;
01824         mouse_emulation_state = 0;
01825         mouse_emulation_window = 0;
01826         }
01827     }
01828 
01835 WId Workspace::getMouseEmulationWindow()
01836     {
01837     Window root;
01838     Window child = qt_xrootwin();
01839     int root_x, root_y, lx, ly;
01840     uint state;
01841     Window w;
01842     Client * c = 0;
01843     do 
01844         {
01845         w = child;
01846         if (!c)
01847             c = findClient( FrameIdMatchPredicate( w ));
01848         XQueryPointer( qt_xdisplay(), w, &root, &child,
01849                        &root_x, &root_y, &lx, &ly, &state );
01850         } while  ( child != None && child != w );
01851 
01852     if ( c && !c->isActive() )
01853         activateClient( c );
01854     return (WId) w;
01855     }
01856 
01860 unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation type, int button, unsigned int state )
01861     {
01862     if ( !w )
01863         return state;
01864     QWidget* widget = QWidget::find( w );
01865     if ( (!widget ||  widget->inherits("QToolButton") ) && !findClient( WindowMatchPredicate( w )) ) 
01866         {
01867         int x, y;
01868         Window xw;
01869         XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw );
01870         if ( type == EmuMove ) 
01871             { // motion notify events
01872             XEvent e;
01873             e.type = MotionNotify;
01874             e.xmotion.window = w;
01875             e.xmotion.root = qt_xrootwin();
01876             e.xmotion.subwindow = w;
01877             e.xmotion.time = qt_x_time;
01878             e.xmotion.x = x;
01879             e.xmotion.y = y;
01880             e.xmotion.x_root = pos.x();
01881             e.xmotion.y_root = pos.y();
01882             e.xmotion.state = state;
01883             e.xmotion.is_hint = NotifyNormal;
01884             XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, &e );
01885             }
01886         else 
01887             {
01888             XEvent e;
01889             e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
01890             e.xbutton.window = w;
01891             e.xbutton.root = qt_xrootwin();
01892             e.xbutton.subwindow = w;
01893             e.xbutton.time = qt_x_time;
01894             e.xbutton.x = x;
01895             e.xbutton.y = y;
01896             e.xbutton.x_root = pos.x();
01897             e.xbutton.y_root = pos.y();
01898             e.xbutton.state = state;
01899             e.xbutton.button = button;
01900             XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, &e );
01901 
01902             if ( type == EmuPress ) 
01903                 {
01904                 switch ( button ) 
01905                     {
01906                     case 2:
01907                         state |= Button2Mask;
01908                         break;
01909                     case 3:
01910                         state |= Button3Mask;
01911                         break;
01912                     default: // 1
01913                         state |= Button1Mask;
01914                         break;
01915                     }
01916                 }
01917             else 
01918                 {
01919                 switch ( button ) 
01920                     {
01921                     case 2:
01922                         state &= ~Button2Mask;
01923                         break;
01924                     case 3:
01925                         state &= ~Button3Mask;
01926                         break;
01927                     default: // 1
01928                         state &= ~Button1Mask;
01929                         break;
01930                     }
01931                 }
01932             }
01933         }
01934     return state;
01935     }
01936 
01940 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev )
01941     {
01942     if ( root != qt_xrootwin() )
01943         return FALSE;
01944     int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0);
01945     int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
01946 
01947     bool is_control = km & ControlMask;
01948     bool is_alt = km & Mod1Mask;
01949     bool is_shift = km & ShiftMask;
01950     int delta = is_control?1:is_alt?32:8;
01951     QPoint pos = QCursor::pos();
01952 
01953     switch ( kc ) 
01954         {
01955         case XK_Left:
01956         case XK_KP_Left:
01957             pos.rx() -= delta;
01958             break;
01959         case XK_Right:
01960         case XK_KP_Right:
01961             pos.rx() += delta;
01962             break;
01963         case XK_Up:
01964         case XK_KP_Up:
01965             pos.ry() -= delta;
01966             break;
01967         case XK_Down:
01968         case XK_KP_Down:
01969             pos.ry() += delta;
01970             break;
01971         case XK_F1:
01972             if ( !mouse_emulation_state )
01973                 mouse_emulation_window = getMouseEmulationWindow();
01974             if ( (mouse_emulation_state & Button1Mask) == 0 )
01975                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
01976             if ( !is_shift )
01977                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01978             break;
01979         case XK_F2:
01980             if ( !mouse_emulation_state )
01981                 mouse_emulation_window = getMouseEmulationWindow();
01982             if ( (mouse_emulation_state & Button2Mask) == 0 )
01983                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state );
01984             if ( !is_shift )
01985                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
01986             break;
01987         case XK_F3:
01988             if ( !mouse_emulation_state )
01989                 mouse_emulation_window = getMouseEmulationWindow();
01990             if ( (mouse_emulation_state & Button3Mask) == 0 )
01991                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state );
01992             if ( !is_shift )
01993                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
01994             break;
01995         case XK_Return:
01996         case XK_space:
01997         case XK_KP_Enter:
01998         case XK_KP_Space: 
01999             {
02000             if ( !mouse_emulation_state ) 
02001                 {
02002             // nothing was pressed, fake a LMB click
02003                 mouse_emulation_window = getMouseEmulationWindow();
02004                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
02005                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
02006                 }
02007             else 
02008                 { // release all
02009                 if ( mouse_emulation_state & Button1Mask )
02010                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
02011                 if ( mouse_emulation_state & Button2Mask )
02012                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
02013                 if ( mouse_emulation_state & Button3Mask )
02014                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
02015                 }
02016             }
02017     // fall through
02018         case XK_Escape:
02019             XUngrabKeyboard(qt_xdisplay(), qt_x_time);
02020             mouse_emulation = FALSE;
02021             return TRUE;
02022         default:
02023             return FALSE;
02024         }
02025 
02026     QCursor::setPos( pos );
02027     if ( mouse_emulation_state )
02028         mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0,  mouse_emulation_state );
02029     return TRUE;
02030 
02031     }
02032 
02038 QWidget* Workspace::desktopWidget()
02039     {
02040     return desktop_widget;
02041     }
02042 
02043 //Delayed focus functions
02044 void Workspace::delayFocus()
02045     {
02046     requestFocus( delayfocus_client );
02047     cancelDelayFocus();
02048     }
02049     
02050 void Workspace::requestDelayFocus( Client* c )
02051     {
02052     delayfocus_client = c;
02053     delete delayFocusTimer;
02054     delayFocusTimer = new QTimer( this );
02055     connect( delayFocusTimer, SIGNAL( timeout() ), this, SLOT( delayFocus() ) );
02056     delayFocusTimer->start( options->delayFocusInterval, TRUE  );
02057     }
02058     
02059 void Workspace::cancelDelayFocus()
02060     {
02061     delete delayFocusTimer;
02062     delayFocusTimer = 0;
02063     }
02064 
02065 // Electric Borders
02066 //========================================================================//
02067 // Electric Border Window management. Electric borders allow a user
02068 // to change the virtual desktop by moving the mouse pointer to the
02069 // borders. Technically this is done with input only windows. Since
02070 // electric borders can be switched on and off, we have these two
02071 // functions to create and destroy them.
02072 void Workspace::checkElectricBorders( bool force )
02073     {
02074     if( force )
02075         destroyBorderWindows();
02076     
02077     electric_current_border = 0;
02078 
02079     QRect r = QApplication::desktop()->geometry();
02080     electricTop = r.top();
02081     electricBottom = r.bottom();
02082     electricLeft = r.left();
02083     electricRight = r.right();
02084 
02085     if (options->electricBorders() == Options::ElectricAlways)
02086        createBorderWindows();
02087     else
02088        destroyBorderWindows();
02089     }
02090 
02091 void Workspace::createBorderWindows()
02092     {
02093     if ( electric_have_borders )
02094         return;
02095 
02096     electric_have_borders = true;
02097 
02098     QRect r = QApplication::desktop()->geometry();
02099     XSetWindowAttributes attributes;
02100     unsigned long valuemask;
02101     attributes.override_redirect = True;
02102     attributes.event_mask =  ( EnterWindowMask | LeaveWindowMask );
02103     valuemask=  (CWOverrideRedirect | CWEventMask | CWCursor );
02104     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02105                                           XC_sb_up_arrow);
02106     electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02107                                 0,0,
02108                                 r.width(),1,
02109                                 0,
02110                                 CopyFromParent, InputOnly,
02111                                 CopyFromParent,
02112                                 valuemask, &attributes);
02113     XMapWindow(qt_xdisplay(), electric_top_border);
02114 
02115     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02116                                           XC_sb_down_arrow);
02117     electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02118                                    0,r.height()-1,
02119                                    r.width(),1,
02120                                    0,
02121                                    CopyFromParent, InputOnly,
02122                                    CopyFromParent,
02123                                    valuemask, &attributes);
02124     XMapWindow(qt_xdisplay(), electric_bottom_border);
02125 
02126     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02127                                           XC_sb_left_arrow);
02128     electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02129                                  0,0,
02130                                  1,r.height(),
02131                                  0,
02132                                  CopyFromParent, InputOnly,
02133                                  CopyFromParent,
02134                                  valuemask, &attributes);
02135     XMapWindow(qt_xdisplay(), electric_left_border);
02136 
02137     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02138                                           XC_sb_right_arrow);
02139     electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02140                                   r.width()-1,0,
02141                                   1,r.height(),
02142                                   0,
02143                                   CopyFromParent, InputOnly,
02144                                   CopyFromParent,
02145                                   valuemask, &attributes);
02146     XMapWindow(qt_xdisplay(),  electric_right_border);
02147     // Set XdndAware on the windows, so that DND enter events are received (#86998)
02148     Atom version = 4; // XDND version
02149     XChangeProperty( qt_xdisplay(), electric_top_border, atoms->xdnd_aware, XA_ATOM,
02150         32, PropModeReplace, ( unsigned char* )&version, 1 );
02151     XChangeProperty( qt_xdisplay(), electric_bottom_border, atoms->xdnd_aware, XA_ATOM,
02152         32, PropModeReplace, ( unsigned char* )&version, 1 );
02153     XChangeProperty( qt_xdisplay(), electric_left_border, atoms->xdnd_aware, XA_ATOM,
02154         32, PropModeReplace, ( unsigned char* )&version, 1 );
02155     XChangeProperty( qt_xdisplay(), electric_right_border, atoms->xdnd_aware, XA_ATOM,
02156         32, PropModeReplace, ( unsigned char* )&version, 1 );
02157     }
02158 
02159 
02160 // Electric Border Window management. Electric borders allow a user
02161 // to change the virtual desktop by moving the mouse pointer to the
02162 // borders. Technically this is done with input only windows. Since
02163 // electric borders can be switched on and off, we have these two
02164 // functions to create and destroy them.
02165 void Workspace::destroyBorderWindows()
02166     {
02167     if( !electric_have_borders)
02168       return;
02169 
02170     electric_have_borders = false;
02171 
02172     if(electric_top_border)
02173       XDestroyWindow(qt_xdisplay(),electric_top_border);
02174     if(electric_bottom_border)
02175       XDestroyWindow(qt_xdisplay(),electric_bottom_border);
02176     if(electric_left_border)
02177       XDestroyWindow(qt_xdisplay(),electric_left_border);
02178     if(electric_right_border)
02179       XDestroyWindow(qt_xdisplay(),electric_right_border);
02180 
02181     electric_top_border    = None;
02182     electric_bottom_border = None;
02183     electric_left_border   = None;
02184     electric_right_border  = None;
02185     }
02186 
02187 void Workspace::clientMoved(const QPoint &pos, Time now)
02188     {
02189     if (options->electricBorders() == Options::ElectricDisabled)
02190        return;
02191 
02192     if ((pos.x() != electricLeft) &&
02193         (pos.x() != electricRight) &&
02194         (pos.y() != electricTop) &&
02195         (pos.y() != electricBottom))
02196        return;
02197 
02198     Time treshold_set = options->electricBorderDelay(); // set timeout
02199     Time treshold_reset = 250; // reset timeout
02200     int distance_reset = 30; // Mouse should not move more than this many pixels
02201 
02202     int border = 0;
02203     if (pos.x() == electricLeft)
02204        border = 1;
02205     else if (pos.x() == electricRight)
02206        border = 2;
02207     else if (pos.y() == electricTop)
02208        border = 3;
02209     else if (pos.y() == electricBottom)
02210        border = 4;
02211 
02212     if ((electric_current_border == border) &&
02213         (timestampDiff(electric_time_last, now) < treshold_reset) &&
02214         ((pos-electric_push_point).manhattanLength() < distance_reset))
02215         {
02216         electric_time_last = now;
02217 
02218         if (timestampDiff(electric_time_first, now) > treshold_set)
02219             {
02220             electric_current_border = 0;
02221 
02222             QRect r = QApplication::desktop()->geometry();
02223             int offset;
02224 
02225             int desk_before = currentDesktop();
02226             switch(border)
02227                 {
02228                 case 1:
02229                  slotSwitchDesktopLeft();
02230                  if (currentDesktop() != desk_before) 
02231                     {
02232                     offset = r.width() / 5;
02233                     QCursor::setPos(r.width() - offset, pos.y());
02234                     }
02235                 break;
02236 
02237                case 2:
02238                 slotSwitchDesktopRight();
02239                 if (currentDesktop() != desk_before) 
02240                     {
02241                     offset = r.width() / 5;
02242                     QCursor::setPos(offset, pos.y());
02243                     }
02244                 break;
02245 
02246                case 3:
02247                 slotSwitchDesktopUp();
02248                 if (currentDesktop() != desk_before) 
02249                     {
02250                     offset = r.height() / 5;
02251                     QCursor::setPos(pos.x(), r.height() - offset);
02252                     }
02253                 break;
02254 
02255                case 4:
02256                 slotSwitchDesktopDown();
02257                 if (currentDesktop() != desk_before) 
02258                     {
02259                     offset = r.height() / 5;
02260                     QCursor::setPos(pos.x(), offset);
02261                     }
02262                 break;
02263                 }
02264             return;
02265             }
02266         }
02267     else 
02268         {
02269         electric_current_border = border;
02270         electric_time_first = now;
02271         electric_time_last = now;
02272         electric_push_point = pos;
02273         }
02274 
02275     int mouse_warp = 1;
02276 
02277   // reset the pointer to find out wether the user is really pushing
02278     switch( border)
02279         {
02280         case 1: QCursor::setPos(pos.x()+mouse_warp, pos.y()); break;
02281         case 2: QCursor::setPos(pos.x()-mouse_warp, pos.y()); break;
02282         case 3: QCursor::setPos(pos.x(), pos.y()+mouse_warp); break;
02283         case 4: QCursor::setPos(pos.x(), pos.y()-mouse_warp); break;
02284         }
02285     }
02286 
02287 // this function is called when the user entered an electric border
02288 // with the mouse. It may switch to another virtual desktop
02289 bool Workspace::electricBorder(XEvent *e)
02290     {
02291     if( !electric_have_borders )
02292         return false;
02293     if( e->type == EnterNotify )
02294         {
02295         if( e->xcrossing.window == electric_top_border ||
02296             e->xcrossing.window == electric_left_border ||
02297             e->xcrossing.window == electric_bottom_border ||
02298             e->xcrossing.window == electric_right_border)
02299             // the user entered an electric border
02300             {
02301             clientMoved( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time );
02302             return true;
02303             }
02304         }
02305     if( e->type == ClientMessage )
02306         {
02307         if( e->xclient.message_type == atoms->xdnd_position
02308             && ( e->xclient.window == electric_top_border
02309                  || e->xclient.window == electric_bottom_border
02310                  || e->xclient.window == electric_left_border
02311                  || e->xclient.window == electric_right_border ))
02312             {
02313             updateXTime();
02314             clientMoved( QPoint( e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), qt_x_time );
02315             return true;
02316             }
02317         }
02318     return false;
02319     }
02320 
02321 // electric borders (input only windows) have to be always on the
02322 // top. For that reason kwm calls this function always after some
02323 // windows have been raised.
02324 void Workspace::raiseElectricBorders()
02325     {
02326 
02327     if(electric_have_borders)
02328         {
02329         XRaiseWindow(qt_xdisplay(), electric_top_border);
02330         XRaiseWindow(qt_xdisplay(), electric_left_border);
02331         XRaiseWindow(qt_xdisplay(), electric_bottom_border);
02332         XRaiseWindow(qt_xdisplay(), electric_right_border);
02333         }
02334     }
02335 
02336 void Workspace::addTopMenu( Client* c )
02337     {
02338     assert( c->isTopMenu());
02339     assert( !topmenus.contains( c ));
02340     topmenus.append( c );
02341     if( managingTopMenus())
02342         {
02343         int minsize = c->minSize().height();
02344         if( minsize > topMenuHeight())
02345             {
02346             topmenu_height = minsize;
02347             updateTopMenuGeometry();
02348             }
02349         updateTopMenuGeometry( c );
02350         updateCurrentTopMenu();
02351         }
02352 //        kdDebug() << "NEW TOPMENU:" << c << endl;
02353     }
02354 
02355 void Workspace::removeTopMenu( Client* c )
02356     {
02357 //    if( c->isTopMenu())
02358 //        kdDebug() << "REMOVE TOPMENU:" << c << endl;
02359     assert( c->isTopMenu());
02360     assert( topmenus.contains( c ));
02361     topmenus.remove( c );
02362     updateCurrentTopMenu();
02363     // TODO reduce topMenuHeight() if possible?
02364     }
02365 
02366 void Workspace::lostTopMenuSelection()
02367     {
02368 //    kdDebug() << "lost TopMenu selection" << endl;
02369     // make sure this signal is always set when not owning the selection
02370     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02371     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02372     if( !managing_topmenus )
02373         return;
02374     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02375     disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
02376     managing_topmenus = false;
02377     delete topmenu_space;
02378     topmenu_space = NULL;
02379     updateClientArea();
02380     for( ClientList::ConstIterator it = topmenus.begin();
02381          it != topmenus.end();
02382          ++it )
02383         (*it)->checkWorkspacePosition();
02384     }
02385 
02386 void Workspace::lostTopMenuOwner()
02387     {
02388     if( !options->topMenuEnabled())
02389         return;
02390 //    kdDebug() << "TopMenu selection lost owner" << endl;
02391     if( !topmenu_selection->claim( false ))
02392         {
02393 //        kdDebug() << "Failed to claim TopMenu selection" << endl;
02394         return;
02395         }
02396 //    kdDebug() << "claimed TopMenu selection" << endl;
02397     setupTopMenuHandling();
02398     }
02399 
02400 void Workspace::setupTopMenuHandling()
02401     {
02402     if( managing_topmenus )
02403         return;
02404     connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
02405     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02406     managing_topmenus = true;
02407     topmenu_space = new QWidget;
02408     Window stack[ 2 ];
02409     stack[ 0 ] = supportWindow->winId();
02410     stack[ 1 ] = topmenu_space->winId();
02411     XRestackWindows(qt_xdisplay(), stack, 2);
02412     updateTopMenuGeometry();
02413     topmenu_space->show();
02414     updateClientArea();
02415     updateCurrentTopMenu();
02416     }
02417 
02418 int Workspace::topMenuHeight() const
02419     {
02420     if( topmenu_height == 0 )
02421         { // simply create a dummy menubar and use its preffered height as the menu height
02422         KMenuBar tmpmenu;
02423         tmpmenu.insertItem( "dummy" );
02424         topmenu_height = tmpmenu.sizeHint().height();
02425         }
02426     return topmenu_height;
02427     }
02428 
02429 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge )
02430     {
02431     return mgr->createDecoration( bridge );
02432     }
02433 
02434 QString Workspace::desktopName( int desk ) const
02435     {
02436     return QString::fromUtf8( rootInfo->desktopName( desk ) );
02437     }
02438 
02439 bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data )
02440     {
02441     return startup->checkStartup( w, id, data ) == KStartupInfo::Match;
02442     }
02443 
02448 void Workspace::focusToNull()
02449     {
02450     XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time );
02451     }
02452 
02453 void Workspace::helperDialog( const QString& message, const Client* c )
02454     {
02455     QStringList args;
02456     QString type;
02457     if( message == "noborderaltf3" )
02458         {
02459         QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
02460             .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
02461         args << "--msgbox" <<
02462               i18n( "You have selected to show a window without its border.\n"
02463                     "Without the border, you will not be able to enable the border "
02464                     "again using the mouse: use the window operations menu instead, "
02465                     "activated using the %1 keyboard shortcut." )
02466                 .arg( shortcut );
02467         type = "altf3warning";
02468         }
02469     else if( message == "fullscreenaltf3" )
02470         {
02471         QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
02472             .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
02473         args << "--msgbox" <<
02474               i18n( "You have selected to show a window in fullscreen mode.\n"
02475                     "If the application itself does not have an option to turn the fullscreen "
02476                     "mode off you will not be able to disable it "
02477                     "again using the mouse: use the window operations menu instead, "
02478                     "activated using the %1 keyboard shortcut." )
02479                 .arg( shortcut );
02480         type = "altf3warning";
02481         }
02482     else
02483         assert( false );
02484     KProcess proc;
02485     proc << "kdialog" << args;
02486     if( !type.isEmpty())
02487         {
02488         KConfig cfg( "kwin_dialogsrc" );
02489         cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox
02490         if( !cfg.readBoolEntry( type, true )) // has don't show again checked
02491             return;                           // save launching kdialog
02492         proc << "--dontagain" << "kwin_dialogsrc:" + type;
02493         }
02494     if( c != NULL )
02495         proc << "--embed" << QString::number( c->window());
02496     proc.start( KProcess::DontCare );
02497     }
02498 
02499 
02500 // kompmgr stuff
02501     
02502 void Workspace::startKompmgr()
02503 {
02504     if (!kompmgr || kompmgr->isRunning())
02505         return;
02506     if (!kompmgr->start(KProcess::OwnGroup, KProcess::Stderr))
02507         {
02508         options->useTranslucency = FALSE;
02509         KProcess proc;
02510         proc << "kdialog" << "--error"
02511             << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.")
02512             << "--title" << "Composite Manager Failure";
02513         proc.start(KProcess::DontCare);
02514         }
02515     else
02516         {
02517         delete kompmgr_selection;
02518         char selection_name[ 100 ];
02519         sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( qt_xdisplay()));
02520         kompmgr_selection = new KSelectionOwner( selection_name );
02521         connect( kompmgr_selection, SIGNAL( lostOwnership()), SLOT( stopKompmgr()));
02522         kompmgr_selection->claim( true );
02523         connect(kompmgr, SIGNAL(processExited(KProcess*)), SLOT(restartKompmgr()));
02524         options->useTranslucency = TRUE;
02525         allowKompmgrRestart = FALSE;
02526         QTimer::singleShot( 60000, this, SLOT(unblockKompmgrRestart()) );
02527         QByteArray ba;
02528         QDataStream arg(ba, IO_WriteOnly);
02529         arg << "";
02530         kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStarted()", ba);
02531         }
02532         if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider
02533 }
02534 
02535 void Workspace::stopKompmgr()
02536 {
02537     if (!kompmgr  || !kompmgr->isRunning())
02538         return;
02539     delete kompmgr_selection;
02540     kompmgr_selection = NULL;
02541     kompmgr->disconnect(this, SLOT(restartKompmgr()));
02542     options->useTranslucency = FALSE;
02543     if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider
02544     kompmgr->kill();
02545     QByteArray ba;
02546     QDataStream arg(ba, IO_WriteOnly);
02547     arg << "";
02548     kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStopped()", ba);
02549 }
02550 
02551 bool Workspace::kompmgrIsRunning()
02552 {
02553    return kompmgr && kompmgr->isRunning();
02554 }
02555 
02556 void Workspace::unblockKompmgrRestart()
02557 {
02558     allowKompmgrRestart = TRUE;
02559 }
02560 
02561 void Workspace::restartKompmgr()
02562 // this is for inernal purpose (crashhandling) only, usually you want to use workspace->stopKompmgr(); QTimer::singleShot(200, workspace, SLOT(startKompmgr()));
02563 {
02564     if (!allowKompmgrRestart) // uh-ohh
02565         {
02566         delete kompmgr_selection;
02567         kompmgr_selection = NULL;
02568         options->useTranslucency = FALSE;
02569         KProcess proc;
02570         proc << "kdialog" << "--error"
02571             << i18n( "The Composite Manager crashed twice within a minute and is therefore disabled for this session.")
02572             << "--title" << i18n("Composite Manager Failure");
02573         proc.start(KProcess::DontCare);
02574         return;
02575         }
02576     if (!kompmgr)
02577         return;
02578 // this should be useless, i keep it for maybe future need
02579 //     if (!kcompmgr)
02580 //         {
02581 //         kompmgr = new KProcess;
02582 //         kompmgr->clearArguments();
02583 //         *kompmgr << "kompmgr";
02584 //         }
02585 // -------------------
02586     if (!kompmgr->start(KProcess::NotifyOnExit, KProcess::Stderr))
02587         {
02588         delete kompmgr_selection;
02589         kompmgr_selection = NULL;
02590         options->useTranslucency = FALSE;
02591         KProcess proc;
02592         proc << "kdialog" << "--error"
02593             << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.")
02594             << "--title" << i18n("Composite Manager Failure");
02595         proc.start(KProcess::DontCare);
02596         }
02597     else
02598         {
02599         allowKompmgrRestart = FALSE;
02600         QTimer::singleShot( 60000, this, SLOT(unblockKompmgrRestart()) );
02601         }
02602 }
02603 
02604 void Workspace::handleKompmgrOutput( KProcess* , char *buffer, int buflen)
02605 {
02606     QString message;
02607     QString output = QString::fromLocal8Bit( buffer, buflen );
02608     if (output.contains("Started",false))
02609         ; // don't do anything, just pass to the connection release
02610     else if (output.contains("Can't open display",false))
02611         message = i18n("<qt><b>kompmgr failed to open the display</b><br>There is probably an invalid display entry in your ~/.xcompmgrrc.</qt>");
02612     else if (output.contains("No render extension",false))
02613         message = i18n("<qt><b>kompmgr cannot find the Xrender extension</b><br>You are using either an outdated or a crippled version of XOrg.<br>Get XOrg &ge; 6.8 from www.freedesktop.org.<br></qt>");
02614     else if (output.contains("No composite extension",false))
02615         message = i18n("<qt><b>Composite extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.<br>Additionally, you need to add a new section to your X config file:<br>"
02616         "<i>Section \"Extensions\"<br>"
02617         "Option \"Composite\" \"Enable\"<br>"
02618         "EndSection</i></qt>");
02619     else if (output.contains("No damage extension",false))
02620         message = i18n("<qt><b>Damage extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.</qt>");
02621     else if (output.contains("No XFixes extension",false))
02622         message = i18n("<qt><b>XFixes extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.</qt>");
02623     else return; //skip others
02624     // kompmgr startup failed or succeeded, release connection
02625     kompmgr->closeStderr();
02626     disconnect(kompmgr, SIGNAL(receivedStderr(KProcess*, char*, int)), this, SLOT(handleKompmgrOutput(KProcess*, char*, int)));
02627     if( !message.isEmpty())
02628         {
02629         KProcess proc;
02630         proc << "kdialog" << "--error"
02631             << message
02632             << "--title" << i18n("Composite Manager Failure");
02633         proc.start(KProcess::DontCare);
02634         }
02635 }
02636     
02637         
02638 void Workspace::setOpacity(unsigned long winId, unsigned int opacityPercent)
02639 {
02640     if (opacityPercent > 100) opacityPercent = 100;
02641     for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
02642         if (winId == (*it)->window())
02643             {
02644             (*it)->setOpacity(opacityPercent < 100, (unsigned int)((opacityPercent/100.0)*0xFFFFFFFF));
02645             return;
02646             }
02647 }
02648 
02649 void Workspace::setShadowSize(unsigned long winId, unsigned int shadowSizePercent)
02650 {
02651     //this is open to the user by dcop - to avoid stupid trials, we limit the max shadow size to 400%
02652     if (shadowSizePercent > 400) shadowSizePercent = 400;
02653     for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
02654         if (winId == (*it)->window())
02655             {
02656             (*it)->setShadowSize(shadowSizePercent);
02657             return;
02658             }
02659 }
02660 
02661 void Workspace::setUnshadowed(unsigned long winId)
02662 {
02663     for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
02664         if (winId == (*it)->window())
02665             {
02666             (*it)->setShadowSize(0);
02667             return;
02668             }
02669 }
02670 
02671 void Workspace::setShowingDesktop( bool showing )
02672     {
02673     rootInfo->setShowingDesktop( showing );
02674     showing_desktop = showing;
02675     ++block_showing_desktop;
02676     if( showing_desktop )
02677         {
02678         showing_desktop_clients.clear();
02679         ++block_focus;
02680         ClientList cls = stackingOrder();
02681         // find them first, then minimize, otherwise transients may get minimized with the window
02682         // they're transient for
02683         for( ClientList::ConstIterator it = cls.begin();
02684              it != cls.end();
02685              ++it )
02686             {
02687             if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow())
02688                 showing_desktop_clients.prepend( *it ); // topmost first to reduce flicker
02689             }
02690         for( ClientList::ConstIterator it = showing_desktop_clients.begin();
02691              it != showing_desktop_clients.end();
02692              ++it )
02693             (*it)->minimize(true);
02694         --block_focus;
02695         if( Client* desk = findDesktop( true, currentDesktop()))
02696             requestFocus( desk );
02697         }
02698     else
02699         {
02700         for( ClientList::ConstIterator it = showing_desktop_clients.begin();
02701              it != showing_desktop_clients.end();
02702              ++it )
02703             (*it)->unminimize(true);
02704         if( showing_desktop_clients.count() > 0 )
02705             requestFocus( showing_desktop_clients.first());
02706         showing_desktop_clients.clear();
02707         }
02708     --block_showing_desktop;
02709     }
02710 
02711 // Following Kicker's behavior:
02712 // Changing a virtual desktop resets the state and shows the windows again.
02713 // Unminimizing a window resets the state but keeps the windows hidden (except
02714 // the one that was unminimized).
02715 // A new window resets the state and shows the windows again, with the new window
02716 // being active. Due to popular demand (#67406) by people who apparently
02717 // don't see a difference between "show desktop" and "minimize all", this is not
02718 // true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing
02719 // a new window resets the state but doesn't show windows.
02720 void Workspace::resetShowingDesktop( bool keep_hidden )
02721     {
02722     if( block_showing_desktop > 0 )
02723         return;
02724     rootInfo->setShowingDesktop( false );
02725     showing_desktop = false;
02726     ++block_showing_desktop;
02727     if( !keep_hidden )
02728         {
02729         for( ClientList::ConstIterator it = showing_desktop_clients.begin();
02730              it != showing_desktop_clients.end();
02731              ++it )
02732             (*it)->unminimize(true);
02733         }
02734     showing_desktop_clients.clear();
02735     --block_showing_desktop;
02736     }
02737 
02738 // Activating/deactivating this feature works like this:
02739 // When nothing is active, and the shortcut is pressed, global shortcuts are disabled
02740 //   (using global_shortcuts_disabled)
02741 // When a window that has disabling forced is activated, global shortcuts are disabled.
02742 //   (using global_shortcuts_disabled_for_client)
02743 // When a shortcut is pressed and global shortcuts are disabled (either by a shortcut
02744 // or for a client), they are enabled again.
02745 void Workspace::slotDisableGlobalShortcuts()
02746     {
02747     if( global_shortcuts_disabled || global_shortcuts_disabled_for_client )
02748         disableGlobalShortcuts( false );
02749     else
02750         disableGlobalShortcuts( true );
02751     }
02752 
02753 static bool pending_dfc = false;
02754 
02755 void Workspace::disableGlobalShortcutsForClient( bool disable )
02756     {
02757     if( global_shortcuts_disabled_for_client == disable )
02758         return;
02759     if( !global_shortcuts_disabled )
02760         {
02761         if( disable )
02762             pending_dfc = true;
02763         KIPC::sendMessageAll( KIPC::BlockShortcuts, disable );
02764         // kwin will get the kipc message too
02765         }
02766     }
02767 
02768 void Workspace::disableGlobalShortcuts( bool disable )
02769     {
02770     KIPC::sendMessageAll( KIPC::BlockShortcuts, disable );
02771     // kwin will get the kipc message too
02772     }
02773 
02774 void Workspace::kipcMessage( int id, int data )
02775     {
02776     if( id != KIPC::BlockShortcuts )
02777         return;
02778     if( pending_dfc && data )
02779         {
02780         global_shortcuts_disabled_for_client = true;
02781         pending_dfc = false;
02782         }
02783     else
02784         {
02785         global_shortcuts_disabled = data;
02786         global_shortcuts_disabled_for_client = false;
02787         }
02788     // update also Alt+LMB actions etc.
02789     for( ClientList::ConstIterator it = clients.begin();
02790          it != clients.end();
02791          ++it )
02792         (*it)->updateMouseGrab();
02793     }
02794 
02795 } // namespace
02796 
02797 #include "workspace.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys