• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • @topname@
  • Sitemap
  • Contact Us
 

kwin

workspace.cpp

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

kwin

Skip menu "kwin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

@topname@

Skip menu "@topname@"
  • kate
  • kwin
  •   lib
  • libkonq
Generated for @topname@ by doxygen 1.5.9
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