kwin Library API Documentation

workspace.cpp

00001 /*****************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 You can Freely distribute this program under the GNU General Public
00009 License. See the file "COPYING" for the exact licensing terms.
00010 ******************************************************************/
00011 
00012 //#define QT_CLEAN_NAMESPACE
00013 
00014 #include "workspace.h"
00015 
00016 #include <kapplication.h>
00017 #include <kstartupinfo.h>
00018 #include <fixx11h.h>
00019 #include <kconfig.h>
00020 #include <kglobal.h>
00021 #include <qpopupmenu.h>
00022 #include <klocale.h>
00023 #include <qregexp.h>
00024 #include <qpainter.h>
00025 #include <qbitmap.h>
00026 #include <qclipboard.h>
00027 #include <kmenubar.h>
00028 #include <kprocess.h>
00029 #include <kglobalaccel.h>
00030 
00031 #include "plugins.h"
00032 #include "client.h"
00033 #include "popupinfo.h"
00034 #include "tabbox.h"
00035 #include "atoms.h"
00036 #include "placement.h"
00037 #include "notifications.h"
00038 #include "group.h"
00039 
00040 #include <X11/extensions/shape.h>
00041 #include <X11/keysym.h>
00042 #include <X11/keysymdef.h>
00043 #include <X11/cursorfont.h>
00044 
00045 extern Time qt_x_time;
00046 
00047 namespace KWinInternal
00048 {
00049 
00050 extern int screen_number;
00051 
00052 static Window null_focus_window = 0;
00053 
00054 Workspace *Workspace::_self = 0;
00055 
00056 // Rikkus: This class is too complex. It needs splitting further.
00057 // It's a nightmare to understand, especially with so few comments :(
00058 
00059 // Matthias: Feel free to ask me questions about it. Feel free to add
00060 // comments. I dissagree that further splittings makes it easier. 2500
00061 // lines are not too much. It's the task that is complex, not the
00062 // code.
00063 Workspace::Workspace( bool restore )
00064   : DCOPObject        ("KWinInterface"),
00065     QObject           (0, "workspace"),
00066     current_desktop   (0),
00067     number_of_desktops(0),
00068     popup_client      (0),
00069     desktop_widget    (0),
00070     active_client     (0),
00071     last_active_client     (0),
00072     most_recently_raised (0),
00073     movingClient(0),
00074     was_user_interaction (false),
00075     session_saving    (false),
00076     control_grab      (false),
00077     tab_grab          (false),
00078     mouse_emulation   (false),
00079     block_focus       (0),
00080     tab_box           (0),
00081     popupinfo         (0),
00082     popup             (0),
00083     advanced_popup    (0),
00084     desk_popup        (0),
00085     desk_popup_index  (0),
00086     keys              (0),
00087     root              (0),
00088     workspaceInit     (true),
00089     startup(0), electric_have_borders(false),
00090     electric_current_border(0),
00091     electric_top_border(None),
00092     electric_bottom_border(None),
00093     electric_left_border(None),
00094     electric_right_border(None),
00095     layoutOrientation(Qt::Vertical),
00096     layoutX(-1),
00097     layoutY(2),
00098     workarea(NULL),
00099     set_active_client_recursion( 0 ),
00100     block_stacking_updates( 0 )
00101     {
00102     _self = this;
00103     mgr = new PluginMgr;
00104     root = qt_xrootwin();
00105     default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() );
00106     installed_colormap = default_colormap;
00107     session.setAutoDelete( TRUE );
00108 
00109     updateXTime(); // needed for proper initialization of user_time in Client ctor
00110 
00111     electric_time_first = qt_x_time;
00112     electric_time_last = qt_x_time;
00113 
00114     if ( restore )
00115       loadSessionInfo();
00116 
00117     loadFakeSessionInfo();
00118 
00119     (void) QApplication::desktop(); // trigger creation of desktop widget
00120 
00121     desktop_widget =
00122       new QWidget(
00123         0,
00124         "desktop_widget",
00125         Qt::WType_Desktop | Qt::WPaintUnclipped
00126     );
00127 
00128     kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later
00129     // call this before XSelectInput() on the root window
00130     startup = new KStartupInfo(
00131         KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this );
00132 
00133     // select windowmanager privileges
00134     XSelectInput(qt_xdisplay(), root,
00135                  KeyPressMask |
00136                  PropertyChangeMask |
00137                  ColormapChangeMask |
00138                  SubstructureRedirectMask |
00139                  SubstructureNotifyMask
00140                  );
00141 
00142     Shape::init();
00143 
00144     // compatibility
00145     long data = 1;
00146 
00147     XChangeProperty(
00148       qt_xdisplay(),
00149       qt_xrootwin(),
00150       atoms->kwin_running,
00151       atoms->kwin_running,
00152       32,
00153       PropModeAppend,
00154       (unsigned char*) &data,
00155       1
00156     );
00157 
00158     initShortcuts();
00159     tab_box = new TabBox( this );
00160     popupinfo = new PopupInfo( );
00161 
00162     init();
00163 
00164 #if (QT_VERSION-0 >= 0x030200) // XRANDR support
00165     connect( kapp->desktop(), SIGNAL( resized( int )), SLOT( desktopResized()));
00166 #endif
00167     }
00168 
00169 
00170 void Workspace::init()
00171     {
00172     if (options->electricBorders() == Options::ElectricAlways)
00173        createBorderWindows();
00174 
00175     supportWindow = new QWidget;
00176     XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp
00177 
00178     unsigned long protocols[ 5 ] =
00179         {
00180         NET::Supported |
00181         NET::SupportingWMCheck |
00182         NET::ClientList |
00183         NET::ClientListStacking |
00184         NET::DesktopGeometry |
00185         NET::NumberOfDesktops |
00186         NET::CurrentDesktop |
00187         NET::ActiveWindow |
00188         NET::WorkArea |
00189         NET::CloseWindow |
00190         NET::DesktopNames |
00191         NET::KDESystemTrayWindows |
00192         NET::WMName |
00193         NET::WMVisibleName |
00194         NET::WMDesktop |
00195         NET::WMWindowType |
00196         NET::WMState |
00197         NET::WMStrut |
00198         NET::WMIconGeometry |
00199         NET::WMIcon |
00200         NET::WMPid |
00201         NET::WMMoveResize |
00202         NET::WMKDESystemTrayWinFor |
00203         NET::WMKDEFrameStrut |
00204         NET::WMPing
00205         ,
00206         NET::NormalMask |
00207         NET::DesktopMask |
00208         NET::DockMask |
00209         NET::ToolbarMask |
00210         NET::MenuMask |
00211         NET::DialogMask |
00212         NET::OverrideMask |
00213         NET::TopMenuMask |
00214         NET::UtilityMask |
00215         NET::SplashMask |
00216         0
00217         ,
00218         NET::Modal |
00219 //        NET::Sticky |  // large desktops not supported (and probably never will be)
00220         NET::MaxVert |
00221         NET::MaxHoriz |
00222         NET::Shaded |
00223         NET::SkipTaskbar |
00224         NET::KeepAbove |
00225 //        NET::StaysOnTop |  the same like KeepAbove
00226         NET::SkipPager |
00227         NET::Hidden |
00228         NET::FullScreen |
00229         NET::KeepBelow |
00230         NET::DemandsAttention |
00231         0
00232         ,
00233         NET::WM2UserTime |
00234         NET::WM2StartupId |
00235         NET::WM2AllowedActions |
00236         NET::WM2RestackWindow |
00237         NET::WM2MoveResizeWindow |
00238         0
00239         ,
00240         NET::ActionMove |
00241         NET::ActionResize |
00242         NET::ActionMinimize |
00243         NET::ActionShade |
00244 //        NET::ActionStick | // Sticky state is not supported
00245         NET::ActionMaxVert |
00246         NET::ActionMaxHoriz |
00247         NET::ActionFullScreen |
00248         NET::ActionChangeDesktop |
00249         NET::ActionClose |
00250         0
00251         ,
00252         };
00253 
00254     rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin",
00255         protocols, 5, qt_xscreen() );
00256 
00257     loadDesktopSettings();
00258     // extra NETRootInfo instance in Client mode is needed to get the values of the properties
00259     NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop );
00260     int initial_desktop;
00261     if( !kapp->isSessionRestored())
00262         initial_desktop = client_info.currentDesktop();
00263     else
00264         {
00265         KConfigGroupSaver saver( kapp->sessionConfig(), "Session" );
00266         initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 );
00267         }
00268     if( !setCurrentDesktop( initial_desktop ))
00269         setCurrentDesktop( 1 );
00270 
00271     // now we know how many desktops we'll, thus, we initialise the positioning object
00272     initPositioning = new Placement(this);
00273 
00274     unsigned int i, nwins;
00275     Window root_return, parent_return, *wins;
00276     XWindowAttributes attr;
00277 
00278     connect(&reconfigureTimer, SIGNAL(timeout()), this,
00279             SLOT(slotReconfigure()));
00280     connect( &updateToolWindowsTimer, SIGNAL( timeout()), this, SLOT( slotUpdateToolWindows()));
00281 
00282     connect(kapp, SIGNAL(appearanceChanged()), this,
00283             SLOT(slotReconfigure()));
00284     connect(kapp, SIGNAL(settingsChanged(int)), this,
00285             SLOT(slotSettingsChanged(int)));
00286 
00287     active_client = NULL;
00288     rootInfo->setActiveWindow( None );
00289     focusToNull();
00290     if( !kapp->isSessionRestored())
00291         ++block_focus; // because it will be set below
00292 
00293     char nm[ 100 ];
00294     sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay()));
00295     Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False );
00296     topmenu_selection = new KSelectionOwner( topmenu_atom );
00297     topmenu_watcher = new KSelectionWatcher( topmenu_atom );
00298     topmenu_height = 0;
00299     managing_topmenus = false;
00300     topmenu_space = NULL;
00301 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before
00302 
00303         { // begin updates blocker block
00304         StackingUpdatesBlocker blocker( this );
00305 
00306         if( options->topMenuEnabled() && topmenu_selection->claim( false ))
00307             setupTopMenuHandling(); // this can call updateStackingOrder()
00308         else
00309             lostTopMenuSelection();
00310 
00311         XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins);
00312         for (i = 0; i < nwins; i++) 
00313             {
00314             XGetWindowAttributes(qt_xdisplay(), wins[i], &attr);
00315             if (attr.override_redirect )
00316                 continue;
00317             if( topmenu_space && topmenu_space->winId() == wins[ i ] )
00318                 continue;
00319             if (attr.map_state != IsUnmapped) 
00320                 {
00321                 if ( addSystemTrayWin( wins[i] ) )
00322                     continue;
00323                 Client* c = createClient( wins[i], true );
00324                 if ( c != NULL && root != qt_xrootwin() ) 
00325                     { // TODO what is this?
00326                 // TODO may use QWidget:.create
00327                     XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 );
00328                     c->move(0,0);
00329                     }
00330                 }
00331             }
00332         if ( wins )
00333             XFree((void *) wins);
00334     // propagate clients, will really happen at the end of the updates blocker block
00335         updateStackingOrder( true );
00336 
00337         updateClientArea();
00338         raiseElectricBorders();
00339 
00340     // NETWM spec says we have to set it to (0,0) if we don't support it
00341         NETPoint* viewports = new NETPoint[ number_of_desktops ];
00342         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
00343         delete[] viewports;
00344         QRect geom = QApplication::desktop()->geometry();
00345         NETSize desktop_geometry;
00346         desktop_geometry.width = geom.width();
00347         desktop_geometry.height = geom.height();
00348     // TODO update also after gaining XRANDR support
00349         rootInfo->setDesktopGeometry( -1, desktop_geometry );
00350 
00351         } // end updates blocker block
00352 
00353     Client* new_active_client = NULL;
00354     if( !kapp->isSessionRestored())
00355         {
00356         --block_focus;
00357         new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow()));
00358         }
00359     if( new_active_client == NULL
00360         && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage()
00361         {
00362         if( new_active_client == NULL )
00363             new_active_client = topClientOnDesktop( currentDesktop());
00364         if( new_active_client == NULL && !desktops.isEmpty() )
00365             new_active_client = findDesktop( true, currentDesktop());
00366         }
00367     if( new_active_client != NULL )
00368         activateClient( new_active_client );
00369     // SELI TODO this won't work with unreasonable focus policies,
00370     // and maybe in rare cases also if the selected client doesn't
00371     // want focus
00372     workspaceInit = false;
00373 // TODO ungrabXServer()
00374     }
00375 
00376 Workspace::~Workspace()
00377     {
00378     blockStackingUpdates( true );
00379 // TODO    grabXServer();
00380     // use stacking_order, so that kwin --replace keeps stacking order
00381     for( ClientList::ConstIterator it = stacking_order.begin();
00382          it != stacking_order.end();
00383          ++it )
00384         {
00385     // only release the window
00386         if( !(*it)->isDesktop()) // TODO ?
00387             storeFakeSessionInfo( *it );
00388         (*it)->releaseWindow( true );
00389         }
00390     delete desktop_widget;
00391     delete tab_box;
00392     delete popupinfo;
00393     delete popup;
00394     if ( root == qt_xrootwin() )
00395         XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running);
00396 
00397     writeFakeSessionInfo();
00398     KGlobal::config()->sync();
00399 
00400     delete rootInfo;
00401     delete supportWindow;
00402     delete mgr;
00403     delete[] workarea;
00404     delete startup;
00405     delete initPositioning;
00406     delete topmenu_watcher;
00407     delete topmenu_selection;
00408     delete topmenu_space;
00409 // TODO    ungrabXServer();
00410     _self = 0;
00411     }
00412 
00413 Client* Workspace::createClient( Window w, bool is_mapped )
00414     {
00415     StackingUpdatesBlocker blocker( this );
00416     Client* c = new Client( this );
00417     if( !c->manage( w, is_mapped ))
00418         {
00419         Client::deleteClient( c, Allowed );
00420         return NULL;
00421         }
00422     addClient( c, Allowed );
00423     return c;
00424     }
00425 
00426 void Workspace::addClient( Client* c, allowed_t )
00427     {
00428     Group* grp = findGroup( c->window());
00429     if( grp != NULL )
00430         grp->gotLeader( c );
00431 
00432     if ( c->isDesktop() )
00433         {
00434         desktops.append( c );
00435         if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
00436             requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active
00437         }
00438     else
00439         {
00440         if ( c->wantsTabFocus() && !focus_chain.contains( c ))
00441             focus_chain.append( c );
00442         clients.append( c );
00443         }
00444     if( !unconstrained_stacking_order.contains( c ))
00445         unconstrained_stacking_order.append( c );
00446     if( c->isTopMenu())
00447         addTopMenu( c );
00448     updateClientArea(); // this cannot be in manage(), because the client got added only now
00449     updateClientLayer( c );
00450     if( c->isDesktop())
00451         {
00452         raiseClient( c );
00453     // if there's no active client, make this desktop the active one
00454         if( activeClient() == NULL && should_get_focus.count() == 0 )
00455             activateClient( findDesktop( true, currentDesktop()));
00456         }
00457     if( c->isUtility() || c->isMenu() || c->isToolbar())
00458         updateToolWindows( true );
00459     checkTransients( c->window()); // SELI does this really belong here?
00460     updateStackingOrder( true ); // propagate new client
00461     }
00462 
00463 /*
00464   Destroys the client \a c
00465  */
00466 void Workspace::removeClient( Client* c, allowed_t )
00467     {
00468     if (c == active_client && popup)
00469         popup->close();
00470     if( c == popup_client )
00471         popup_client = 0;
00472 
00473     if( c->isDialog())
00474         Notify::raise( Notify::TransDelete );
00475     if( c->isNormalWindow())
00476         Notify::raise( Notify::Delete );
00477 
00478     storeFakeSessionInfo( c );
00479 
00480     Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
00481     clients.remove( c );
00482     desktops.remove( c );
00483     unconstrained_stacking_order.remove( c );
00484     stacking_order.remove( c );
00485     focus_chain.remove( c );
00486     attention_chain.remove( c );
00487     if( c->isTopMenu())
00488         removeTopMenu( c );
00489     Group* group = findGroup( c->window());
00490     if( group != NULL )
00491         group->lostLeader();
00492 
00493     if ( c == most_recently_raised )
00494         most_recently_raised = 0;
00495     should_get_focus.remove( c );
00496     Q_ASSERT( c != active_client );
00497     if ( c == last_active_client )
00498         last_active_client = 0;
00499 
00500     updateStackingOrder( true );
00501 
00502     if (tab_grab)
00503        tab_box->repaint();
00504 
00505     updateClientArea();
00506     }
00507 
00508 void Workspace::updateCurrentTopMenu()
00509     {
00510     if( !managingTopMenus())
00511         return;
00512     // toplevel menubar handling
00513     Client* menubar = 0;
00514     bool block_desktop_menubar = false;
00515     if( active_client )
00516         {
00517         // show the new menu bar first...
00518         Client* menu_client = active_client;
00519         for(;;)
00520             {
00521             if( menu_client->isFullScreen())
00522                 block_desktop_menubar = true;
00523             for( ClientList::ConstIterator it = menu_client->transients().begin();
00524                  it != menu_client->transients().end();
00525                  ++it )
00526                 if( (*it)->isTopMenu())
00527                     {
00528                     menubar = *it;
00529                     break;
00530                     }
00531             if( menubar != NULL || !menu_client->isTransient())
00532                 break;
00533             if( menu_client->isModal() || menu_client->transientFor() == NULL )
00534                 break; // don't use mainwindow's menu if this is modal or group transient
00535             menu_client = menu_client->transientFor();
00536             }
00537         if( !menubar )
00538             { // try to find any topmenu from the application (#72113)
00539             for( ClientList::ConstIterator it = active_client->group()->members().begin();
00540                  it != active_client->group()->members().end();
00541                  ++it )
00542                 if( (*it)->isTopMenu())
00543                     {
00544                     menubar = *it;
00545                     break;
00546                     }
00547             }
00548         }
00549     if( !menubar && !block_desktop_menubar && options->desktopTopMenu())
00550         {
00551         // Find the menubar of the desktop
00552         Client* desktop = findDesktop( true, currentDesktop());
00553         if( desktop != NULL )
00554             {
00555             for( ClientList::ConstIterator it = desktop->transients().begin();
00556                  it != desktop->transients().end();
00557                  ++it )
00558                 if( (*it)->isTopMenu())
00559                     {
00560                     menubar = *it;
00561                     break;
00562                     }
00563             }
00564         // TODO to be cleaned app with window grouping
00565         // Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
00566         // thus the topmenu is not transient for it :-/.
00567         if( menubar == NULL )
00568             {
00569             for( ClientList::ConstIterator it = topmenus.begin();
00570                  it != topmenus.end();
00571                  ++it )
00572                 if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR
00573                     {                                     // set pointing to the root window
00574                     menubar = *it;                        // to recognize it here
00575                     break;                                // Also, with the xroot hack in kdesktop,
00576                     }                                     // there's no NET::Desktop window to be transient for
00577             }
00578         }
00579 
00580 //    kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl;
00581     if ( menubar )
00582         {
00583         if( active_client && !menubar->isOnDesktop( active_client->desktop()))
00584             menubar->setDesktop( active_client->desktop());
00585         menubar->hideClient( false );
00586         topmenu_space->hide();
00587         // make it appear like it's been raised manually - it's in the Dock layer anyway,
00588         // and not raising it could mess up stacking order of topmenus within one application,
00589         // and thus break raising of mainclients in raiseClient()
00590         unconstrained_stacking_order.remove( menubar );
00591         unconstrained_stacking_order.append( menubar );
00592         }
00593     else if( !block_desktop_menubar )
00594         { // no topmenu active - show the space window, so that there's not empty space
00595         topmenu_space->show();
00596         }
00597 
00598     // ... then hide the other ones. Avoids flickers.
00599     for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 
00600         {
00601         if( (*it)->isTopMenu() && (*it) != menubar )
00602             (*it)->hideClient( true );
00603         }
00604     }
00605 
00606 
00607 void Workspace::updateToolWindows( bool also_hide )
00608     {
00609     // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
00610     const Group* group = NULL;
00611     const Client* client = active_client;
00612 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
00613 // will be shown; if a group transient is group, all tools in the group will be shown
00614     while( client != NULL )
00615         {
00616         if( !client->isTransient())
00617             break;
00618         if( client->groupTransient())
00619             {
00620             group = client->group();
00621             break;
00622             }
00623         client = client->transientFor();
00624         }
00625     // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
00626     // i.e. if it's not up to date
00627 
00628     // SELI but maybe it should - what if a new client has been added that's not in stacking order yet?
00629     ClientList to_show, to_hide;
00630     for( ClientList::ConstIterator it = stacking_order.begin();
00631          it != stacking_order.end();
00632          ++it )
00633         {
00634         if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar())
00635             {
00636             bool show = true;
00637             if( !(*it)->isTransient())
00638                 {
00639                 if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible
00640                     show = true;
00641                 else if( client != NULL && (*it)->group() == client->group())
00642                     show = true;
00643                 else
00644                     show = false;
00645                 }
00646             else
00647                 {
00648                 if( group != NULL && (*it)->group() == group )
00649                     show = true;
00650                 else if( client != NULL && client->hasTransient( (*it), true ))
00651                     show = true;
00652                 else
00653                     show = false;
00654                 }
00655             if( show )
00656                 to_show.append( *it );
00657             else if( also_hide )
00658                 to_hide.append( *it );
00659             }
00660         } // first show new ones, then hide
00661     for( ClientList::ConstIterator it = to_show.fromLast();
00662          it != to_show.end();
00663          --it ) // from topmost
00664         // TODO since this is in stacking order, the order of taskbar entries changes :(
00665         (*it)->hideClient( false );
00666     if( also_hide )
00667         {
00668         for( ClientList::ConstIterator it = to_hide.begin();
00669              it != to_hide.end();
00670              ++it ) // from bottommost
00671             (*it)->hideClient( true );
00672         updateToolWindowsTimer.stop();
00673         }
00674     else // setActiveClient() is after called with NULL client, quickly followed
00675         {    // by setting a new client, which would result in flickering
00676         updateToolWindowsTimer.start( 50, true );
00677         }
00678     }
00679 
00680 void Workspace::slotUpdateToolWindows()
00681     {
00682     updateToolWindows( true );
00683     }
00684 
00688 void Workspace::updateColormap()
00689     {
00690     Colormap cmap = default_colormap;
00691     if ( activeClient() && activeClient()->colormap() != None )
00692         cmap = activeClient()->colormap();
00693     if ( cmap != installed_colormap ) 
00694         {
00695         XInstallColormap(qt_xdisplay(), cmap );
00696         installed_colormap = cmap;
00697         }
00698     }
00699 
00700 void Workspace::reconfigure()
00701     {
00702     reconfigureTimer.start(200, true);
00703     }
00704 
00705 
00706 void Workspace::slotSettingsChanged(int category)
00707     {
00708     kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl;
00709     if( category == (int) KApplication::SETTINGS_SHORTCUTS )
00710         readShortcuts();
00711     }
00712 
00716 KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() );
00717 
00718 void Workspace::slotReconfigure()
00719     {
00720     kdDebug(1212) << "Workspace::slotReconfigure()" << endl;
00721     reconfigureTimer.stop();
00722 
00723     KGlobal::config()->reparseConfiguration();
00724     unsigned long changed = options->updateSettings();
00725     tab_box->reconfigure();
00726     popupinfo->reconfigure();
00727     readShortcuts();
00728     forEachClient( CheckIgnoreFocusStealingProcedure());
00729 
00730     if( mgr->reset( changed ))
00731         { // decorations need to be recreated
00732 #if 0 // This actually seems to make things worse now
00733         QWidget curtain;
00734         curtain.setBackgroundMode( NoBackground );
00735         curtain.setGeometry( QApplication::desktop()->geometry() );
00736         curtain.show();
00737 #endif
00738         for( ClientList::ConstIterator it = clients.begin();
00739                 it != clients.end();
00740                 ++it )
00741             {
00742             (*it)->updateDecoration( true, true );
00743             }
00744         mgr->destroyPreviousPlugin();
00745         }
00746     else
00747         {
00748         forEachClient( CheckBorderSizesProcedure());
00749         }
00750 
00751     if (options->electricBorders() == Options::ElectricAlways)
00752        createBorderWindows();
00753     else
00754        destroyBorderWindows();
00755 
00756     if( options->topMenuEnabled() && !managingTopMenus())
00757         {
00758         if( topmenu_selection->claim( false ))
00759             setupTopMenuHandling();
00760         else
00761             lostTopMenuSelection();
00762         }
00763     else if( !options->topMenuEnabled() && managingTopMenus())
00764         {
00765         topmenu_selection->release();
00766         lostTopMenuSelection();
00767         }
00768     topmenu_height = 0; // invalidate used menu height
00769     if( managingTopMenus())
00770         {
00771         updateTopMenuGeometry();
00772         updateCurrentTopMenu();
00773         }
00774     }
00775 
00776 void Workspace::loadDesktopSettings()
00777     {
00778     KConfig c("kwinrc");
00779 
00780     QCString groupname;
00781     if (screen_number == 0)
00782         groupname = "Desktops";
00783     else
00784         groupname.sprintf("Desktops-screen-%d", screen_number);
00785     c.setGroup(groupname);
00786 
00787     int n = c.readNumEntry("Number", 4);
00788     number_of_desktops = n;
00789     delete workarea;
00790     workarea = new QRect[ n + 1 ];
00791     rootInfo->setNumberOfDesktops( number_of_desktops );
00792     desktop_focus_chain.resize( n );
00793     for(int i = 1; i <= n; i++) 
00794         {
00795         QString s = c.readEntry(QString("Name_%1").arg(i),
00796                                 i18n("Desktop %1").arg(i));
00797         rootInfo->setDesktopName( i, s.utf8().data() );
00798         desktop_focus_chain[i-1] = i;
00799         }
00800     }
00801 
00802 void Workspace::saveDesktopSettings()
00803     {
00804     KConfig c("kwinrc");
00805 
00806     QCString groupname;
00807     if (screen_number == 0)
00808         groupname = "Desktops";
00809     else
00810         groupname.sprintf("Desktops-screen-%d", screen_number);
00811     c.setGroup(groupname);
00812 
00813     c.writeEntry("Number", number_of_desktops );
00814     for(int i = 1; i <= number_of_desktops; i++) 
00815         {
00816         QString s = desktopName( i );
00817         QString defaultvalue = i18n("Desktop %1").arg(i);
00818         if ( s.isEmpty() ) 
00819             {
00820             s = defaultvalue;
00821             rootInfo->setDesktopName( i, s.utf8().data() );
00822             }
00823 
00824         if (s != defaultvalue) 
00825             {
00826             c.writeEntry( QString("Name_%1").arg(i), s );
00827             }
00828         else 
00829             {
00830             QString currentvalue = c.readEntry(QString("Name_%1").arg(i));
00831             if (currentvalue != defaultvalue)
00832                 c.writeEntry( QString("Name_%1").arg(i), "" );
00833             }
00834         }
00835     }
00836 
00837 QStringList Workspace::configModules(bool controlCenter)
00838     {
00839     QStringList args;
00840     args <<  "kde-kwindecoration.desktop";
00841     if (controlCenter)
00842         args << "kde-kwinoptions.desktop";
00843     else if (kapp->authorizeControlModule("kde-kwinoptions.desktop"))
00844         args  << "kwinactions" << "kwinfocus" <<  "kwinmoving" << "kwinadvanced";
00845     return args;
00846     }
00847 
00848 void Workspace::configureWM()
00849     {
00850     KApplication::kdeinitExec( "kcmshell", configModules(false) );
00851     }
00852 
00856 void Workspace::doNotManage( QString title )
00857     {
00858     doNotManageList.append( title );
00859     }
00860 
00864 bool Workspace::isNotManaged( const QString& title )
00865     {
00866     for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) 
00867         {
00868         QRegExp r( (*it) );
00869         if (r.search(title) != -1) 
00870             {
00871             doNotManageList.remove( it );
00872             return TRUE;
00873             }
00874         }
00875     return FALSE;
00876     }
00877 
00881 void Workspace::refresh() 
00882     {
00883     QWidget w;
00884     w.setGeometry( QApplication::desktop()->geometry() );
00885     w.show();
00886     w.hide();
00887     QApplication::flushX();
00888     }
00889 
00897 class ObscuringWindows
00898     {
00899     public:
00900         ~ObscuringWindows();
00901         void create( Client* c );
00902     private:
00903         QValueList<Window> obscuring_windows;
00904         static QValueList<Window>* cached;
00905         static unsigned int max_cache_size;
00906     };
00907 
00908 QValueList<Window>* ObscuringWindows::cached = 0;
00909 unsigned int ObscuringWindows::max_cache_size = 0;
00910 
00911 void ObscuringWindows::create( Client* c )
00912     {
00913     if( cached == 0 )
00914         cached = new QValueList<Window>;
00915     Window obs_win;
00916     XWindowChanges chngs;
00917     int mask = CWSibling | CWStackMode;
00918     if( cached->count() > 0 ) 
00919         {
00920         cached->remove( obs_win = cached->first());
00921         chngs.x = c->x();
00922         chngs.y = c->y();
00923         chngs.width = c->width();
00924         chngs.height = c->height();
00925         mask |= CWX | CWY | CWWidth | CWHeight;
00926         }
00927     else 
00928         {
00929         XSetWindowAttributes a;
00930         a.background_pixmap = None;
00931         a.override_redirect = True;
00932         obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(),
00933             c->width(), c->height(), 0, CopyFromParent, InputOutput,
00934             CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a );
00935         }
00936     chngs.sibling = c->frameId();
00937     chngs.stack_mode = Below;
00938     XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs );
00939     XMapWindow( qt_xdisplay(), obs_win );
00940     obscuring_windows.append( obs_win );
00941     }
00942 
00943 ObscuringWindows::~ObscuringWindows()
00944     {
00945     max_cache_size = QMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1;
00946     for( QValueList<Window>::ConstIterator it = obscuring_windows.begin();
00947          it != obscuring_windows.end();
00948          ++it ) 
00949         {
00950         XUnmapWindow( qt_xdisplay(), *it );
00951         if( cached->count() < max_cache_size )
00952             cached->prepend( *it );
00953         else
00954             XDestroyWindow( qt_xdisplay(), *it );
00955         }
00956     }
00957 
00958 
00965 bool Workspace::setCurrentDesktop( int new_desktop )
00966     {
00967     if (new_desktop < 1 || new_desktop > number_of_desktops )
00968         return false;
00969 
00970     if( popup )
00971         popup->close();
00972     ++block_focus;
00973 // TODO    Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date
00974     StackingUpdatesBlocker blocker( this );
00975 
00976     if (new_desktop != current_desktop) 
00977         {
00978         /*
00979           optimized Desktop switching: unmapping done from back to front
00980           mapping done from front to back => less exposure events
00981         */
00982         Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
00983 
00984         ObscuringWindows obs_wins;
00985 
00986         int old_desktop = current_desktop;
00987         current_desktop = new_desktop; // change the desktop (so that Client::virtualDesktopChange() works)
00988 
00989         for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
00990             if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
00991                 {
00992                 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop ))
00993                     obs_wins.create( *it );
00994                 (*it)->virtualDesktopChange();
00995                 }
00996 
00997         rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing
00998 
00999         if( movingClient && !movingClient->isOnDesktop( new_desktop ))
01000             movingClient->setDesktop( new_desktop );
01001 
01002         for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it)
01003             if ( (*it)->isOnDesktop( new_desktop ) )
01004                 (*it)->virtualDesktopChange();
01005         }
01006 
01007     // restore the focus on this desktop
01008     --block_focus;
01009     Client* c = 0;
01010 
01011     if ( options->focusPolicyIsReasonable()) 
01012         {
01013         // Search in focus chain
01014 
01015         if ( focus_chain.contains( active_client ) && active_client->isShown( true )
01016             && active_client->isOnCurrentDesktop())
01017             {
01018             c = active_client; // the requestFocus below will fail, as the client is already active
01019             }
01020 
01021         if ( !c ) 
01022             {
01023             for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) 
01024                 {
01025                 if ( (*it)->isShown( false ) && !(*it)->isOnAllDesktops() && (*it)->isOnCurrentDesktop()) 
01026                     {
01027                     c = *it;
01028                     break;
01029                     }
01030                 }
01031             }
01032 
01033         if ( !c ) 
01034             {
01035             for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) 
01036                 {
01037                 if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop()) 
01038                     {
01039                     c = *it;
01040                     break;
01041                     }
01042                 }
01043             }
01044         }
01045 
01046     //if "unreasonable focus policy"
01047     // and active_client is on_all_desktops and under mouse (hence == old_active_client),
01048     // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
01049     else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
01050       c= active_client;
01051 
01052     if( c != active_client )
01053         setActiveClient( NULL, Allowed );
01054 
01055     if ( c ) 
01056         requestFocus( c );
01057     else 
01058         focusToNull();
01059 
01060     if( !desktops.isEmpty() ) 
01061         {
01062         Window w_tmp;
01063         int i_tmp;
01064         XGetInputFocus( qt_xdisplay(), &w_tmp, &i_tmp );
01065         if( w_tmp == null_focus_window ) // CHECKME?
01066             requestFocus( findDesktop( true, currentDesktop()));
01067         }
01068 
01069     // Update focus chain:
01070     //  If input: chain = { 1, 2, 3, 4 } and current_desktop = 3,
01071     //   Output: chain = { 3, 1, 2, 4 }.
01072 //    kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n")
01073 //      .arg(current_desktop).arg(desktop_focus_chain.find( current_desktop ));
01074     for( int i = desktop_focus_chain.find( current_desktop ); i > 0; i-- )
01075         desktop_focus_chain[i] = desktop_focus_chain[i-1];
01076     desktop_focus_chain[0] = current_desktop;
01077 
01078 //    QString s = "desktop_focus_chain[] = { ";
01079 //    for( uint i = 0; i < desktop_focus_chain.size(); i++ )
01080 //        s += QString::number(desktop_focus_chain[i]) + ", ";
01081 //    kdDebug(1212) << s << "}\n";
01082     return true;
01083     }
01084 
01085 void Workspace::nextDesktop()
01086     {
01087     int desktop = currentDesktop() + 1;
01088     setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
01089     popupinfo->showInfo( desktopName(currentDesktop()) );
01090     }
01091 
01092 void Workspace::previousDesktop()
01093     {
01094     int desktop = currentDesktop() - 1;
01095     setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
01096     popupinfo->showInfo( desktopName(currentDesktop()) );
01097     }
01098 
01102 void Workspace::setNumberOfDesktops( int n )
01103     {
01104     if ( n == number_of_desktops )
01105         return;
01106     int old_number_of_desktops = number_of_desktops;
01107     number_of_desktops = n;
01108 
01109     if( currentDesktop() > numberOfDesktops())
01110         setCurrentDesktop( numberOfDesktops());
01111 
01112     // if increasing the number, do the resizing now,
01113     // otherwise after the moving of windows to still existing desktops
01114     if( old_number_of_desktops < number_of_desktops ) 
01115         {
01116         rootInfo->setNumberOfDesktops( number_of_desktops );
01117         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01118         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01119         delete[] viewports;
01120         updateClientArea( true );
01121         }
01122 
01123     // if the number of desktops decreased, move all
01124     // windows that would be hidden to the last visible desktop
01125     if( old_number_of_desktops > number_of_desktops ) 
01126         {
01127         for( ClientList::ConstIterator it = clients.begin();
01128               it != clients.end();
01129               ++it) 
01130             {
01131             if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops())
01132                 sendClientToDesktop( *it, numberOfDesktops(), true );
01133             }
01134         }
01135     if( old_number_of_desktops > number_of_desktops ) 
01136         {
01137         rootInfo->setNumberOfDesktops( number_of_desktops );
01138         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01139         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01140         delete[] viewports;
01141         updateClientArea( true );
01142         }
01143 
01144     saveDesktopSettings();
01145 
01146     // Resize and reset the desktop focus chain.
01147     desktop_focus_chain.resize( n );
01148     for( int i = 0; i < (int)desktop_focus_chain.size(); i++ )
01149         desktop_focus_chain[i] = i+1;
01150     }
01151 
01157 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
01158     {
01159     if ( c->desktop() == desk )
01160         return;
01161 
01162     bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops();
01163     c->setDesktop( desk );
01164     desk = c->desktop(); // Client did range checking
01165 
01166     if ( c->isOnDesktop( currentDesktop() ) )
01167         {
01168         if ( c->wantsTabFocus() && options->focusPolicyIsReasonable()
01169             && !was_on_desktop // for stickyness changes
01170             && !dont_activate )
01171             requestFocus( c );
01172         else
01173             restackClientUnderActive( c );
01174         }
01175     else 
01176         {
01177         raiseClient( c );
01178         focus_chain.remove( c );
01179         if ( c->wantsTabFocus() )
01180             focus_chain.append( c );
01181         }
01182 
01183     ClientList transients_stacking_order = ensureStackingOrder( c->transients());
01184     for( ClientList::ConstIterator it = transients_stacking_order.begin();
01185          it != transients_stacking_order.end();
01186          ++it )
01187         sendClientToDesktop( *it, desk, dont_activate );
01188     updateClientArea();
01189     }
01190 
01191 void Workspace::setDesktopLayout(int o, int x, int y)
01192     {
01193     layoutOrientation = (Qt::Orientation) o;
01194     layoutX = x;
01195     layoutY = y;
01196     }
01197 
01198 void Workspace::calcDesktopLayout(int &x, int &y)
01199     {
01200     x = layoutX;
01201     y = layoutY;
01202     if ((x == -1) && (y > 0))
01203        x = (numberOfDesktops()+y-1) / y;
01204     else if ((y == -1) && (x > 0))
01205        y = (numberOfDesktops()+x-1) / x;
01206 
01207     if (x == -1)
01208        x = 1;
01209     if (y == -1)
01210        y = 1;
01211     }
01212 
01217 bool Workspace::addSystemTrayWin( WId w )
01218     {
01219     if ( systemTrayWins.contains( w ) )
01220         return TRUE;
01221 
01222     NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor );
01223     WId trayWinFor = ni.kdeSystemTrayWinFor();
01224     if ( !trayWinFor )
01225         return FALSE;
01226     systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) );
01227     XSelectInput( qt_xdisplay(), w,
01228                   StructureNotifyMask
01229                   );
01230     XAddToSaveSet( qt_xdisplay(), w );
01231     propagateSystemTrayWins();
01232     return TRUE;
01233     }
01234 
01239 bool Workspace::removeSystemTrayWin( WId w, bool check )
01240     {
01241     if ( !systemTrayWins.contains( w ) )
01242         return FALSE;
01243     if( check )
01244         {
01245     // When getting UnmapNotify, it's not clear if it's the systray
01246     // reparenting the window into itself, or if it's the window
01247     // going away. This is obviously a flaw in the design, and we were
01248     // just lucky it worked for so long. Kicker's systray temporarily
01249     // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while
01250     // embedding it, allowing KWin to figure out. Kicker just mustn't
01251     // crash before removing it again ... *shrug* .
01252         int num_props;
01253         Atom* props = XListProperties( qt_xdisplay(), w, &num_props );
01254         if( props != NULL )
01255             {
01256             for( int i = 0;
01257                  i < num_props;
01258                  ++i )
01259                 if( props[ i ] == atoms->kde_system_tray_embedding )
01260                     {
01261                     XFree( props );
01262                     return false;
01263                     }
01264             XFree( props );
01265             }
01266         }
01267     systemTrayWins.remove( w );
01268     propagateSystemTrayWins();
01269     return TRUE;
01270     }
01271 
01272 
01276 void Workspace::propagateSystemTrayWins()
01277     {
01278     Window *cl = new Window[ systemTrayWins.count()];
01279 
01280     int i = 0;
01281     for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) 
01282         {
01283         cl[i++] =  (*it).win;
01284         }
01285 
01286     rootInfo->setKDESystemTrayWindows( cl, i );
01287     delete [] cl;
01288     }
01289 
01290 
01291 void Workspace::killWindowId( Window window_to_kill )
01292     {
01293     if( window_to_kill == None )
01294         return;
01295     Window window = window_to_kill;
01296     Client* client = NULL;
01297     for(;;) 
01298         {
01299         client = findClient( FrameIdMatchPredicate( window ));
01300         if( client != NULL ) // found the client
01301             break;
01302         Window parent, root;
01303         Window* children;
01304         unsigned int children_count;
01305         XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count );
01306         if( children != NULL )
01307             XFree( children );
01308         if( window == root ) // we didn't find the client, probably an override-redirect window
01309             break;
01310         window = parent; // go up
01311         }
01312     if( client != NULL )
01313         client->killWindow();
01314     else
01315         XKillClient( qt_xdisplay(), window_to_kill );
01316     }
01317 
01318 
01319 void Workspace::sendPingToWindow( Window window, Time timestamp )
01320     {
01321     rootInfo->sendPing( window, timestamp );
01322     }
01323 
01324 
01328 void Workspace::slotGrabWindow()
01329     {
01330     if ( active_client ) 
01331         {
01332         QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() );
01333 
01334     //No XShape - no work.
01335         if( Shape::available()) 
01336             {
01337         //As the first step, get the mask from XShape.
01338             int count, order;
01339             XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(),
01340                                                      ShapeBounding, &count, &order);
01341         //The ShapeBounding region is the outermost shape of the window;
01342         //ShapeBounding - ShapeClipping is defined to be the border.
01343         //Since the border area is part of the window, we use bounding
01344         // to limit our work region
01345             if (rects) 
01346                 {
01347         //Create a QRegion from the rectangles describing the bounding mask.
01348                 QRegion contents;
01349                 for (int pos = 0; pos < count; pos++)
01350                     contents += QRegion(rects[pos].x, rects[pos].y,
01351                                         rects[pos].width, rects[pos].height);
01352                 XFree(rects);
01353 
01354         //Create the bounding box.
01355                 QRegion bbox(0, 0, snapshot.width(), snapshot.height());
01356 
01357         //Get the masked away area.
01358                 QRegion maskedAway = bbox - contents;
01359                 QMemArray<QRect> maskedAwayRects = maskedAway.rects();
01360 
01361         //Construct a bitmap mask from the rectangles
01362                 QBitmap mask( snapshot.width(), snapshot.height());
01363                 QPainter p(&mask);
01364                 p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1);
01365                 for (uint pos = 0; pos < maskedAwayRects.count(); pos++)
01366                     p.fillRect(maskedAwayRects[pos], Qt::color0);
01367                 p.end();
01368                 snapshot.setMask(mask);
01369                 }
01370             }
01371 
01372         QClipboard *cb = QApplication::clipboard();
01373         cb->setPixmap( snapshot );
01374         }
01375     else
01376         slotGrabDesktop();
01377     }
01378 
01382 void Workspace::slotGrabDesktop()
01383     {
01384     QPixmap p = QPixmap::grabWindow( qt_xrootwin() );
01385     QClipboard *cb = QApplication::clipboard();
01386     cb->setPixmap( p );
01387     }
01388 
01389 
01393 void Workspace::slotMouseEmulation()
01394     {
01395 
01396     if ( mouse_emulation ) 
01397         {
01398         XUngrabKeyboard(qt_xdisplay(), qt_x_time);
01399         mouse_emulation = FALSE;
01400         return;
01401         }
01402 
01403     if ( XGrabKeyboard(qt_xdisplay(),
01404                        root, FALSE,
01405                        GrabModeAsync, GrabModeAsync,
01406                        qt_x_time) == GrabSuccess ) 
01407         {
01408         mouse_emulation = TRUE;
01409         mouse_emulation_state = 0;
01410         mouse_emulation_window = 0;
01411         }
01412     }
01413 
01420 WId Workspace::getMouseEmulationWindow()
01421     {
01422     Window root;
01423     Window child = qt_xrootwin();
01424     int root_x, root_y, lx, ly;
01425     uint state;
01426     Window w;
01427     Client * c = 0;
01428     do 
01429         {
01430         w = child;
01431         if (!c)
01432             c = findClient( FrameIdMatchPredicate( w ));
01433         XQueryPointer( qt_xdisplay(), w, &root, &child,
01434                        &root_x, &root_y, &lx, &ly, &state );
01435         } while  ( child != None && child != w );
01436 
01437     if ( c && !c->isActive() )
01438         activateClient( c );
01439     return (WId) w;
01440     }
01441 
01445 unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation type, int button, unsigned int state )
01446     {
01447     if ( !w )
01448         return state;
01449     QWidget* widget = QWidget::find( w );
01450     if ( (!widget ||  widget->inherits("QToolButton") ) && !findClient( WindowMatchPredicate( w )) ) 
01451         {
01452         int x, y;
01453         Window xw;
01454         XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw );
01455         if ( type == EmuMove ) 
01456             { // motion notify events
01457             XMotionEvent e;
01458             e.type = MotionNotify;
01459             e.window = w;
01460             e.root = qt_xrootwin();
01461             e.subwindow = w;
01462             e.time = qt_x_time;
01463             e.x = x;
01464             e.y = y;
01465             e.x_root = pos.x();
01466             e.y_root = pos.y();
01467             e.state = state;
01468             e.is_hint = NotifyNormal;
01469             XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, (XEvent*)&e );
01470             }
01471         else 
01472             {
01473             XButtonEvent e;
01474             e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
01475             e.window = w;
01476             e.root = qt_xrootwin();
01477             e.subwindow = w;
01478             e.time = qt_x_time;
01479             e.x = x;
01480             e.y = y;
01481             e.x_root = pos.x();
01482             e.y_root = pos.y();
01483             e.state = state;
01484             e.button = button;
01485             XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, (XEvent*)&e );
01486 
01487             if ( type == EmuPress ) 
01488                 {
01489                 switch ( button ) 
01490                     {
01491                     case 2:
01492                         state |= Button2Mask;
01493                         break;
01494                     case 3:
01495                         state |= Button3Mask;
01496                         break;
01497                     default: // 1
01498                         state |= Button1Mask;
01499                         break;
01500                     }
01501                 }
01502             else 
01503                 {
01504                 switch ( button ) 
01505                     {
01506                     case 2:
01507                         state &= ~Button2Mask;
01508                         break;
01509                     case 3:
01510                         state &= ~Button3Mask;
01511                         break;
01512                     default: // 1
01513                         state &= ~Button1Mask;
01514                         break;
01515                     }
01516                 }
01517             }
01518         }
01519     return state;
01520     }
01521 
01525 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev )
01526     {
01527     if ( root != qt_xrootwin() )
01528         return FALSE;
01529     int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0);
01530     int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
01531 
01532     bool is_control = km & ControlMask;
01533     bool is_alt = km & Mod1Mask;
01534     bool is_shift = km & ShiftMask;
01535     int delta = is_control?1:is_alt?32:8;
01536     QPoint pos = QCursor::pos();
01537 
01538     switch ( kc ) 
01539         {
01540         case XK_Left:
01541         case XK_KP_Left:
01542             pos.rx() -= delta;
01543             break;
01544         case XK_Right:
01545         case XK_KP_Right:
01546             pos.rx() += delta;
01547             break;
01548         case XK_Up:
01549         case XK_KP_Up:
01550             pos.ry() -= delta;
01551             break;
01552         case XK_Down:
01553         case XK_KP_Down:
01554             pos.ry() += delta;
01555             break;
01556         case XK_F1:
01557             if ( !mouse_emulation_state )
01558                 mouse_emulation_window = getMouseEmulationWindow();
01559             if ( (mouse_emulation_state & Button1Mask) == 0 )
01560                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
01561             if ( !is_shift )
01562                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01563             break;
01564         case XK_F2:
01565             if ( !mouse_emulation_state )
01566                 mouse_emulation_window = getMouseEmulationWindow();
01567             if ( (mouse_emulation_state & Button2Mask) == 0 )
01568                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state );
01569             if ( !is_shift )
01570                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
01571             break;
01572         case XK_F3:
01573             if ( !mouse_emulation_state )
01574                 mouse_emulation_window = getMouseEmulationWindow();
01575             if ( (mouse_emulation_state & Button3Mask) == 0 )
01576                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state );
01577             if ( !is_shift )
01578                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
01579             break;
01580         case XK_Return:
01581         case XK_space:
01582         case XK_KP_Enter:
01583         case XK_KP_Space: 
01584             {
01585             if ( !mouse_emulation_state ) 
01586                 {
01587             // nothing was pressed, fake a LMB click
01588                 mouse_emulation_window = getMouseEmulationWindow();
01589                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
01590                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01591                 }
01592             else 
01593                 { // release all
01594                 if ( mouse_emulation_state & Button1Mask )
01595                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01596                 if ( mouse_emulation_state & Button2Mask )
01597                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
01598                 if ( mouse_emulation_state & Button3Mask )
01599                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
01600                 }
01601             }
01602     // fall through
01603         case XK_Escape:
01604             XUngrabKeyboard(qt_xdisplay(), qt_x_time);
01605             mouse_emulation = FALSE;
01606             return TRUE;
01607         default:
01608             return FALSE;
01609         }
01610 
01611     QCursor::setPos( pos );
01612     if ( mouse_emulation_state )
01613         mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0,  mouse_emulation_state );
01614     return TRUE;
01615 
01616     }
01617 
01623 QWidget* Workspace::desktopWidget()
01624     {
01625     return desktop_widget;
01626     }
01627 
01628 
01629 
01630 // Electric Borders
01631 //========================================================================//
01632 // Electric Border Window management. Electric borders allow a user
01633 // to change the virtual desktop by moving the mouse pointer to the
01634 // borders. Technically this is done with input only windows. Since
01635 // electric borders can be switched on and off, we have these two
01636 // functions to create and destroy them.
01637 void Workspace::createBorderWindows()
01638     {
01639     if ( electric_have_borders )
01640         return;
01641 
01642     electric_have_borders = true;
01643     electric_current_border = 0;
01644 
01645     QRect r = QApplication::desktop()->geometry();
01646     electricTop = r.top();
01647     electricBottom = r.bottom();
01648     electricLeft = r.left();
01649     electricRight = r.right();
01650 
01651     XSetWindowAttributes attributes;
01652     unsigned long valuemask;
01653     attributes.override_redirect = True;
01654     attributes.event_mask =  (EnterWindowMask | LeaveWindowMask |
01655                               VisibilityChangeMask);
01656     valuemask=  (CWOverrideRedirect | CWEventMask | CWCursor );
01657     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01658                                           XC_sb_up_arrow);
01659     electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01660                                 0,0,
01661                                 r.width(),1,
01662                                 0,
01663                                 CopyFromParent, InputOnly,
01664                                 CopyFromParent,
01665                                 valuemask, &attributes);
01666     XMapWindow(qt_xdisplay(), electric_top_border);
01667 
01668     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01669                                           XC_sb_down_arrow);
01670     electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01671                                    0,r.height()-1,
01672                                    r.width(),1,
01673                                    0,
01674                                    CopyFromParent, InputOnly,
01675                                    CopyFromParent,
01676                                    valuemask, &attributes);
01677     XMapWindow(qt_xdisplay(), electric_bottom_border);
01678 
01679     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01680                                           XC_sb_left_arrow);
01681     electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01682                                  0,0,
01683                                  1,r.height(),
01684                                  0,
01685                                  CopyFromParent, InputOnly,
01686                                  CopyFromParent,
01687                                  valuemask, &attributes);
01688     XMapWindow(qt_xdisplay(), electric_left_border);
01689 
01690     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01691                                           XC_sb_right_arrow);
01692     electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01693                                   r.width()-1,0,
01694                                   1,r.height(),
01695                                   0,
01696                                   CopyFromParent, InputOnly,
01697                                   CopyFromParent,
01698                                   valuemask, &attributes);
01699     XMapWindow(qt_xdisplay(),  electric_right_border);
01700     }
01701 
01702 
01703 // Electric Border Window management. Electric borders allow a user
01704 // to change the virtual desktop by moving the mouse pointer to the
01705 // borders. Technically this is done with input only windows. Since
01706 // electric borders can be switched on and off, we have these two
01707 // functions to create and destroy them.
01708 void Workspace::destroyBorderWindows()
01709     {
01710     if( !electric_have_borders)
01711       return;
01712 
01713     electric_have_borders = false;
01714 
01715     if(electric_top_border)
01716       XDestroyWindow(qt_xdisplay(),electric_top_border);
01717     if(electric_bottom_border)
01718       XDestroyWindow(qt_xdisplay(),electric_bottom_border);
01719     if(electric_left_border)
01720       XDestroyWindow(qt_xdisplay(),electric_left_border);
01721     if(electric_right_border)
01722       XDestroyWindow(qt_xdisplay(),electric_right_border);
01723 
01724     electric_top_border    = None;
01725     electric_bottom_border = None;
01726     electric_left_border   = None;
01727     electric_right_border  = None;
01728     }
01729 
01730 void Workspace::clientMoved(const QPoint &pos, Time now)
01731     {
01732     if (options->electricBorders() == Options::ElectricDisabled)
01733        return;
01734 
01735     if ((pos.x() != electricLeft) &&
01736         (pos.x() != electricRight) &&
01737         (pos.y() != electricTop) &&
01738         (pos.y() != electricBottom))
01739        return;
01740 
01741     Time treshold_set = options->electricBorderDelay(); // set timeout
01742     Time treshold_reset = 250; // reset timeout
01743     int distance_reset = 10; // Mouse should not move more than this many pixels
01744 
01745     int border = 0;
01746     if (pos.x() == electricLeft)
01747        border = 1;
01748     else if (pos.x() == electricRight)
01749        border = 2;
01750     else if (pos.y() == electricTop)
01751        border = 3;
01752     else if (pos.y() == electricBottom)
01753        border = 4;
01754 
01755     if ((electric_current_border == border) &&
01756         (timestampDiff(electric_time_last, now) < treshold_reset) &&
01757         ((pos-electric_push_point).manhattanLength() < distance_reset))
01758         {
01759         electric_time_last = now;
01760 
01761         if (timestampDiff(electric_time_first, now) > treshold_set)
01762             {
01763             electric_current_border = 0;
01764 
01765             QRect r = QApplication::desktop()->geometry();
01766             int offset;
01767 
01768             int desk_before = currentDesktop();
01769             switch(border)
01770                 {
01771                 case 1:
01772                  slotSwitchDesktopLeft();
01773                  if (currentDesktop() != desk_before) 
01774                     {
01775                     offset = r.width() / 5;
01776                     QCursor::setPos(r.width() - offset, pos.y());
01777                     }
01778                 break;
01779 
01780                case 2:
01781                 slotSwitchDesktopRight();
01782                 if (currentDesktop() != desk_before) 
01783                     {
01784                     offset = r.width() / 5;
01785                     QCursor::setPos(offset, pos.y());
01786                     }
01787                 break;
01788 
01789                case 3:
01790                 slotSwitchDesktopUp();
01791                 if (currentDesktop() != desk_before) 
01792                     {
01793                     offset = r.height() / 5;
01794                     QCursor::setPos(pos.x(), r.height() - offset);
01795                     }
01796                 break;
01797 
01798                case 4:
01799                 slotSwitchDesktopDown();
01800                 if (currentDesktop() != desk_before) 
01801                     {
01802                     offset = r.height() / 5;
01803                     QCursor::setPos(pos.x(), offset);
01804                     }
01805                 break;
01806                 }
01807             return;
01808             }
01809         }
01810     else 
01811         {
01812         electric_current_border = border;
01813         electric_time_first = now;
01814         electric_time_last = now;
01815         electric_push_point = pos;
01816         }
01817 
01818     int mouse_warp = 1;
01819 
01820   // reset the pointer to find out wether the user is really pushing
01821     switch( border)
01822         {
01823         case 1: QCursor::setPos(pos.x()+mouse_warp, pos.y()); break;
01824         case 2: QCursor::setPos(pos.x()-mouse_warp, pos.y()); break;
01825         case 3: QCursor::setPos(pos.x(), pos.y()+mouse_warp); break;
01826         case 4: QCursor::setPos(pos.x(), pos.y()-mouse_warp); break;
01827         }
01828     }
01829 
01830 // this function is called when the user entered an electric border
01831 // with the mouse. It may switch to another virtual desktop
01832 void Workspace::electricBorder(XEvent *e)
01833     {
01834     Time now = e->xcrossing.time;
01835     QPoint p(e->xcrossing.x_root, e->xcrossing.y_root);
01836 
01837     clientMoved(p, now);
01838     }
01839 
01840 // electric borders (input only windows) have to be always on the
01841 // top. For that reason kwm calls this function always after some
01842 // windows have been raised.
01843 void Workspace::raiseElectricBorders()
01844     {
01845 
01846     if(electric_have_borders)
01847         {
01848         XRaiseWindow(qt_xdisplay(), electric_top_border);
01849         XRaiseWindow(qt_xdisplay(), electric_left_border);
01850         XRaiseWindow(qt_xdisplay(), electric_bottom_border);
01851         XRaiseWindow(qt_xdisplay(), electric_right_border);
01852         }
01853     }
01854 
01855 void Workspace::addTopMenu( Client* c )
01856     {
01857     assert( c->isTopMenu());
01858     assert( !topmenus.contains( c ));
01859     topmenus.append( c );
01860     if( managingTopMenus())
01861         {
01862         int minsize = c->minSize().height();
01863         if( minsize > topMenuHeight())
01864             {
01865             topmenu_height = minsize;
01866             updateTopMenuGeometry();
01867             }
01868         updateTopMenuGeometry( c );
01869         updateCurrentTopMenu();
01870         }
01871 //        kdDebug() << "NEW TOPMENU:" << c << endl;
01872     }
01873 
01874 void Workspace::removeTopMenu( Client* c )
01875     {
01876 //    if( c->isTopMenu())
01877 //        kdDebug() << "REMOVE TOPMENU:" << c << endl;
01878     assert( c->isTopMenu());
01879     assert( topmenus.contains( c ));
01880     topmenus.remove( c );
01881     updateCurrentTopMenu();
01882     // TODO reduce topMenuHeight() if possible?
01883     }
01884 
01885 void Workspace::lostTopMenuSelection()
01886     {
01887 //    kdDebug() << "lost TopMenu selection" << endl;
01888     // make sure this signal is always set when not owning the selection
01889     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
01890     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
01891     if( !managing_topmenus )
01892         return;
01893     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
01894     disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
01895     managing_topmenus = false;
01896     delete topmenu_space;
01897     topmenu_space = NULL;
01898     updateClientArea();
01899     for( ClientList::ConstIterator it = topmenus.begin();
01900          it != topmenus.end();
01901          ++it )
01902         (*it)->checkWorkspacePosition();
01903     }
01904 
01905 void Workspace::lostTopMenuOwner()
01906     {
01907     if( !options->topMenuEnabled())
01908         return;
01909 //    kdDebug() << "TopMenu selection lost owner" << endl;
01910     if( !topmenu_selection->claim( false ))
01911         {
01912 //        kdDebug() << "Failed to claim TopMenu selection" << endl;
01913         return;
01914         }
01915 //    kdDebug() << "claimed TopMenu selection" << endl;
01916     setupTopMenuHandling();
01917     }
01918 
01919 void Workspace::setupTopMenuHandling()
01920     {
01921     if( managing_topmenus )
01922         return;
01923     connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
01924     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
01925     managing_topmenus = true;
01926     topmenu_space = new QWidget;
01927     updateTopMenuGeometry();
01928     topmenu_space->show();
01929     updateClientArea();
01930     updateCurrentTopMenu();
01931     }
01932 
01933 int Workspace::topMenuHeight() const
01934     {
01935     if( topmenu_height == 0 )
01936         { // simply create a dummy menubar and use its preffered height as the menu height
01937         KMenuBar tmpmenu;
01938         tmpmenu.insertItem( "dummy" );
01939         topmenu_height = tmpmenu.sizeHint().height();
01940         }
01941     return topmenu_height;
01942     }
01943 
01944 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge )
01945     {
01946     return mgr->createDecoration( bridge );
01947     }
01948 
01949 QString Workspace::desktopName( int desk ) const
01950     {
01951     return QString::fromUtf8( rootInfo->desktopName( desk ) );
01952     }
01953 
01954 bool Workspace::checkStartupNotification( Window w, KStartupInfoData& data )
01955     {
01956     return startup->checkStartup( w, data ) == KStartupInfo::Match;
01957     }
01958 
01963 void Workspace::focusToNull()
01964     {
01965     int mask;
01966     XSetWindowAttributes attr;
01967     if (null_focus_window == 0) 
01968         {
01969         mask = CWOverrideRedirect;
01970         attr.override_redirect = 1;
01971         null_focus_window = XCreateWindow(qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent,
01972                           InputOnly, CopyFromParent, mask, &attr);
01973         XMapWindow(qt_xdisplay(), null_focus_window);
01974         }
01975     XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time );
01976     }
01977 
01978 void Workspace::helperDialog( const QString& message, const Client* c )
01979     {
01980     QStringList args;
01981     QString type;
01982     if( message == "noborderaltf3" )
01983         {
01984         args << "--msgbox" <<
01985               i18n( "You have selected to show a window without its border.\n"
01986                     "Without the border, you won't be able to enable the border "
01987                     "again using the mouse. Use the window operations menu instead, "
01988                     "activated using the %1 keyboard shortcut." )
01989                 .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
01990         type = "altf3warning";
01991         }
01992     else if( message == "fullscreenaltf3" )
01993         {
01994         args << "--msgbox" <<
01995               i18n( "You have selected to show a window in fullscreen mode.\n"
01996                     "If the application itself doesn't have an option to turn the fullscreen "
01997                     "mode off, you won't be able to disable it "
01998                     "again using the mouse. Use the window operations menu instead, "
01999                     "activated using the %1 keyboard shortcut." )
02000                 .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
02001         type = "altf3warning";
02002         }
02003     else
02004         assert( false );
02005     KProcess proc;
02006     proc << "kdialog" << args;
02007     if( !type.isEmpty())
02008         {
02009         KConfig cfg( "kwin_dialogsrc" );
02010         cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox
02011         if( !cfg.readBoolEntry( type, true )) // has don't show again checked
02012             return;                           // save launching kdialog
02013         proc << "--dontagain" << "kwin_dialogsrc:" + type;
02014         }
02015     if( c != NULL )
02016         proc << "--embed" << QString::number( c->window());
02017     proc.start( KProcess::DontCare );
02018     }
02019 
02020 } // namespace
02021 
02022 #include "workspace.moc"
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 29 21:20:55 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003