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