workspace.cpp

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