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