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