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