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