diff options
Diffstat (limited to 'kwin/workspace.cpp')
-rw-r--r-- | kwin/workspace.cpp | 3010 |
1 files changed, 0 insertions, 3010 deletions
diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp deleted file mode 100644 index 44045838c..000000000 --- a/kwin/workspace.cpp +++ /dev/null @@ -1,3010 +0,0 @@ -/***************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 1999, 2000 Matthias Ettrich <[email protected]> -Copyright (C) 2003 Lubos Lunak <[email protected]> - -You can Freely distribute this program under the GNU General Public -License. See the file "COPYING" for the exact licensing terms. -******************************************************************/ - -//#define QT_CLEAN_NAMESPACE - -#include "workspace.h" - -#include <kapplication.h> -#include <kstartupinfo.h> -#include <fixx11h.h> -#include <kconfig.h> -#include <kglobal.h> -#include <tqpopupmenu.h> -#include <klocale.h> -#include <tqregexp.h> -#include <tqpainter.h> -#include <tqbitmap.h> -#include <tqclipboard.h> -#include <kmenubar.h> -#include <kprocess.h> -#include <kglobalaccel.h> -#include <dcopclient.h> -#include <kipc.h> - -#include "plugins.h" -#include "client.h" -#include "popupinfo.h" -#include "tabbox.h" -#include "atoms.h" -#include "placement.h" -#include "notifications.h" -#include "group.h" -#include "rules.h" - -#include <X11/extensions/shape.h> -#include <X11/keysym.h> -#include <X11/keysymdef.h> -#include <X11/cursorfont.h> - -#include <pwd.h> - -namespace KWinInternal -{ - -extern int screen_number; - -Workspace *Workspace::_self = 0; - -KProcess* kompmgr = 0; -KSelectionOwner* kompmgr_selection; - -bool allowKompmgrRestart = TRUE; - -bool supportsCompMgr() -{ - int i; - - bool damageExt = XQueryExtension(qt_xdisplay(), "DAMAGE", &i, &i, &i); - bool compositeExt = XQueryExtension(qt_xdisplay(), "Composite", &i, &i, &i); - bool xfixesExt = XQueryExtension(qt_xdisplay(), "XFIXES", &i, &i, &i); - - return damageExt && compositeExt && xfixesExt; -} - -// Rikkus: This class is too complex. It needs splitting further. -// It's a nightmare to understand, especially with so few comments :( - -// Matthias: Feel free to ask me questions about it. Feel free to add -// comments. I disagree that further splittings makes it easier. 2500 -// lines are not too much. It's the task that is complex, not the -// code. -Workspace::Workspace( bool restore ) - : DCOPObject ("KWinInterface"), - TQObject (0, "workspace"), - current_desktop (0), - number_of_desktops(0), - active_screen (0), - active_popup( NULL ), - active_popup_client( NULL ), - desktop_widget (0), - temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ), - rules_updates_disabled( false ), - active_client (0), - last_active_client (0), - next_active_client (0), - most_recently_raised (0), - movingClient(0), - pending_take_activity ( NULL ), - delayfocus_client (0), - showing_desktop( false ), - block_showing_desktop( 0 ), - was_user_interaction (false), - session_saving (false), - control_grab (false), - tab_grab (false), - mouse_emulation (false), - block_focus (0), - tab_box (0), - popupinfo (0), - popup (0), - advanced_popup (0), - desk_popup (0), - desk_popup_index (0), - keys (0), - client_keys ( NULL ), - client_keys_dialog ( NULL ), - client_keys_client ( NULL ), - disable_shortcuts_keys ( NULL ), - global_shortcuts_disabled( false ), - global_shortcuts_disabled_for_client( false ), - root (0), - workspaceInit (true), - startup(0), electric_have_borders(false), - electric_current_border(0), - electric_top_border(None), - electric_bottom_border(None), - electric_left_border(None), - electric_right_border(None), - layoutOrientation(Qt::Vertical), - layoutX(-1), - layoutY(2), - workarea(NULL), - screenarea(NULL), - managing_topmenus( false ), - topmenu_selection( NULL ), - topmenu_watcher( NULL ), - topmenu_height( 0 ), - topmenu_space( NULL ), - set_active_client_recursion( 0 ), - block_stacking_updates( 0 ), - forced_global_mouse_grab( false ) - { - _self = this; - mgr = new PluginMgr; - root = qt_xrootwin(); - default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() ); - installed_colormap = default_colormap; - session.setAutoDelete( TRUE ); - - connect( &temporaryRulesMessages, TQT_SIGNAL( gotMessage( const TQString& )), - this, TQT_SLOT( gotTemporaryRulesMessage( const TQString& ))); - connect( &rulesUpdatedTimer, TQT_SIGNAL( timeout()), this, TQT_SLOT( writeWindowRules())); - - updateXTime(); // needed for proper initialization of user_time in Client ctor - - delayFocusTimer = 0; - - electric_time_first = GET_QT_X_TIME(); - electric_time_last = GET_QT_X_TIME(); - - if ( restore ) - loadSessionInfo(); - - loadWindowRules(); - - (void) TQApplication::desktop(); // trigger creation of desktop widget - - desktop_widget = - new TQWidget( - 0, - "desktop_widget", - (WFlags)(TQt::WType_Desktop | TQt::WPaintUnclipped) - ); - - kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later - // call this before XSelectInput() on the root window - startup = new KStartupInfo( - KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this ); - - // select windowmanager privileges - XSelectInput(qt_xdisplay(), root, - KeyPressMask | - PropertyChangeMask | - ColormapChangeMask | - SubstructureRedirectMask | - SubstructureNotifyMask | - FocusChangeMask // for NotifyDetailNone - ); - - Shape::init(); - - // compatibility - long data = 1; - - XChangeProperty( - qt_xdisplay(), - qt_xrootwin(), - atoms->twin_running, - atoms->twin_running, - 32, - PropModeAppend, - (unsigned char*) &data, - 1 - ); - - client_keys = new KGlobalAccel( this ); - initShortcuts(); - tab_box = new TabBox( this ); - popupinfo = new PopupInfo( this ); - - init(); - -#if (QT_VERSION-0 >= 0x030200) // XRANDR support - connect( kapp->desktop(), TQT_SIGNAL( resized( int )), TQT_SLOT( desktopResized())); -#endif - - if (!supportsCompMgr()) - options->useTranslucency = false; - - // start kompmgr - i wanted to put this into main.cpp, but that would prevent dcop support, as long as Application was no dcop_object - if (options->useTranslucency) - { - kompmgr = new KProcess; - connect(kompmgr, TQT_SIGNAL(receivedStderr(KProcess*, char*, int)), TQT_SLOT(handleKompmgrOutput(KProcess*, char*, int))); - *kompmgr << "kompmgr"; - startKompmgr(); - } - else - { - // If kompmgr is already running, send it SIGTERM - // Attempt to load the kompmgr pid file - const char *home; - struct passwd *p; - p = getpwuid(getuid()); - if (p) - home = p->pw_dir; - else - home = getenv("HOME"); - char *filename; - const char *configfile = "/.kompmgr.pid"; - int n = strlen(home)+strlen(configfile)+1; - filename = (char*)malloc(n*sizeof(char)); - memset(filename,0,n); - strcat(filename, home); - strcat(filename, configfile); - - printf("reading '%s' as kompmgr pidfile\n\n", filename); - - // Now that we did all that by way of introduction...read the file! - FILE *pFile; - char buffer[255]; - pFile = fopen(filename, "r"); - int kompmgrpid = 0; - if (pFile) - { - // obtain file size - fseek (pFile , 0 , SEEK_END); - unsigned long lSize = ftell (pFile); - if (lSize > 254) - lSize = 254; - rewind (pFile); - size_t result = fread (buffer, 1, lSize, pFile); - fclose(pFile); - kompmgrpid = atoi(buffer); - } - - free(filename); - filename = NULL; - - if (kompmgrpid) - { - kill(kompmgrpid, SIGTERM); - } - else - { - stopKompmgr(); - } - } - } - - -void Workspace::init() - { - checkElectricBorders(); - -// not used yet -// topDock = 0L; -// maximizedWindowCounter = 0; - - supportWindow = new TQWidget; - XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp - - XSetWindowAttributes attr; - attr.override_redirect = 1; - null_focus_window = XCreateWindow( qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent, - InputOnly, CopyFromParent, CWOverrideRedirect, &attr ); - XMapWindow(qt_xdisplay(), null_focus_window); - - unsigned long protocols[ 5 ] = - { - NET::Supported | - NET::SupportingWMCheck | - NET::ClientList | - NET::ClientListStacking | - NET::DesktopGeometry | - NET::NumberOfDesktops | - NET::CurrentDesktop | - NET::ActiveWindow | - NET::WorkArea | - NET::CloseWindow | - NET::DesktopNames | - NET::KDESystemTrayWindows | - NET::WMName | - NET::WMVisibleName | - NET::WMDesktop | - NET::WMWindowType | - NET::WMState | - NET::WMStrut | - NET::WMIconGeometry | - NET::WMIcon | - NET::WMPid | - NET::WMMoveResize | - NET::WMKDESystemTrayWinFor | - NET::WMFrameExtents | - NET::WMPing - , - NET::NormalMask | - NET::DesktopMask | - NET::DockMask | - NET::ToolbarMask | - NET::MenuMask | - NET::DialogMask | - NET::OverrideMask | - NET::TopMenuMask | - NET::UtilityMask | - NET::SplashMask | - 0 - , - NET::Modal | -// NET::Sticky | // large desktops not supported (and probably never will be) - NET::MaxVert | - NET::MaxHoriz | - NET::Shaded | - NET::SkipTaskbar | - NET::KeepAbove | -// NET::StaysOnTop | the same like KeepAbove - NET::SkipPager | - NET::Hidden | - NET::FullScreen | - NET::KeepBelow | - NET::DemandsAttention | - 0 - , - NET::WM2UserTime | - NET::WM2StartupId | - NET::WM2AllowedActions | - NET::WM2RestackWindow | - NET::WM2MoveResizeWindow | - NET::WM2ExtendedStrut | - NET::WM2KDETemporaryRules | - NET::WM2ShowingDesktop | - NET::WM2FullPlacement | - NET::WM2DesktopLayout | - 0 - , - NET::ActionMove | - NET::ActionResize | - NET::ActionMinimize | - NET::ActionShade | -// NET::ActionStick | // Sticky state is not supported - NET::ActionMaxVert | - NET::ActionMaxHoriz | - NET::ActionFullScreen | - NET::ActionChangeDesktop | - NET::ActionClose | - 0 - , - }; - - rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin", - protocols, 5, qt_xscreen() ); - - loadDesktopSettings(); - updateDesktopLayout(); - // extra NETRootInfo instance in Client mode is needed to get the values of the properties - NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop ); - int initial_desktop; - if( !kapp->isSessionRestored()) - initial_desktop = client_info.currentDesktop(); - else - { - KConfigGroupSaver saver( kapp->sessionConfig(), "Session" ); - initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 ); - } - if( !setCurrentDesktop( initial_desktop )) - setCurrentDesktop( 1 ); - - // now we know how many desktops we'll, thus, we initialise the positioning object - initPositioning = new Placement(this); - - connect(&reconfigureTimer, TQT_SIGNAL(timeout()), this, - TQT_SLOT(slotReconfigure())); - connect( &updateToolWindowsTimer, TQT_SIGNAL( timeout()), this, TQT_SLOT( slotUpdateToolWindows())); - - connect(kapp, TQT_SIGNAL(appearanceChanged()), this, - TQT_SLOT(slotReconfigure())); - connect(kapp, TQT_SIGNAL(settingsChanged(int)), this, - TQT_SLOT(slotSettingsChanged(int))); - connect(kapp, TQT_SIGNAL( kipcMessage( int, int )), this, TQT_SLOT( kipcMessage( int, int ))); - - active_client = NULL; - rootInfo->setActiveWindow( None ); - focusToNull(); - if( !kapp->isSessionRestored()) - ++block_focus; // because it will be set below - - char nm[ 100 ]; - sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay())); - Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False ); - topmenu_selection = new KSelectionOwner( topmenu_atom ); - topmenu_watcher = new KSelectionWatcher( topmenu_atom ); -// TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before - - { // begin updates blocker block - StackingUpdatesBlocker blocker( this ); - - if( options->topMenuEnabled() && topmenu_selection->claim( false )) - setupTopMenuHandling(); // this can call updateStackingOrder() - else - lostTopMenuSelection(); - - unsigned int i, nwins; - Window root_return, parent_return, *wins; - XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins); - for (i = 0; i < nwins; i++) - { - XWindowAttributes attr; - XGetWindowAttributes(qt_xdisplay(), wins[i], &attr); - if (attr.override_redirect ) - continue; - if( topmenu_space && topmenu_space->winId() == wins[ i ] ) - continue; - if (attr.map_state != IsUnmapped) - { - if ( addSystemTrayWin( wins[i] ) ) - continue; - Client* c = createClient( wins[i], true ); - if ( c != NULL && root != qt_xrootwin() ) - { // TODO what is this? - // TODO may use TQWidget:.create - XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 ); - c->move(0,0); - } - } - } - if ( wins ) - XFree((void *) wins); - // propagate clients, will really happen at the end of the updates blocker block - updateStackingOrder( true ); - - updateClientArea(); - raiseElectricBorders(); - - // NETWM spec says we have to set it to (0,0) if we don't support it - NETPoint* viewports = new NETPoint[ number_of_desktops ]; - rootInfo->setDesktopViewport( number_of_desktops, *viewports ); - delete[] viewports; - TQRect geom = TQApplication::desktop()->geometry(); - NETSize desktop_geometry; - desktop_geometry.width = geom.width(); - desktop_geometry.height = geom.height(); - rootInfo->setDesktopGeometry( -1, desktop_geometry ); - setShowingDesktop( false ); - - } // end updates blocker block - - Client* new_active_client = NULL; - if( !kapp->isSessionRestored()) - { - --block_focus; - new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow())); - } - if( new_active_client == NULL - && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage() - { - if( new_active_client == NULL ) - new_active_client = topClientOnDesktop( currentDesktop()); - if( new_active_client == NULL && !desktops.isEmpty() ) - new_active_client = findDesktop( true, currentDesktop()); - } - if( new_active_client != NULL ) - activateClient( new_active_client ); - // SELI TODO this won't work with unreasonable focus policies, - // and maybe in rare cases also if the selected client doesn't - // want focus - workspaceInit = false; -// TODO ungrabXServer() - } - -Workspace::~Workspace() - { - if (kompmgr) - delete kompmgr; - blockStackingUpdates( true ); -// TODO grabXServer(); - // use stacking_order, so that twin --replace keeps stacking order - for( ClientList::ConstIterator it = stacking_order.begin(); - it != stacking_order.end(); - ++it ) - { - // only release the window - (*it)->releaseWindow( true ); - // No removeClient() is called, it does more than just removing. - // However, remove from some lists to e.g. prevent performTransiencyCheck() - // from crashing. - clients.remove( *it ); - desktops.remove( *it ); - } - delete desktop_widget; - delete tab_box; - delete popupinfo; - delete popup; - if ( root == qt_xrootwin() ) - XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->twin_running); - - writeWindowRules(); - KGlobal::config()->sync(); - - delete rootInfo; - delete supportWindow; - delete mgr; - delete[] workarea; - delete[] screenarea; - delete startup; - delete initPositioning; - delete topmenu_watcher; - delete topmenu_selection; - delete topmenu_space; - delete client_keys_dialog; - while( !rules.isEmpty()) - { - delete rules.front(); - rules.pop_front(); - } - XDestroyWindow( qt_xdisplay(), null_focus_window ); -// TODO ungrabXServer(); - _self = 0; - } - -Client* Workspace::createClient( Window w, bool is_mapped ) - { - StackingUpdatesBlocker blocker( this ); - Client* c = new Client( this ); - if( !c->manage( w, is_mapped )) - { - Client::deleteClient( c, Allowed ); - return NULL; - } - addClient( c, Allowed ); - return c; - } - -void Workspace::addClient( Client* c, allowed_t ) - { - // waited with trans settings until window figured out if active or not ;) -// qWarning("%s", (const char*)(c->resourceClass())); - c->setBMP(c->resourceName() == "beep-media-player" || c->decorationId() == None); - // first check if the window has it's own opinion of it's translucency ;) - c->getWindowOpacity(); - if (c->isDock()) - { -// if (c->x() == 0 && c->y() == 0 && c->width() > c->height()) topDock = c; - if (!c->hasCustomOpacity()) // this xould be done slightly more efficient, but we want to support the topDock in future - { - c->setShadowSize(options->dockShadowSize); - c->setOpacity(options->translucentDocks, options->dockOpacity); - } - } - - if (c->isMenu() || c->isTopMenu()) - { - c->setShadowSize(options->menuShadowSize); - } -//------------------------------------------------ - Group* grp = findGroup( c->window()); - if( grp != NULL ) - grp->gotLeader( c ); - - if ( c->isDesktop() ) - { - desktops.append( c ); - if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop()) - requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active - } - else - { - updateFocusChains( c, FocusChainUpdate ); // add to focus chain if not already there - clients.append( c ); - } - if( !unconstrained_stacking_order.contains( c )) - unconstrained_stacking_order.append( c ); - if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires - stacking_order.append( c ); // c to be in stacking_order - if( c->isTopMenu()) - addTopMenu( c ); - updateClientArea(); // this cannot be in manage(), because the client got added only now - updateClientLayer( c ); - if( c->isDesktop()) - { - raiseClient( c ); - // if there's no active client, make this desktop the active one - if( activeClient() == NULL && should_get_focus.count() == 0 ) - activateClient( findDesktop( true, currentDesktop())); - } - c->checkActiveModal(); - checkTransients( c->window()); // SELI does this really belong here? - updateStackingOrder( true ); // propagate new client - if( c->isUtility() || c->isMenu() || c->isToolbar()) - updateToolWindows( true ); - checkNonExistentClients(); - } - -/* - Destroys the client \a c - */ -void Workspace::removeClient( Client* c, allowed_t ) - { - if (c == active_popup_client) - closeActivePopup(); - - if( client_keys_client == c ) - setupWindowShortcutDone( false ); - if( !c->shortcut().isNull()) - c->setShortcut( TQString::null ); // remove from client_keys - - if( c->isDialog()) - Notify::raise( Notify::TransDelete ); - if( c->isNormalWindow()) - Notify::raise( Notify::Delete ); - - Q_ASSERT( clients.contains( c ) || desktops.contains( c )); - clients.remove( c ); - desktops.remove( c ); - unconstrained_stacking_order.remove( c ); - stacking_order.remove( c ); - for( int i = 1; - i <= numberOfDesktops(); - ++i ) - focus_chain[ i ].remove( c ); - global_focus_chain.remove( c ); - attention_chain.remove( c ); - showing_desktop_clients.remove( c ); - if( c->isTopMenu()) - removeTopMenu( c ); - Group* group = findGroup( c->window()); - if( group != NULL ) - group->lostLeader(); - - if ( c == most_recently_raised ) - most_recently_raised = 0; - should_get_focus.remove( c ); - Q_ASSERT( c != active_client ); - if ( c == last_active_client ) - last_active_client = 0; - if( c == pending_take_activity ) - pending_take_activity = NULL; - if( c == delayfocus_client ) - cancelDelayFocus(); - - updateStackingOrder( true ); - - if (tab_grab) - tab_box->repaint(); - - updateClientArea(); - } - -void Workspace::updateFocusChains( Client* c, FocusChainChange change ) - { - if( !c->wantsTabFocus()) // doesn't want tab focus, remove - { - for( int i=1; - i<= numberOfDesktops(); - ++i ) - focus_chain[i].remove(c); - global_focus_chain.remove( c ); - return; - } - if(c->desktop() == NET::OnAllDesktops) - { //now on all desktops, add it to focus_chains it is not already in - for( int i=1; i<= numberOfDesktops(); i++) - { // making first/last works only on current desktop, don't affect all desktops - if( i == currentDesktop() - && ( change == FocusChainMakeFirst || change == FocusChainMakeLast )) - { - focus_chain[ i ].remove( c ); - if( change == FocusChainMakeFirst ) - focus_chain[ i ].append( c ); - else - focus_chain[ i ].prepend( c ); - } - else if( !focus_chain[ i ].contains( c )) - { // add it after the active one - if( active_client != NULL && active_client != c - && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client ) - focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c ); - else - focus_chain[ i ].append( c ); // otherwise add as the first one - } - } - } - else //now only on desktop, remove it anywhere else - { - for( int i=1; i<= numberOfDesktops(); i++) - { - if( i == c->desktop()) - { - if( change == FocusChainMakeFirst ) - { - focus_chain[ i ].remove( c ); - focus_chain[ i ].append( c ); - } - else if( change == FocusChainMakeLast ) - { - focus_chain[ i ].remove( c ); - focus_chain[ i ].prepend( c ); - } - else if( !focus_chain[ i ].contains( c )) - { - if( active_client != NULL && active_client != c - && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client ) - focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c ); - else - focus_chain[ i ].append( c ); // otherwise add as the first one - } - } - else - focus_chain[ i ].remove( c ); - } - } - if( change == FocusChainMakeFirst ) - { - global_focus_chain.remove( c ); - global_focus_chain.append( c ); - } - else if( change == FocusChainMakeLast ) - { - global_focus_chain.remove( c ); - global_focus_chain.prepend( c ); - } - else if( !global_focus_chain.contains( c )) - { - if( active_client != NULL && active_client != c - && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client ) - global_focus_chain.insert( global_focus_chain.fromLast(), c ); - else - global_focus_chain.append( c ); // otherwise add as the first one - } - } - -void Workspace::updateOverlappingShadows(unsigned long window) - { - Client *client; - - if ((client = findClient(WindowMatchPredicate((WId)window)))) - // Redraw overlapping shadows without waiting for the specified window - // to redraw its own shadow - client->drawOverlappingShadows(false); - } - -void Workspace::setShadowed(unsigned long window, bool shadowed) - { - Client *client; - - if ((client = findClient(WindowMatchPredicate((WId)window)))) - client->setShadowed(shadowed); - } - -void Workspace::updateCurrentTopMenu() - { - if( !managingTopMenus()) - return; - // toplevel menubar handling - Client* menubar = 0; - bool block_desktop_menubar = false; - if( active_client ) - { - // show the new menu bar first... - Client* menu_client = active_client; - for(;;) - { - if( menu_client->isFullScreen()) - block_desktop_menubar = true; - for( ClientList::ConstIterator it = menu_client->transients().begin(); - it != menu_client->transients().end(); - ++it ) - if( (*it)->isTopMenu()) - { - menubar = *it; - break; - } - if( menubar != NULL || !menu_client->isTransient()) - break; - if( menu_client->isModal() || menu_client->transientFor() == NULL ) - break; // don't use mainwindow's menu if this is modal or group transient - menu_client = menu_client->transientFor(); - } - if( !menubar ) - { // try to find any topmenu from the application (#72113) - for( ClientList::ConstIterator it = active_client->group()->members().begin(); - it != active_client->group()->members().end(); - ++it ) - if( (*it)->isTopMenu()) - { - menubar = *it; - break; - } - } - } - if( !menubar && !block_desktop_menubar && options->desktopTopMenu()) - { - // Find the menubar of the desktop - Client* desktop = findDesktop( true, currentDesktop()); - if( desktop != NULL ) - { - for( ClientList::ConstIterator it = desktop->transients().begin(); - it != desktop->transients().end(); - ++it ) - if( (*it)->isTopMenu()) - { - menubar = *it; - break; - } - } - // TODO to be cleaned app with window grouping - // Without qt-copy patch #0009, the topmenu and desktop are not in the same group, - // thus the topmenu is not transient for it :-/. - if( menubar == NULL ) - { - for( ClientList::ConstIterator it = topmenus.begin(); - it != topmenus.end(); - ++it ) - if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR - { // set pointing to the root window - menubar = *it; // to recognize it here - break; // Also, with the xroot hack in kdesktop, - } // there's no NET::Desktop window to be transient for - } - } - -// kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl; - if ( menubar ) - { - if( active_client && !menubar->isOnDesktop( active_client->desktop())) - menubar->setDesktop( active_client->desktop()); - menubar->hideClient( false ); - topmenu_space->hide(); - // make it appear like it's been raised manually - it's in the Dock layer anyway, - // and not raising it could mess up stacking order of topmenus within one application, - // and thus break raising of mainclients in raiseClient() - unconstrained_stacking_order.remove( menubar ); - unconstrained_stacking_order.append( menubar ); - } - else if( !block_desktop_menubar ) - { // no topmenu active - show the space window, so that there's not empty space - topmenu_space->show(); - } - - // ... then hide the other ones. Avoids flickers. - for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) - { - if( (*it)->isTopMenu() && (*it) != menubar ) - (*it)->hideClient( true ); - } - } - - -void Workspace::updateToolWindows( bool also_hide ) - { - // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?) - if( !options->hideUtilityWindowsForInactive ) - { - for( ClientList::ConstIterator it = clients.begin(); - it != clients.end(); - ++it ) - (*it)->hideClient( false ); - return; - } - const Group* group = NULL; - const Client* client = active_client; -// Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow -// will be shown; if a group transient is group, all tools in the group will be shown - while( client != NULL ) - { - if( !client->isTransient()) - break; - if( client->groupTransient()) - { - group = client->group(); - break; - } - client = client->transientFor(); - } - // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0, - // i.e. if it's not up to date - - // SELI but maybe it should - what if a new client has been added that's not in stacking order yet? - ClientList to_show, to_hide; - for( ClientList::ConstIterator it = stacking_order.begin(); - it != stacking_order.end(); - ++it ) - { - if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar()) - { - bool show = true; - if( !(*it)->isTransient()) - { - if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible - show = true; - else if( client != NULL && (*it)->group() == client->group()) - show = true; - else - show = false; - } - else - { - if( group != NULL && (*it)->group() == group ) - show = true; - else if( client != NULL && client->hasTransient( (*it), true )) - show = true; - else - show = false; - } - if( !show && also_hide ) - { - const ClientList mainclients = (*it)->mainClients(); - // don't hide utility windows which are standalone(?) or - // have e.g. kicker as mainwindow - if( mainclients.isEmpty()) - show = true; - for( ClientList::ConstIterator it2 = mainclients.begin(); - it2 != mainclients.end(); - ++it2 ) - { - if( (*it2)->isSpecialWindow()) - show = true; - } - if( !show ) - to_hide.append( *it ); - } - if( show ) - to_show.append( *it ); - } - } // first show new ones, then hide - for( ClientList::ConstIterator it = to_show.fromLast(); - it != to_show.end(); - --it ) // from topmost - // TODO since this is in stacking order, the order of taskbar entries changes :( - (*it)->hideClient( false ); - if( also_hide ) - { - for( ClientList::ConstIterator it = to_hide.begin(); - it != to_hide.end(); - ++it ) // from bottommost - (*it)->hideClient( true ); - updateToolWindowsTimer.stop(); - } - else // setActiveClient() is after called with NULL client, quickly followed - { // by setting a new client, which would result in flickering - updateToolWindowsTimer.start( 50, true ); - } - } - -void Workspace::slotUpdateToolWindows() - { - updateToolWindows( true ); - } - -/*! - Updates the current colormap according to the currently active client - */ -void Workspace::updateColormap() - { - Colormap cmap = default_colormap; - if ( activeClient() && activeClient()->colormap() != None ) - cmap = activeClient()->colormap(); - if ( cmap != installed_colormap ) - { - XInstallColormap(qt_xdisplay(), cmap ); - installed_colormap = cmap; - } - } - -void Workspace::reconfigure() - { - reconfigureTimer.start(200, true); - } - - -void Workspace::slotSettingsChanged(int category) - { - kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl; - if( category == (int) KApplication::SETTINGS_SHORTCUTS ) - readShortcuts(); - } - -/*! - Reread settings - */ -KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() ); - -void Workspace::slotReconfigure() - { - kdDebug(1212) << "Workspace::slotReconfigure()" << endl; - reconfigureTimer.stop(); - - KGlobal::config()->reparseConfiguration(); - unsigned long changed = options->updateSettings(); - tab_box->reconfigure(); - popupinfo->reconfigure(); - initPositioning->reinitCascading( 0 ); - readShortcuts(); - forEachClient( CheckIgnoreFocusStealingProcedure()); - updateToolWindows( true ); - - if( mgr->reset( changed )) - { // decorations need to be recreated -#if 0 // This actually seems to make things worse now - TQWidget curtain; - curtain.setBackgroundMode( NoBackground ); - curtain.setGeometry( TQApplication::desktop()->geometry() ); - curtain.show(); -#endif - for( ClientList::ConstIterator it = clients.begin(); - it != clients.end(); - ++it ) - { - (*it)->updateDecoration( true, true ); - } - mgr->destroyPreviousPlugin(); - } - else - { - forEachClient( CheckBorderSizesProcedure()); - } - - checkElectricBorders(); - - if( options->topMenuEnabled() && !managingTopMenus()) - { - if( topmenu_selection->claim( false )) - setupTopMenuHandling(); - else - lostTopMenuSelection(); - } - else if( !options->topMenuEnabled() && managingTopMenus()) - { - topmenu_selection->release(); - lostTopMenuSelection(); - } - topmenu_height = 0; // tqinvalidate used menu height - if( managingTopMenus()) - { - updateTopMenuGeometry(); - updateCurrentTopMenu(); - } - - loadWindowRules(); - for( ClientList::Iterator it = clients.begin(); - it != clients.end(); - ++it ) - { - (*it)->setupWindowRules( true ); - (*it)->applyWindowRules(); - discardUsedWindowRules( *it, false ); - } - - if (options->resetKompmgr) // need restart - { - bool tmp = options->useTranslucency; - - // If kompmgr is already running, sending SIGUSR2 will force a reload of its settings - // Attempt to load the kompmgr pid file - const char *home; - struct passwd *p; - p = getpwuid(getuid()); - if (p) - home = p->pw_dir; - else - home = getenv("HOME"); - char *filename; - const char *configfile = "/.kompmgr.pid"; - int n = strlen(home)+strlen(configfile)+1; - filename = (char*)malloc(n*sizeof(char)); - memset(filename,0,n); - strcat(filename, home); - strcat(filename, configfile); - - printf("reading '%s' as kompmgr pidfile\n\n", filename); - - // Now that we did all that by way of introduction...read the file! - FILE *pFile; - char buffer[255]; - pFile = fopen(filename, "r"); - int kompmgrpid = 0; - if (pFile) - { - // obtain file size - fseek (pFile , 0 , SEEK_END); - unsigned long lSize = ftell (pFile); - if (lSize > 254) - lSize = 254; - rewind (pFile); - size_t result = fread (buffer, 1, lSize, pFile); - fclose(pFile); - kompmgrpid = atoi(buffer); - } - - free(filename); - filename = NULL; - - if (tmp) - { - if (kompmgrpid) - { - kill(kompmgrpid, SIGUSR2); - } - else - { - stopKompmgr(); - TQTimer::singleShot( 200, this, TQT_SLOT(startKompmgr()) ); // wait some time to ensure system's ready for restart - } - } - else - { - if (kompmgrpid) - { - kill(kompmgrpid, SIGTERM); - } - else - { - stopKompmgr(); - } - } - } - } - -void Workspace::loadDesktopSettings() - { - KConfig* c = KGlobal::config(); - TQCString groupname; - if (screen_number == 0) - groupname = "Desktops"; - else - groupname.sprintf("Desktops-screen-%d", screen_number); - KConfigGroupSaver saver(c,groupname); - - int n = c->readNumEntry("Number", 4); - number_of_desktops = n; - delete workarea; - workarea = new TQRect[ n + 1 ]; - delete screenarea; - screenarea = NULL; - rootInfo->setNumberOfDesktops( number_of_desktops ); - desktop_focus_chain.resize( n ); - // make it +1, so that it can be accessed as [1..numberofdesktops] - focus_chain.resize( n + 1 ); - for(int i = 1; i <= n; i++) - { - TQString s = c->readEntry(TQString("Name_%1").arg(i), - i18n("Desktop %1").arg(i)); - rootInfo->setDesktopName( i, s.utf8().data() ); - desktop_focus_chain[i-1] = i; - } - } - -void Workspace::saveDesktopSettings() - { - KConfig* c = KGlobal::config(); - TQCString groupname; - if (screen_number == 0) - groupname = "Desktops"; - else - groupname.sprintf("Desktops-screen-%d", screen_number); - KConfigGroupSaver saver(c,groupname); - - c->writeEntry("Number", number_of_desktops ); - for(int i = 1; i <= number_of_desktops; i++) - { - TQString s = desktopName( i ); - TQString defaultvalue = i18n("Desktop %1").arg(i); - if ( s.isEmpty() ) - { - s = defaultvalue; - rootInfo->setDesktopName( i, s.utf8().data() ); - } - - if (s != defaultvalue) - { - c->writeEntry( TQString("Name_%1").arg(i), s ); - } - else - { - TQString currentvalue = c->readEntry(TQString("Name_%1").arg(i)); - if (currentvalue != defaultvalue) - c->writeEntry( TQString("Name_%1").arg(i), "" ); - } - } - } - -TQStringList Workspace::configModules(bool controlCenter) - { - TQStringList args; - args << "kde-twindecoration.desktop"; - if (controlCenter) - args << "kde-twinoptions.desktop"; - else if (kapp->authorizeControlModule("kde-twinoptions.desktop")) - args << "twinactions" << "twinfocus" << "twinmoving" << "twinadvanced" << "twinrules" << "twintranslucency"; - return args; - } - -void Workspace::configureWM() - { - KApplication::tdeinitExec( "kcmshell", configModules(false) ); - } - -/*! - avoids managing a window with title \a title - */ -void Workspace::doNotManage( TQString title ) - { - doNotManageList.append( title ); - } - -/*! - Hack for java applets - */ -bool Workspace::isNotManaged( const TQString& title ) - { - for ( TQStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) - { - TQRegExp r( (*it) ); - if (r.search(title) != -1) - { - doNotManageList.remove( it ); - return TRUE; - } - } - return FALSE; - } - -/*! - Refreshes all the client windows - */ -void Workspace::refresh() - { - TQWidget w; - w.setGeometry( TQApplication::desktop()->geometry() ); - w.show(); - w.hide(); - TQApplication::flushX(); - } - -/*! - During virt. desktop switching, desktop areas covered by windows that are - going to be hidden are first obscured by new windows with no background - ( i.e. transparent ) placed right below the windows. These invisible windows - are removed after the switch is complete. - Reduces desktop ( wallpaper ) repaints during desktop switching -*/ -class ObscuringWindows - { - public: - ~ObscuringWindows(); - void create( Client* c ); - private: - TQValueList<Window> obscuring_windows; - static TQValueList<Window>* cached; - static unsigned int max_cache_size; - }; - -TQValueList<Window>* ObscuringWindows::cached = 0; -unsigned int ObscuringWindows::max_cache_size = 0; - -void ObscuringWindows::create( Client* c ) - { - if( cached == 0 ) - cached = new TQValueList<Window>; - Window obs_win; - XWindowChanges chngs; - int mask = CWSibling | CWStackMode; - if( cached->count() > 0 ) - { - cached->remove( obs_win = cached->first()); - chngs.x = c->x(); - chngs.y = c->y(); - chngs.width = c->width(); - chngs.height = c->height(); - mask |= CWX | CWY | CWWidth | CWHeight; - } - else - { - XSetWindowAttributes a; - a.background_pixmap = None; - a.override_redirect = True; - obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(), - c->width(), c->height(), 0, CopyFromParent, InputOutput, - CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a ); - } - chngs.sibling = c->frameId(); - chngs.stack_mode = Below; - XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs ); - XMapWindow( qt_xdisplay(), obs_win ); - obscuring_windows.append( obs_win ); - } - -ObscuringWindows::~ObscuringWindows() - { - max_cache_size = TQMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1; - for( TQValueList<Window>::ConstIterator it = obscuring_windows.begin(); - it != obscuring_windows.end(); - ++it ) - { - XUnmapWindow( qt_xdisplay(), *it ); - if( cached->count() < max_cache_size ) - cached->prepend( *it ); - else - XDestroyWindow( qt_xdisplay(), *it ); - } - } - - -/*! - Sets the current desktop to \a new_desktop - - Shows/Hides windows according to the stacking order and finally - propages the new desktop to the world - */ -bool Workspace::setCurrentDesktop( int new_desktop ) - { - if (new_desktop < 1 || new_desktop > number_of_desktops ) - return false; - - closeActivePopup(); - ++block_focus; -// TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date - StackingUpdatesBlocker blocker( this ); - - int old_desktop = current_desktop; - if (new_desktop != current_desktop) - { - ++block_showing_desktop; - /* - optimized Desktop switching: unmapping done from back to front - mapping done from front to back => less exposure events - */ - Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop)); - - ObscuringWindows obs_wins; - - current_desktop = new_desktop; // change the desktop (so that Client::updateVisibility() works) - - 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.... - if (!desktopHasCompositing) { - // If composition is not in use then we can hide the old windows before showing the new ones - for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { - if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient ) - { - if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) { - obs_wins.create( *it ); - } - (*it)->updateVisibility(); - } - } - } - - rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing - - if( movingClient && !movingClient->isOnDesktop( new_desktop )) - movingClient->setDesktop( new_desktop ); - - for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { - if ( (*it)->isOnDesktop( new_desktop ) ) { - (*it)->updateVisibility(); - } - } - - if (desktopHasCompositing) { - // If composition is in use then we cannot hide the old windows before showing the new ones, - // unless you happen to like the "flicker annoyingly to desktop" effect... :-P - XSync( qt_xdisplay(), false); // Make absolutely certain all new windows are shown before hiding the old ones - for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { - if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient ) - { - if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) { - obs_wins.create( *it ); - } - (*it)->updateVisibility(); - } - } - } - - --block_showing_desktop; - if( showingDesktop()) // do this only after desktop change to avoid flicker - resetShowingDesktop( false ); - } - - // restore the focus on this desktop - --block_focus; - Client* c = 0; - - if ( options->focusPolicyIsReasonable()) - { - // Search in focus chain - if ( movingClient != NULL && active_client == movingClient - && focus_chain[currentDesktop()].contains( active_client ) - && active_client->isShown( true ) && active_client->isOnCurrentDesktop()) - { - c = active_client; // the requestFocus below will fail, as the client is already active - } - if ( !c ) - { - for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast(); - it != focus_chain[currentDesktop()].end(); - --it ) - { - if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop()) - { - c = *it; - break; - } - } - } - } - - //if "unreasonable focus policy" - // and active_client is on_all_desktops and under mouse (hence == old_active_client), - // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>) - else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop()) - c = active_client; - - if( c == NULL && !desktops.isEmpty()) - c = findDesktop( true, currentDesktop()); - - if( c != active_client ) - setActiveClient( NULL, Allowed ); - - if ( c ) - requestFocus( c ); - else - focusToNull(); - - updateCurrentTopMenu(); - - // Update focus chain: - // If input: chain = { 1, 2, 3, 4 } and current_desktop = 3, - // Output: chain = { 3, 1, 2, 4 }. -// kdDebug(1212) << TQString("Switching to desktop #%1, at focus_chain[currentDesktop()] index %2\n") -// .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() )); - for( int i = desktop_focus_chain.find( currentDesktop() ); i > 0; i-- ) - desktop_focus_chain[i] = desktop_focus_chain[i-1]; - desktop_focus_chain[0] = currentDesktop(); - -// TQString s = "desktop_focus_chain[] = { "; -// for( uint i = 0; i < desktop_focus_chain.size(); i++ ) -// s += TQString::number(desktop_focus_chain[i]) + ", "; -// kdDebug(1212) << s << "}\n"; - - if( old_desktop != 0 ) // not for the very first time - popupinfo->showInfo( desktopName(currentDesktop()) ); - return true; - } - -// called only from DCOP -void Workspace::nextDesktop() - { - int desktop = currentDesktop() + 1; - setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop); - } - -// called only from DCOP -void Workspace::previousDesktop() - { - int desktop = currentDesktop() - 1; - setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops()); - } - -int Workspace::desktopToRight( int desktop ) const - { - int x,y; - calcDesktopLayout(x,y); - int dt = desktop-1; - if (layoutOrientation == Qt::Vertical) - { - dt += y; - if ( dt >= numberOfDesktops() ) - { - if ( options->rollOverDesktops ) - dt -= numberOfDesktops(); - else - return desktop; - } - } - else - { - int d = (dt % x) + 1; - if ( d >= x ) - { - if ( options->rollOverDesktops ) - d -= x; - else - return desktop; - } - dt = dt - (dt % x) + d; - } - return dt+1; - } - -int Workspace::desktopToLeft( int desktop ) const - { - int x,y; - calcDesktopLayout(x,y); - int dt = desktop-1; - if (layoutOrientation == Qt::Vertical) - { - dt -= y; - if ( dt < 0 ) - { - if ( options->rollOverDesktops ) - dt += numberOfDesktops(); - else - return desktop; - } - } - else - { - int d = (dt % x) - 1; - if ( d < 0 ) - { - if ( options->rollOverDesktops ) - d += x; - else - return desktop; - } - dt = dt - (dt % x) + d; - } - return dt+1; - } - -int Workspace::desktopUp( int desktop ) const - { - int x,y; - calcDesktopLayout(x,y); - int dt = desktop-1; - if (layoutOrientation == Qt::Horizontal) - { - dt -= x; - if ( dt < 0 ) - { - if ( options->rollOverDesktops ) - dt += numberOfDesktops(); - else - return desktop; - } - } - else - { - int d = (dt % y) - 1; - if ( d < 0 ) - { - if ( options->rollOverDesktops ) - d += y; - else - return desktop; - } - dt = dt - (dt % y) + d; - } - return dt+1; - } - -int Workspace::desktopDown( int desktop ) const - { - int x,y; - calcDesktopLayout(x,y); - int dt = desktop-1; - if (layoutOrientation == Qt::Horizontal) - { - dt += x; - if ( dt >= numberOfDesktops() ) - { - if ( options->rollOverDesktops ) - dt -= numberOfDesktops(); - else - return desktop; - } - } - else - { - int d = (dt % y) + 1; - if ( d >= y ) - { - if ( options->rollOverDesktops ) - d -= y; - else - return desktop; - } - dt = dt - (dt % y) + d; - } - return dt+1; - } - - -/*! - Sets the number of virtual desktops to \a n - */ -void Workspace::setNumberOfDesktops( int n ) - { - if ( n == number_of_desktops ) - return; - int old_number_of_desktops = number_of_desktops; - number_of_desktops = n; - - if( currentDesktop() > numberOfDesktops()) - setCurrentDesktop( numberOfDesktops()); - - // if increasing the number, do the resizing now, - // otherwise after the moving of windows to still existing desktops - if( old_number_of_desktops < number_of_desktops ) - { - rootInfo->setNumberOfDesktops( number_of_desktops ); - NETPoint* viewports = new NETPoint[ number_of_desktops ]; - rootInfo->setDesktopViewport( number_of_desktops, *viewports ); - delete[] viewports; - updateClientArea( true ); - focus_chain.resize( number_of_desktops + 1 ); - } - - // if the number of desktops decreased, move all - // windows that would be hidden to the last visible desktop - if( old_number_of_desktops > number_of_desktops ) - { - for( ClientList::ConstIterator it = clients.begin(); - it != clients.end(); - ++it) - { - if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops()) - sendClientToDesktop( *it, numberOfDesktops(), true ); - } - } - if( old_number_of_desktops > number_of_desktops ) - { - rootInfo->setNumberOfDesktops( number_of_desktops ); - NETPoint* viewports = new NETPoint[ number_of_desktops ]; - rootInfo->setDesktopViewport( number_of_desktops, *viewports ); - delete[] viewports; - updateClientArea( true ); - focus_chain.resize( number_of_desktops + 1 ); - } - - saveDesktopSettings(); - - // Resize and reset the desktop focus chain. - desktop_focus_chain.resize( n ); - for( int i = 0; i < (int)desktop_focus_chain.size(); i++ ) - desktop_focus_chain[i] = i+1; - } - -/*! - Sends client \a c to desktop \a desk. - - Takes care of transients as well. - */ -void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) - { - bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops(); - c->setDesktop( desk ); - if ( c->desktop() != desk ) // no change or desktop forced - return; - desk = c->desktop(); // Client did range checking - - if ( c->isOnDesktop( currentDesktop() ) ) - { - if ( c->wantsTabFocus() && options->focusPolicyIsReasonable() - && !was_on_desktop // for stickyness changes - && !dont_activate ) - requestFocus( c ); - else - restackClientUnderActive( c ); - } - else - { - raiseClient( c ); - } - - ClientList transients_stacking_order = ensureStackingOrder( c->transients()); - for( ClientList::ConstIterator it = transients_stacking_order.begin(); - it != transients_stacking_order.end(); - ++it ) - sendClientToDesktop( *it, desk, dont_activate ); - updateClientArea(); - } - -int Workspace::numScreens() const - { - if( !options->xineramaEnabled ) - return 0; - return tqApp->desktop()->numScreens(); - } - -int Workspace::activeScreen() const - { - if( !options->xineramaEnabled ) - return 0; - if( !options->activeMouseScreen ) - { - if( activeClient() != NULL && !activeClient()->isOnScreen( active_screen )) - return tqApp->desktop()->screenNumber( activeClient()->geometry().center()); - return active_screen; - } - return tqApp->desktop()->screenNumber( TQCursor::pos()); - } - -// check whether a client moved completely out of what's considered the active screen, -// if yes, set a new active screen -void Workspace::checkActiveScreen( const Client* c ) - { - if( !options->xineramaEnabled ) - return; - if( !c->isActive()) - return; - if( !c->isOnScreen( active_screen )) - active_screen = c->screen(); - } - -// called e.g. when a user clicks on a window, set active screen to be the screen -// where the click occured -void Workspace::setActiveScreenMouse( TQPoint mousepos ) - { - if( !options->xineramaEnabled ) - return; - active_screen = tqApp->desktop()->screenNumber( mousepos ); - } - -TQRect Workspace::screenGeometry( int screen ) const - { - if (( !options->xineramaEnabled ) || (kapp->desktop()->numScreens() < 2)) - return tqApp->desktop()->geometry(); - return tqApp->desktop()->screenGeometry( screen ); - } - -int Workspace::screenNumber( TQPoint pos ) const - { - if( !options->xineramaEnabled ) - return 0; - return tqApp->desktop()->screenNumber( pos ); - } - -void Workspace::sendClientToScreen( Client* c, int screen ) - { - if( c->screen() == screen ) // don't use isOnScreen(), that's true even when only partially - return; - GeometryUpdatesPostponer blocker( c ); - TQRect old_sarea = clientArea( MaximizeArea, c ); - TQRect sarea = clientArea( MaximizeArea, screen, c->desktop()); - c->setGeometry( sarea.x() - old_sarea.x() + c->x(), sarea.y() - old_sarea.y() + c->y(), - c->size().width(), c->size().height()); - c->checkWorkspacePosition(); - ClientList transients_stacking_order = ensureStackingOrder( c->transients()); - for( ClientList::ConstIterator it = transients_stacking_order.begin(); - it != transients_stacking_order.end(); - ++it ) - sendClientToScreen( *it, screen ); - if( c->isActive()) - active_screen = screen; - } - - -void Workspace::setDesktopLayout( int, int, int ) - { // DCOP-only, unused - } - -void Workspace::updateDesktopLayout() - { - // rootInfo->desktopLayoutCorner(); // I don't find this worth bothering, feel free to - layoutOrientation = ( rootInfo->desktopLayoutOrientation() == NET::OrientationHorizontal - ? Qt::Horizontal : Qt::Vertical ); - layoutX = rootInfo->desktopLayoutColumnsRows().width(); - layoutY = rootInfo->desktopLayoutColumnsRows().height(); - if( layoutX == 0 && layoutY == 0 ) // not given, set default layout - layoutY = 2; - } - -void Workspace::calcDesktopLayout(int &x, int &y) const - { - x = layoutX; // <= 0 means compute it from the other and total number of desktops - y = layoutY; - if((x <= 0) && (y > 0)) - x = (numberOfDesktops()+y-1) / y; - else if((y <=0) && (x > 0)) - y = (numberOfDesktops()+x-1) / x; - - if(x <=0) - x = 1; - if (y <= 0) - y = 1; - } - -/*! - Check whether \a w is a system tray window. If so, add it to the respective - datastructures and propagate it to the world. - */ -bool Workspace::addSystemTrayWin( WId w ) - { - if ( systemTrayWins.contains( w ) ) - return TRUE; - - NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor ); - WId trayWinFor = ni.kdeSystemTrayWinFor(); - if ( !trayWinFor ) - return FALSE; - systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) ); - XSelectInput( qt_xdisplay(), w, - StructureNotifyMask - ); - XAddToSaveSet( qt_xdisplay(), w ); - propagateSystemTrayWins(); - return TRUE; - } - -/*! - Check whether \a w is a system tray window. If so, remove it from - the respective datastructures and propagate this to the world. - */ -bool Workspace::removeSystemTrayWin( WId w, bool check ) - { - if ( !systemTrayWins.contains( w ) ) - return FALSE; - if( check ) - { - // When getting UnmapNotify, it's not clear if it's the systray - // reparenting the window into itself, or if it's the window - // going away. This is obviously a flaw in the design, and we were - // just lucky it worked for so long. Kicker's systray temporarily - // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while - // embedding it, allowing KWin to figure out. Kicker just mustn't - // crash before removing it again ... *shrug* . - int num_props; - Atom* props = XListProperties( qt_xdisplay(), w, &num_props ); - if( props != NULL ) - { - for( int i = 0; - i < num_props; - ++i ) - if( props[ i ] == atoms->kde_system_tray_embedding ) - { - XFree( props ); - return false; - } - XFree( props ); - } - } - systemTrayWins.remove( w ); - XRemoveFromSaveSet (qt_xdisplay (), w); - propagateSystemTrayWins(); - return TRUE; - } - - -/*! - Propagates the systemTrayWins to the world - */ -void Workspace::propagateSystemTrayWins() - { - Window *cl = new Window[ systemTrayWins.count()]; - - int i = 0; - for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) - { - cl[i++] = (*it).win; - } - - rootInfo->setKDESystemTrayWindows( cl, i ); - delete [] cl; - } - - -void Workspace::killWindowId( Window window_to_kill ) - { - if( window_to_kill == None ) - return; - Window window = window_to_kill; - Client* client = NULL; - for(;;) - { - client = findClient( FrameIdMatchPredicate( window )); - if( client != NULL ) // found the client - break; - Window parent, root; - Window* children; - unsigned int children_count; - XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count ); - if( children != NULL ) - XFree( children ); - if( window == root ) // we didn't find the client, probably an override-redirect window - break; - window = parent; // go up - } - if( client != NULL ) - client->killWindow(); - else - XKillClient( qt_xdisplay(), window_to_kill ); - } - - -void Workspace::sendPingToWindow( Window window, Time timestamp ) - { - rootInfo->sendPing( window, timestamp ); - } - -void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags ) - { - rootInfo->takeActivity( c->window(), timestamp, flags ); - pending_take_activity = c; - } - - -/*! - Takes a screenshot of the current window and puts it in the clipboard. -*/ -void Workspace::slotGrabWindow() - { - if ( active_client ) - { - TQPixmap snapshot = TQPixmap::grabWindow( active_client->frameId() ); - - //No XShape - no work. - if( Shape::available()) - { - //As the first step, get the mask from XShape. - int count, order; - XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(), - ShapeBounding, &count, &order); - //The ShapeBounding region is the outermost tqshape of the window; - //ShapeBounding - ShapeClipping is defined to be the border. - //Since the border area is part of the window, we use bounding - // to limit our work region - if (rects) - { - //Create a TQRegion from the rectangles describing the bounding mask. - TQRegion contents; - for (int pos = 0; pos < count; pos++) - contents += TQRegion(rects[pos].x, rects[pos].y, - rects[pos].width, rects[pos].height); - XFree(rects); - - //Create the bounding box. - TQRegion bbox(0, 0, snapshot.width(), snapshot.height()); - - //Get the masked away area. - TQRegion maskedAway = bbox - contents; - TQMemArray<TQRect> maskedAwayRects = maskedAway.tqrects(); - - //Construct a bitmap mask from the rectangles - TQBitmap mask( snapshot.width(), snapshot.height()); - TQPainter p(&mask); - p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1); - for (uint pos = 0; pos < maskedAwayRects.count(); pos++) - p.fillRect(maskedAwayRects[pos], Qt::color0); - p.end(); - snapshot.setMask(mask); - } - } - - TQClipboard *cb = TQApplication::tqclipboard(); - cb->setPixmap( snapshot ); - } - else - slotGrabDesktop(); - } - -/*! - Takes a screenshot of the whole desktop and puts it in the clipboard. -*/ -void Workspace::slotGrabDesktop() - { - TQPixmap p = TQPixmap::grabWindow( qt_xrootwin() ); - TQClipboard *cb = TQApplication::tqclipboard(); - cb->setPixmap( p ); - } - - -/*! - Invokes keyboard mouse emulation - */ -void Workspace::slotMouseEmulation() - { - - if ( mouse_emulation ) - { - XUngrabKeyboard(qt_xdisplay(), GET_QT_X_TIME()); - mouse_emulation = FALSE; - return; - } - - if ( XGrabKeyboard(qt_xdisplay(), - root, FALSE, - GrabModeAsync, GrabModeAsync, - GET_QT_X_TIME()) == GrabSuccess ) - { - mouse_emulation = TRUE; - mouse_emulation_state = 0; - mouse_emulation_window = 0; - } - } - -/*! - Returns the child window under the mouse and activates the - respective client if necessary. - - Auxiliary function for the mouse emulation system. - */ -WId Workspace::getMouseEmulationWindow() - { - Window root; - Window child = qt_xrootwin(); - int root_x, root_y, lx, ly; - uint state; - Window w; - Client * c = 0; - do - { - w = child; - if (!c) - c = findClient( FrameIdMatchPredicate( w )); - XQueryPointer( qt_xdisplay(), w, &root, &child, - &root_x, &root_y, &lx, &ly, &state ); - } while ( child != None && child != w ); - - if ( c && !c->isActive() ) - activateClient( c ); - return (WId) w; - } - -/*! - Sends a faked mouse event to the specified window. Returns the new button state. - */ -unsigned int Workspace::sendFakedMouseEvent( TQPoint pos, WId w, MouseEmulation type, int button, unsigned int state ) - { - if ( !w ) - return state; - TQWidget* widget = TQWidget::find( w ); - if ( (!widget || widget->inherits(TQTOOLBUTTON_OBJECT_NAME_STRING) ) && !findClient( WindowMatchPredicate( w )) ) - { - int x, y; - Window xw; - XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw ); - if ( type == EmuMove ) - { // motion notify events - XEvent e; - e.type = MotionNotify; - e.xmotion.window = w; - e.xmotion.root = qt_xrootwin(); - e.xmotion.subwindow = w; - e.xmotion.time = GET_QT_X_TIME(); - e.xmotion.x = x; - e.xmotion.y = y; - e.xmotion.x_root = pos.x(); - e.xmotion.y_root = pos.y(); - e.xmotion.state = state; - e.xmotion.is_hint = NotifyNormal; - XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, &e ); - } - else - { - XEvent e; - e.type = type == EmuRelease ? ButtonRelease : ButtonPress; - e.xbutton.window = w; - e.xbutton.root = qt_xrootwin(); - e.xbutton.subwindow = w; - e.xbutton.time = GET_QT_X_TIME(); - e.xbutton.x = x; - e.xbutton.y = y; - e.xbutton.x_root = pos.x(); - e.xbutton.y_root = pos.y(); - e.xbutton.state = state; - e.xbutton.button = button; - XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, &e ); - - if ( type == EmuPress ) - { - switch ( button ) - { - case 2: - state |= Button2Mask; - break; - case 3: - state |= Button3Mask; - break; - default: // 1 - state |= Button1Mask; - break; - } - } - else - { - switch ( button ) - { - case 2: - state &= ~Button2Mask; - break; - case 3: - state &= ~Button3Mask; - break; - default: // 1 - state &= ~Button1Mask; - break; - } - } - } - } - return state; - } - -/*! - Handles keypress event during mouse emulation - */ -bool Workspace::keyPressMouseEmulation( XKeyEvent& ev ) - { - if ( root != qt_xrootwin() ) - return FALSE; - int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0); - int km = ev.state & (ControlMask | Mod1Mask | ShiftMask); - - bool is_control = km & ControlMask; - bool is_alt = km & Mod1Mask; - bool is_shift = km & ShiftMask; - int delta = is_control?1:is_alt?32:8; - TQPoint pos = TQCursor::pos(); - - switch ( kc ) - { - case XK_Left: - case XK_KP_Left: - pos.rx() -= delta; - break; - case XK_Right: - case XK_KP_Right: - pos.rx() += delta; - break; - case XK_Up: - case XK_KP_Up: - pos.ry() -= delta; - break; - case XK_Down: - case XK_KP_Down: - pos.ry() += delta; - break; - case XK_F1: - if ( !mouse_emulation_state ) - mouse_emulation_window = getMouseEmulationWindow(); - if ( (mouse_emulation_state & Button1Mask) == 0 ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); - if ( !is_shift ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); - break; - case XK_F2: - if ( !mouse_emulation_state ) - mouse_emulation_window = getMouseEmulationWindow(); - if ( (mouse_emulation_state & Button2Mask) == 0 ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state ); - if ( !is_shift ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); - break; - case XK_F3: - if ( !mouse_emulation_state ) - mouse_emulation_window = getMouseEmulationWindow(); - if ( (mouse_emulation_state & Button3Mask) == 0 ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state ); - if ( !is_shift ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); - break; - case XK_Return: - case XK_space: - case XK_KP_Enter: - case XK_KP_Space: - { - if ( !mouse_emulation_state ) - { - // nothing was pressed, fake a LMB click - mouse_emulation_window = getMouseEmulationWindow(); - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); - } - else - { // release all - if ( mouse_emulation_state & Button1Mask ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); - if ( mouse_emulation_state & Button2Mask ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); - if ( mouse_emulation_state & Button3Mask ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); - } - } - // fall through - case XK_Escape: - XUngrabKeyboard(qt_xdisplay(), GET_QT_X_TIME()); - mouse_emulation = FALSE; - return TRUE; - default: - return FALSE; - } - - TQCursor::setPos( pos ); - if ( mouse_emulation_state ) - mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0, mouse_emulation_state ); - return TRUE; - - } - -/*! - Returns the workspace's desktop widget. The desktop widget is - sometimes required by clients to draw on it, for example outlines on - moving or resizing. - */ -TQWidget* Workspace::desktopWidget() - { - return desktop_widget; - } - -//Delayed focus functions -void Workspace::delayFocus() - { - requestFocus( delayfocus_client ); - cancelDelayFocus(); - } - -void Workspace::requestDelayFocus( Client* c ) - { - delayfocus_client = c; - delete delayFocusTimer; - delayFocusTimer = new TQTimer( this ); - connect( delayFocusTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( delayFocus() ) ); - delayFocusTimer->start( options->delayFocusInterval, TRUE ); - } - -void Workspace::cancelDelayFocus() - { - delete delayFocusTimer; - delayFocusTimer = 0; - } - -// Electric Borders -//========================================================================// -// Electric Border Window management. Electric borders allow a user -// to change the virtual desktop by moving the mouse pointer to the -// borders. Technically this is done with input only windows. Since -// electric borders can be switched on and off, we have these two -// functions to create and destroy them. -void Workspace::checkElectricBorders( bool force ) - { - if( force ) - destroyBorderWindows(); - - electric_current_border = 0; - - TQRect r = TQApplication::desktop()->geometry(); - electricTop = r.top(); - electricBottom = r.bottom(); - electricLeft = r.left(); - electricRight = r.right(); - - if (options->electricBorders() == Options::ElectricAlways) - createBorderWindows(); - else - destroyBorderWindows(); - } - -void Workspace::createBorderWindows() - { - if ( electric_have_borders ) - return; - - electric_have_borders = true; - - TQRect r = TQApplication::desktop()->geometry(); - XSetWindowAttributes attributes; - unsigned long valuemask; - attributes.override_redirect = True; - attributes.event_mask = ( EnterWindowMask | LeaveWindowMask ); - valuemask= (CWOverrideRedirect | CWEventMask | CWCursor ); - attributes.cursor = XCreateFontCursor(qt_xdisplay(), - XC_sb_up_arrow); - electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), - 0,0, - r.width(),1, - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(qt_xdisplay(), electric_top_border); - - attributes.cursor = XCreateFontCursor(qt_xdisplay(), - XC_sb_down_arrow); - electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), - 0,r.height()-1, - r.width(),1, - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(qt_xdisplay(), electric_bottom_border); - - attributes.cursor = XCreateFontCursor(qt_xdisplay(), - XC_sb_left_arrow); - electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), - 0,0, - 1,r.height(), - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(qt_xdisplay(), electric_left_border); - - attributes.cursor = XCreateFontCursor(qt_xdisplay(), - XC_sb_right_arrow); - electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), - r.width()-1,0, - 1,r.height(), - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(qt_xdisplay(), electric_right_border); - // Set XdndAware on the windows, so that DND enter events are received (#86998) - Atom version = 4; // XDND version - XChangeProperty( qt_xdisplay(), electric_top_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - XChangeProperty( qt_xdisplay(), electric_bottom_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - XChangeProperty( qt_xdisplay(), electric_left_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - XChangeProperty( qt_xdisplay(), electric_right_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - } - - -// Electric Border Window management. Electric borders allow a user -// to change the virtual desktop by moving the mouse pointer to the -// borders. Technically this is done with input only windows. Since -// electric borders can be switched on and off, we have these two -// functions to create and destroy them. -void Workspace::destroyBorderWindows() - { - if( !electric_have_borders) - return; - - electric_have_borders = false; - - if(electric_top_border) - XDestroyWindow(qt_xdisplay(),electric_top_border); - if(electric_bottom_border) - XDestroyWindow(qt_xdisplay(),electric_bottom_border); - if(electric_left_border) - XDestroyWindow(qt_xdisplay(),electric_left_border); - if(electric_right_border) - XDestroyWindow(qt_xdisplay(),electric_right_border); - - electric_top_border = None; - electric_bottom_border = None; - electric_left_border = None; - electric_right_border = None; - } - -void Workspace::clientMoved(const TQPoint &pos, Time now) - { - if (options->electricBorders() == Options::ElectricDisabled) - return; - - if ((pos.x() != electricLeft) && - (pos.x() != electricRight) && - (pos.y() != electricTop) && - (pos.y() != electricBottom)) - return; - - Time treshold_set = options->electricBorderDelay(); // set timeout - Time treshold_reset = 250; // reset timeout - int distance_reset = 30; // Mouse should not move more than this many pixels - - int border = 0; - if (pos.x() == electricLeft) - border = 1; - else if (pos.x() == electricRight) - border = 2; - else if (pos.y() == electricTop) - border = 3; - else if (pos.y() == electricBottom) - border = 4; - - if ((electric_current_border == border) && - (timestampDiff(electric_time_last, now) < treshold_reset) && - ((pos-electric_push_point).manhattanLength() < distance_reset)) - { - electric_time_last = now; - - if (timestampDiff(electric_time_first, now) > treshold_set) - { - electric_current_border = 0; - - TQRect r = TQApplication::desktop()->geometry(); - int offset; - - int desk_before = currentDesktop(); - switch(border) - { - case 1: - slotSwitchDesktopLeft(); - if (currentDesktop() != desk_before) - { - offset = r.width() / 5; - TQCursor::setPos(r.width() - offset, pos.y()); - } - break; - - case 2: - slotSwitchDesktopRight(); - if (currentDesktop() != desk_before) - { - offset = r.width() / 5; - TQCursor::setPos(offset, pos.y()); - } - break; - - case 3: - slotSwitchDesktopUp(); - if (currentDesktop() != desk_before) - { - offset = r.height() / 5; - TQCursor::setPos(pos.x(), r.height() - offset); - } - break; - - case 4: - slotSwitchDesktopDown(); - if (currentDesktop() != desk_before) - { - offset = r.height() / 5; - TQCursor::setPos(pos.x(), offset); - } - break; - } - return; - } - } - else - { - electric_current_border = border; - electric_time_first = now; - electric_time_last = now; - electric_push_point = pos; - } - - int mouse_warp = 1; - - // reset the pointer to find out wether the user is really pushing - switch( border) - { - case 1: TQCursor::setPos(pos.x()+mouse_warp, pos.y()); break; - case 2: TQCursor::setPos(pos.x()-mouse_warp, pos.y()); break; - case 3: TQCursor::setPos(pos.x(), pos.y()+mouse_warp); break; - case 4: TQCursor::setPos(pos.x(), pos.y()-mouse_warp); break; - } - } - -// this function is called when the user entered an electric border -// with the mouse. It may switch to another virtual desktop -bool Workspace::electricBorder(XEvent *e) - { - if( !electric_have_borders ) - return false; - if( e->type == EnterNotify ) - { - if( e->xcrossing.window == electric_top_border || - e->xcrossing.window == electric_left_border || - e->xcrossing.window == electric_bottom_border || - e->xcrossing.window == electric_right_border) - // the user entered an electric border - { - clientMoved( TQPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time ); - return true; - } - } - if( e->type == ClientMessage ) - { - if( e->xclient.message_type == atoms->xdnd_position - && ( e->xclient.window == electric_top_border - || e->xclient.window == electric_bottom_border - || e->xclient.window == electric_left_border - || e->xclient.window == electric_right_border )) - { - updateXTime(); - clientMoved( TQPoint( e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), GET_QT_X_TIME() ); - return true; - } - } - return false; - } - -// electric borders (input only windows) have to be always on the -// top. For that reason kwm calls this function always after some -// windows have been raised. -void Workspace::raiseElectricBorders() - { - - if(electric_have_borders) - { - XRaiseWindow(qt_xdisplay(), electric_top_border); - XRaiseWindow(qt_xdisplay(), electric_left_border); - XRaiseWindow(qt_xdisplay(), electric_bottom_border); - XRaiseWindow(qt_xdisplay(), electric_right_border); - } - } - -void Workspace::addTopMenu( Client* c ) - { - assert( c->isTopMenu()); - assert( !topmenus.contains( c )); - topmenus.append( c ); - if( managingTopMenus()) - { - int minsize = c->minSize().height(); - if( minsize > topMenuHeight()) - { - topmenu_height = minsize; - updateTopMenuGeometry(); - } - updateTopMenuGeometry( c ); - updateCurrentTopMenu(); - } -// kdDebug() << "NEW TOPMENU:" << c << endl; - } - -void Workspace::removeTopMenu( Client* c ) - { -// if( c->isTopMenu()) -// kdDebug() << "REMOVE TOPMENU:" << c << endl; - assert( c->isTopMenu()); - assert( topmenus.contains( c )); - topmenus.remove( c ); - updateCurrentTopMenu(); - // TODO reduce topMenuHeight() if possible? - } - -void Workspace::lostTopMenuSelection() - { -// kdDebug() << "lost TopMenu selection" << endl; - // make sure this signal is always set when not owning the selection - disconnect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); - connect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); - if( !managing_topmenus ) - return; - connect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); - disconnect( topmenu_selection, TQT_SIGNAL( lostOwnership()), this, TQT_SLOT( lostTopMenuSelection())); - managing_topmenus = false; - delete topmenu_space; - topmenu_space = NULL; - updateClientArea(); - for( ClientList::ConstIterator it = topmenus.begin(); - it != topmenus.end(); - ++it ) - (*it)->checkWorkspacePosition(); - } - -void Workspace::lostTopMenuOwner() - { - if( !options->topMenuEnabled()) - return; -// kdDebug() << "TopMenu selection lost owner" << endl; - if( !topmenu_selection->claim( false )) - { -// kdDebug() << "Failed to claim TopMenu selection" << endl; - return; - } -// kdDebug() << "claimed TopMenu selection" << endl; - setupTopMenuHandling(); - } - -void Workspace::setupTopMenuHandling() - { - if( managing_topmenus ) - return; - connect( topmenu_selection, TQT_SIGNAL( lostOwnership()), this, TQT_SLOT( lostTopMenuSelection())); - disconnect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); - managing_topmenus = true; - topmenu_space = new TQWidget; - Window stack[ 2 ]; - stack[ 0 ] = supportWindow->winId(); - stack[ 1 ] = topmenu_space->winId(); - XRestackWindows(qt_xdisplay(), stack, 2); - updateTopMenuGeometry(); - topmenu_space->show(); - updateClientArea(); - updateCurrentTopMenu(); - } - -int Workspace::topMenuHeight() const - { - if( topmenu_height == 0 ) - { // simply create a dummy menubar and use its preffered height as the menu height - KMenuBar tmpmenu; - tmpmenu.insertItem( "dummy" ); - topmenu_height = tmpmenu.tqsizeHint().height(); - } - return topmenu_height; - } - -KDecoration* Workspace::createDecoration( KDecorationBridge* bridge ) - { - return mgr->createDecoration( bridge ); - } - -TQString Workspace::desktopName( int desk ) const - { - return TQString::fromUtf8( rootInfo->desktopName( desk ) ); - } - -bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data ) - { - return startup->checkStartup( w, id, data ) == KStartupInfo::Match; - } - -/*! - Puts the focus on a dummy window - Just using XSetInputFocus() with None would block keyboard input - */ -void Workspace::focusToNull() - { - XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, GET_QT_X_TIME() ); - } - -void Workspace::helperDialog( const TQString& message, const Client* c ) - { - TQStringList args; - TQString type; - if( message == "noborderaltf3" ) - { - TQString shortcut = TQString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) - .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); - args << "--msgbox" << - i18n( "You have selected to show a window without its border.\n" - "Without the border, you will not be able to enable the border " - "again using the mouse: use the window operations menu instead, " - "activated using the %1 keyboard shortcut." ) - .arg( shortcut ); - type = "altf3warning"; - } - else if( message == "fullscreenaltf3" ) - { - TQString shortcut = TQString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) - .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); - args << "--msgbox" << - i18n( "You have selected to show a window in fullscreen mode.\n" - "If the application itself does not have an option to turn the fullscreen " - "mode off you will not be able to disable it " - "again using the mouse: use the window operations menu instead, " - "activated using the %1 keyboard shortcut." ) - .arg( shortcut ); - type = "altf3warning"; - } - else - assert( false ); - KProcess proc; - proc << "kdialog" << args; - if( !type.isEmpty()) - { - KConfig cfg( "twin_dialogsrc" ); - cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox - if( !cfg.readBoolEntry( type, true )) // has don't show again checked - return; // save launching kdialog - proc << "--dontagain" << "twin_dialogsrc:" + type; - } - if( c != NULL ) - proc << "--embed" << TQString::number( c->window()); - proc.start( KProcess::DontCare ); - } - - -// kompmgr stuff - -void Workspace::startKompmgr() -{ - // See if the desktop is loaded yet - Atom type; - int format; - unsigned long length, after; - unsigned char* data_root; - Atom prop_root; - prop_root = XInternAtom(qt_xdisplay(), "_XROOTPMAP_ID", False); - if( XGetWindowProperty( qt_xdisplay(), qt_xrootwin(), prop_root, 0L, 1L, False, AnyPropertyType, &type, &format, &length, &after, &data_root) == Success && data_root != NULL ) { - // Root pixmap is available; OK to load... - } - else { - // Try again a bit later! - TQTimer::singleShot( 200, this, TQT_SLOT(startKompmgr()) ); - return; - } - if (!kompmgr || kompmgr->isRunning()) - return; - if (!kompmgr->start(KProcess::OwnGroup, KProcess::Stderr)) - { - options->useTranslucency = FALSE; - KProcess proc; - proc << "kdialog" << "--error" - << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.") - << "--title" << "Composite Manager Failure"; - proc.start(KProcess::DontCare); - } - else - { - delete kompmgr_selection; - char selection_name[ 100 ]; - sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( qt_xdisplay())); - kompmgr_selection = new KSelectionOwner( selection_name ); - connect( kompmgr_selection, TQT_SIGNAL( lostOwnership()), TQT_SLOT( stopKompmgr())); - kompmgr_selection->claim( true ); - connect(kompmgr, TQT_SIGNAL(processExited(KProcess*)), TQT_SLOT(restartKompmgr(KProcess*))); - options->useTranslucency = TRUE; - //allowKompmgrRestart = FALSE; - //TQTimer::singleShot( 60000, this, TQT_SLOT(unblockKompmgrRestart()) ); - TQByteArray ba; - TQDataStream arg(ba, IO_WriteOnly); - arg << ""; - kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStarted()", ba); - } - if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider -} - -void Workspace::stopKompmgr() -{ - if (!kompmgr || !kompmgr->isRunning()) { - return; - } - delete kompmgr_selection; - kompmgr_selection = NULL; - kompmgr->disconnect(this, TQT_SLOT(restartKompmgr(KProcess*))); - options->useTranslucency = FALSE; - if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider - kompmgr->kill(); - TQByteArray ba; - TQDataStream arg(ba, IO_WriteOnly); - arg << ""; - kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStopped()", ba); -} - -bool Workspace::kompmgrIsRunning() -{ - return kompmgr && kompmgr->isRunning(); -} - -void Workspace::unblockKompmgrRestart() -{ - allowKompmgrRestart = TRUE; -} - -void Workspace::restartKompmgr( KProcess *proc ) -// this is for inernal purpose (crashhandling) only, usually you want to use workspace->stopKompmgr(); TQTimer::singleShot(200, workspace, TQT_SLOT(startKompmgr())); -{ - bool crashed; - if (proc->signalled()) { // looks like kompmgr may have crashed - int exit_signal_number = proc->exitSignal(); - 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) ) { - crashed = true; - } - else { - crashed = false; - } - if (!allowKompmgrRestart) // uh oh, it exited recently already - { - delete kompmgr_selection; - kompmgr_selection = NULL; - options->useTranslucency = FALSE; - if (crashed) { - KProcess proc; - proc << "kdialog" << "--error" - << i18n( "The Composite Manager crashed twice within a minute and is therefore disabled for this session.") - << "--title" << i18n("Composite Manager Failure"); - proc.start(KProcess::DontCare); - } - return; - } - if (!kompmgr) - return; -// this should be useless, i keep it for maybe future need -// if (!kcompmgr) -// { -// kompmgr = new KProcess; -// kompmgr->clearArguments(); -// *kompmgr << "kompmgr"; -// } -// ------------------- - if (!kompmgr->start(KProcess::NotifyOnExit, KProcess::Stderr)) - { - delete kompmgr_selection; - kompmgr_selection = NULL; - options->useTranslucency = FALSE; - KProcess proc; - proc << "kdialog" << "--error" - << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.") - << "--title" << i18n("Composite Manager Failure"); - proc.start(KProcess::DontCare); - } - else - { - allowKompmgrRestart = FALSE; - TQTimer::singleShot( 60000, this, TQT_SLOT(unblockKompmgrRestart()) ); - } - } -} - -void Workspace::handleKompmgrOutput( KProcess* , char *buffer, int buflen) -{ - TQString message; - TQString output = TQString::fromLocal8Bit( buffer, buflen ); - if (output.contains("Started",false)) - ; // don't do anything, just pass to the connection release - else if (output.contains("Can't open display",false)) - message = i18n("<qt><b>kompmgr failed to open the display</b><br>There is probably an invalid display entry in your ~/.xcompmgrrc.</qt>"); - else if (output.contains("No render extension",false)) - message = i18n("<qt><b>kompmgr cannot find the Xrender extension</b><br>You are using either an outdated or a crippled version of XOrg.<br>Get XOrg ≥ 6.8 from www.freedesktop.org.<br></qt>"); - else if (output.contains("No composite extension",false)) - message = i18n("<qt><b>Composite extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.<br>Additionally, you need to add a new section to your X config file:<br>" - "<i>Section \"Extensions\"<br>" - "Option \"Composite\" \"Enable\"<br>" - "EndSection</i></qt>"); - else if (output.contains("No damage extension",false)) - message = i18n("<qt><b>Damage extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt>"); - else if (output.contains("No XFixes extension",false)) - message = i18n("<qt><b>XFixes extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt>"); - else return; //skip others - // kompmgr startup failed or succeeded, release connection - kompmgr->closeStderr(); - disconnect(kompmgr, TQT_SIGNAL(receivedStderr(KProcess*, char*, int)), this, TQT_SLOT(handleKompmgrOutput(KProcess*, char*, int))); - if( !message.isEmpty()) - { - KProcess proc; - proc << "kdialog" << "--error" - << message - << "--title" << i18n("Composite Manager Failure"); - proc.start(KProcess::DontCare); - } -} - - -void Workspace::setOpacity(unsigned long winId, unsigned int opacityPercent) -{ - if (opacityPercent > 100) opacityPercent = 100; - for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) - if (winId == (*it)->window()) - { - (*it)->setOpacity(opacityPercent < 100, (unsigned int)((opacityPercent/100.0)*0xFFFFFFFF)); - return; - } -} - -void Workspace::setShadowSize(unsigned long winId, unsigned int shadowSizePercent) -{ - //this is open to the user by dcop - to avoid stupid trials, we limit the max shadow size to 400% - if (shadowSizePercent > 400) shadowSizePercent = 400; - for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) - if (winId == (*it)->window()) - { - (*it)->setShadowSize(shadowSizePercent); - return; - } -} - -void Workspace::setUnshadowed(unsigned long winId) -{ - for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) - if (winId == (*it)->window()) - { - (*it)->setShadowSize(0); - return; - } -} - -void Workspace::setShowingDesktop( bool showing ) - { - rootInfo->setShowingDesktop( showing ); - showing_desktop = showing; - ++block_showing_desktop; - if( showing_desktop ) - { - showing_desktop_clients.clear(); - ++block_focus; - ClientList cls = stackingOrder(); - // find them first, then minimize, otherwise transients may get minimized with the window - // they're transient for - for( ClientList::ConstIterator it = cls.begin(); - it != cls.end(); - ++it ) - { - if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow()) - showing_desktop_clients.prepend( *it ); // topmost first to reduce flicker - } - for( ClientList::ConstIterator it = showing_desktop_clients.begin(); - it != showing_desktop_clients.end(); - ++it ) - (*it)->minimize(true); - --block_focus; - if( Client* desk = findDesktop( true, currentDesktop())) - requestFocus( desk ); - } - else - { - for( ClientList::ConstIterator it = showing_desktop_clients.begin(); - it != showing_desktop_clients.end(); - ++it ) - (*it)->unminimize(true); - if( showing_desktop_clients.count() > 0 ) - requestFocus( showing_desktop_clients.first()); - showing_desktop_clients.clear(); - } - --block_showing_desktop; - } - -// Following Kicker's behavior: -// Changing a virtual desktop resets the state and shows the windows again. -// Unminimizing a window resets the state but keeps the windows hidden (except -// the one that was unminimized). -// A new window resets the state and shows the windows again, with the new window -// being active. Due to popular demand (#67406) by people who apparently -// don't see a difference between "show desktop" and "minimize all", this is not -// true if "showDesktopIsMinimizeAll" is set in twinrc. In such case showing -// a new window resets the state but doesn't show windows. -void Workspace::resetShowingDesktop( bool keep_hidden ) - { - if( block_showing_desktop > 0 ) - return; - rootInfo->setShowingDesktop( false ); - showing_desktop = false; - ++block_showing_desktop; - if( !keep_hidden ) - { - for( ClientList::ConstIterator it = showing_desktop_clients.begin(); - it != showing_desktop_clients.end(); - ++it ) - (*it)->unminimize(true); - } - showing_desktop_clients.clear(); - --block_showing_desktop; - } - -// Activating/deactivating this feature works like this: -// When nothing is active, and the shortcut is pressed, global shortcuts are disabled -// (using global_shortcuts_disabled) -// When a window that has disabling forced is activated, global shortcuts are disabled. -// (using global_shortcuts_disabled_for_client) -// When a shortcut is pressed and global shortcuts are disabled (either by a shortcut -// or for a client), they are enabled again. -void Workspace::slotDisableGlobalShortcuts() - { - if( global_shortcuts_disabled || global_shortcuts_disabled_for_client ) - disableGlobalShortcuts( false ); - else - disableGlobalShortcuts( true ); - } - -static bool pending_dfc = false; - -void Workspace::disableGlobalShortcutsForClient( bool disable ) - { - if( global_shortcuts_disabled_for_client == disable ) - return; - if( !global_shortcuts_disabled ) - { - if( disable ) - pending_dfc = true; - KIPC::sendMessageAll( KIPC::BlockShortcuts, disable ); - // twin will get the kipc message too - } - } - -void Workspace::disableGlobalShortcuts( bool disable ) - { - KIPC::sendMessageAll( KIPC::BlockShortcuts, disable ); - // twin will get the kipc message too - } - -void Workspace::kipcMessage( int id, int data ) - { - if( id != KIPC::BlockShortcuts ) - return; - if( pending_dfc && data ) - { - global_shortcuts_disabled_for_client = true; - pending_dfc = false; - } - else - { - global_shortcuts_disabled = data; - global_shortcuts_disabled_for_client = false; - } - // update also Alt+LMB actions etc. - for( ClientList::ConstIterator it = clients.begin(); - it != clients.end(); - ++it ) - (*it)->updateMouseGrab(); - } - -} // namespace - -#include "workspace.moc" |