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