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

twin

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

twin

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

twin

Skip menu "twin"
  • kate
  • libkonq
  • twin
  •   lib
Generated for twin by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.