• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

workspace.cpp

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

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal