• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kwin
 

kwin

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

kwin

Skip menu "kwin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

kwin

Skip menu "kwin"
  • kate
  • kwin
  •   lib
  • libkonq
Generated for kwin by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |