kwin Library API Documentation

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