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