diff --git a/kwin/client.cpp b/kwin/client.cpp index 080cb1cd63..a83aa2a148 100644 --- a/kwin/client.cpp +++ b/kwin/client.cpp @@ -1,3223 +1,3225 @@ /***************************************************************** kwin - the KDE window manager Copyright (C) 1999, 2000 Matthias Ettrich ******************************************************************/ //#define QT_CLEAN_NAMESPACE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "workspace.h" #include "client.h" #include "events.h" #include "atoms.h" #include #include #include #include #include #include #include // Needed for --enable-final // XIconincState is defined in workspace.cpp #ifndef IconicState #define IconicState XIconicState #endif namespace KWinInternal { // NET WM Protocol handler class class WinInfo : public NETWinInfo { public: WinInfo(KWinInternal::Client * c, Display * display, Window window, Window rwin, unsigned long pr ) : NETWinInfo( display, window, rwin, pr, NET::WindowManager ) { m_client = c; } virtual void changeDesktop(int desktop) { m_client->workspace()->sendClientToDesktop( m_client, desktop ); } virtual void changeState( unsigned long state, unsigned long mask ) { // state : kwin.h says: possible values are or'ed combinations of NET::Modal, // NET::Sticky, NET::MaxVert, NET::MaxHoriz, NET::Shaded, NET::SkipTaskbar, NET::SkipPager state &= mask; // for safety, clear all other bits if ( (mask & NET::Max) == NET::Max ) { m_client->maximizeRaw( state & NET::MaxVert, state & NET::MaxHoriz ); } else if ( mask & NET::MaxVert ) { m_client->maximizeRaw( state & NET::MaxVert, m_client->maximizeMode() & KWinInternal::Client::MaximizeHorizontal ); } else if ( mask & NET::MaxHoriz ) { m_client->maximizeRaw( m_client->maximizeMode() & KWinInternal::Client::MaximizeVertical, state & NET::MaxHoriz ); } if ( mask & NET::Shaded ) m_client->setShade( state & NET::Shaded ); if ( mask & NET::StaysOnTop) { m_client->setStaysOnTop( (state & NET::StaysOnTop) != 0 ); if ( m_client->staysOnTop() ) m_client->workspace()->raiseClient( m_client ); } if( mask & NET::SkipTaskbar ) m_client->setSkipTaskbar( ( state & NET::SkipTaskbar ) != 0 ); if( mask & NET::SkipPager ) m_client->setSkipPager( ( state & NET::SkipPager ) != 0 ); } private: KWinInternal::Client * m_client; }; class WindowWrapperPrivate { public: WindowWrapperPrivate() {}; }; class ClientPrivate { public: ClientPrivate() {}; }; }; // put all externs before the namespace statement to allow the linker // to resolve them properly extern Atom qt_wm_state; extern Time qt_x_time; extern Atom qt_window_role; extern Atom qt_sm_client_id; static int nullErrorHandler(Display *, XErrorEvent *) { return 0; } using namespace KWinInternal; static bool blockAnimation = FALSE; static QRect* visible_bound = 0; void Client::drawbound( const QRect& geom ) { if ( visible_bound ) *visible_bound = geom; else visible_bound = new QRect( geom ); QPainter p ( workspace()->desktopWidget() ); p.setPen( QPen( Qt::white, 5 ) ); p.setRasterOp( Qt::XorROP ); p.drawRect( geom ); } void Client::clearbound() { if ( !visible_bound ) return; drawbound( *visible_bound ); delete visible_bound; visible_bound = 0; } void Client::updateShape() { if ( shape() ) XShapeCombineShape(qt_xdisplay(), winId(), ShapeBounding, windowWrapper()->x(), windowWrapper()->y(), window(), ShapeBounding, ShapeSet); else XShapeCombineMask( qt_xdisplay(), winId(), ShapeBounding, 0, 0, None, ShapeSet); } static void sendClientMessage(Window w, Atom a, long x){ XEvent ev; long mask; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = x; ev.xclient.data.l[1] = qt_x_time; mask = 0L; if (w == qt_xrootwin()) mask = SubstructureRedirectMask; /* magic! */ XSendEvent(qt_xdisplay(), w, False, mask, &ev); } /*! \class WindowWrapper client.h \brief The WindowWrapper class encapsulates a client's managed window. There's not much to know about this class, it's completley handled by the abstract class Client. You get access to the window wrapper with Client::windowWrapper(). The big advantage of WindowWrapper is, that you can use just like a normal QWidget, although it encapsulates an X window that belongs to another application. In particular, this means adding a client's windowWrapper() to a QLayout for the geometry management. See StdClient for an example on how to do this. */ const long ClientWinMask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | // need this, too! EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | StructureNotifyMask | SubstructureRedirectMask; WindowWrapper::WindowWrapper( WId w, Client *parent, const char* name) : QWidget( parent, name ) { d = new WindowWrapperPrivate; win = w; setMouseTracking( TRUE ); setBackgroundColor( colorGroup().background() ); // we don't want the window to be destroyed when we are destroyed XAddToSaveSet(qt_xdisplay(), win ); // no need to be mapped at this point XUnmapWindow( qt_xdisplay(), win ); // set the border width to 0 XWindowChanges wc; wc.border_width = 0; XConfigureWindow( qt_xdisplay(), win, CWBorderWidth, &wc ); // overwrite Qt-defaults because we need SubstructureNotifyMask XSelectInput( qt_xdisplay(), winId(), ClientWinMask | SubstructureNotifyMask ); XSelectInput( qt_xdisplay(), w, FocusChangeMask | PropertyChangeMask | ColormapChangeMask | EnterWindowMask | LeaveWindowMask ); // install a passive grab to catch mouse button events XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, winId(), FALSE, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None ); reparented = FALSE; } WindowWrapper::~WindowWrapper() { releaseWindow(); delete d; } static void ungrabButton( WId winId, int modifier ) { XUngrabButton( qt_xdisplay(), AnyButton, modifier, winId ); XUngrabButton( qt_xdisplay(), AnyButton, modifier | LockMask, winId ); } /*! Called by the client to notify the window wrapper when activation state changes. Releases the passive grab for some modifier combinations when a window becomes active. This helps broken X programs that missinterpret LeaveNotify events in grab mode to work properly (Motif, AWT, Tk, ...) */ void WindowWrapper::setActive( bool active ) { if ( active ) { if ( options->focusPolicy == Options::ClickToFocus || !options->clickRaise ) ungrabButton( winId(), None ); ungrabButton( winId(), ShiftMask ); ungrabButton( winId(), ControlMask ); ungrabButton( winId(), ControlMask | ShiftMask ); } else { XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, winId(), FALSE, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None ); } } QSize WindowWrapper::sizeHint() const { return size(); } QSizePolicy WindowWrapper::sizePolicy() const { return QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); } void WindowWrapper::resizeEvent( QResizeEvent * ) { if ( win && reparented ) { if ( ((Client*)parentWidget())->isResize() ) { QTimer::singleShot( 0, this, SLOT( deferredResize() ) ); } else { XMoveResizeWindow( qt_xdisplay(), win, 0, 0, width(), height() ); if ( ((Client*)parentWidget())->shape() ) ((Client*)parentWidget())->updateShape(); } } } void WindowWrapper::deferredResize() { XMoveResizeWindow( qt_xdisplay(), win, 0, 0, width(), height() ); ((Client*)parentWidget())->sendSyntheticConfigureNotify(); if ( ((Client*)parentWidget())->shape() ) ((Client*)parentWidget())->updateShape(); QApplication::syncX(); // process our own configure events synchronously. } /*! Reimplemented to do map() as well */ void WindowWrapper::show() { map(); QWidget::show(); } /*! Reimplemented to do unmap() as well */ void WindowWrapper::hide() { QWidget::hide(); unmap(); } /*! Maps the managed window. */ void WindowWrapper::map() { if ( win ) { if ( !reparented ) { // get the window. We do it this late in order to // guarantee that our geometry is final. This allows // toolkits to guess the proper frame geometry when // processing the ReparentNotify event from X. XReparentWindow( qt_xdisplay(), win, winId(), 0, 0 ); reparented = TRUE; } XMoveResizeWindow( qt_xdisplay(), win, 0, 0, width(), height() ); XSelectInput( qt_xdisplay(), winId(), ClientWinMask ); XMapRaised( qt_xdisplay(), win ); XSelectInput( qt_xdisplay(), winId(), ClientWinMask | SubstructureNotifyMask ); } } /*! Unmaps the managed window. */ void WindowWrapper::unmap() { if ( win ) { XSelectInput( qt_xdisplay(), winId(), ClientWinMask ); XUnmapWindow( qt_xdisplay(), win ); XSelectInput( qt_xdisplay(), winId(), ClientWinMask | SubstructureNotifyMask ); } } /*! Invalidates the managed window. After that, window() returns 0. */ void WindowWrapper::invalidateWindow() { win = 0; } /*! Releases the window. The client has done its job and the window is still existing. */ void WindowWrapper::releaseWindow() { if ( win ) { if ( reparented ) { XReparentWindow( qt_xdisplay(), win, ((Client*)parentWidget())->workspace()->rootWin(), parentWidget()->x(), parentWidget()->y() ); } XDeleteProperty( qt_xdisplay(), win, atoms->kde_net_user_time); XRemoveFromSaveSet( qt_xdisplay(), win ); XSelectInput( qt_xdisplay(), win, NoEventMask ); invalidateWindow(); } } bool WindowWrapper::x11Event( XEvent * e) { switch ( e->type ) { case ButtonPress: { ((Client*)parentWidget())->updateUserTime(); uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ? KKeyNative::modX(KKey::WIN) : KKeyNative::modX(KKey::ALT); bool bModKeyHeld = ( e->xbutton.state & KKeyNative::accelModMaskX()) == keyModX; if ( ((Client*)parentWidget())->isActive() && ( options->focusPolicy != Options::ClickToFocus && options->clickRaise && !bModKeyHeld ) ) { if ( e->xbutton.button < 4 ) // exclude wheel ((Client*)parentWidget())->autoRaise(); ungrabButton( winId(), None ); } Options::MouseCommand com = Options::MouseNothing; if ( bModKeyHeld ){ switch (e->xbutton.button) { case Button1: com = options->commandAll1(); break; case Button2: com = options->commandAll2(); break; case Button3: com = options->commandAll3(); break; } } else { switch (e->xbutton.button) { case Button1: com = options->commandWindow1(); break; case Button2: com = options->commandWindow2(); break; case Button3: com = options->commandWindow3(); break; default: com = Options::MouseActivateAndPassClick; } } bool replay = ( (Client*)parentWidget() )->performMouseCommand( com, QPoint( e->xbutton.x_root, e->xbutton.y_root) ); if ( ((Client*)parentWidget())->windowType() != NET::Normal && ((Client*)parentWidget())->windowType() != NET::Dialog && ((Client*)parentWidget())->windowType() != NET::Menu && ((Client*)parentWidget())->windowType() != NET::Override ) replay = TRUE; XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //qt_x_time); return TRUE; } break; case ButtonRelease: XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time); break; default: break; } return FALSE; } /*! \class Client client.h \brief The Client class encapsulates a window decoration frame. */ /*! Creates a client on workspace \a ws for window \a w. */ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags f ) : QWidget( parent, name, f | WX11BypassWM ) { d = new ClientPrivate; wspace = ws; win = w; autoRaiseTimer = 0; shadeHoverTimer = 0; unsigned long properties = NET::WMDesktop | NET::WMState | NET::WMWindowType | NET::WMStrut | NET::WMName | NET::WMIconGeometry ; info = new WinInfo( this, qt_xdisplay(), win, qt_xrootwin(), properties ); wwrap = new WindowWrapper( w, this ); wwrap->installEventFilter( this ); // set the initial mapping state setMappingState( WithdrawnState ); desk = -1; // and no desktop yet mode = Nowhere; buttonDown = FALSE; moveResizeMode = FALSE; setMouseTracking( TRUE ); shaded = FALSE; hover_unshade = FALSE; active = FALSE; is_sticky = FALSE; stays_on_top = FALSE; is_shape = FALSE; may_move = TRUE; is_fullscreen = FALSE; skip_taskbar = FALSE; Pdeletewindow = 0; Ptakefocus = 0; Pcontexthelp = 0; input = FALSE; store_settings = FALSE; skip_pager = FALSE; transient_for = None; transient_for_defined = FALSE; max_mode = MaximizeRestore; cmap = None; Window ww; if ( !XGetTransientForHint( qt_xdisplay(), (Window) win, &ww ) ) transient_for = None; else { transient_for = (WId) ww; transient_for_defined = TRUE; verifyTransientFor(); } XClassHint classHint; if ( XGetClassHint( qt_xdisplay(), win, &classHint ) ) { resource_name = classHint.res_name; resource_class = classHint.res_class; XFree( classHint.res_name ); XFree( classHint.res_class ); } getWMHints(); getWindowProtocols(); getWmNormalHints(); // get xSizeHint getWmClientLeader(); fetchName(); if ( mainClient()->isSticky() ) setSticky( TRUE ); // window wants to stay on top? stays_on_top = ( info->state() & NET::StaysOnTop) != 0 || ( transient_for == None && transient_for_defined ); // window does not want a taskbar entry? skip_taskbar = ( info->state() & NET::SkipTaskbar) != 0; skip_pager = ( info->state() & NET::SkipPager) != 0; // should we open this window on a certain desktop? if ( info->desktop() == NETWinInfo::OnAllDesktops ) setSticky( TRUE ); else if ( info->desktop() ) desk = info->desktop(); // window had the initial desktop property! } /*! Destructor, nothing special. */ Client::~Client() { if (moveResizeMode) stopMoveResize(); releaseWindow(); + if( workspace()->activeClient() == this ) // this really shouldn't happen, + workspace()->setActiveClient( NULL ); // but just in case delete info; delete d; } void Client::startMoveResize() { moveResizeMode = true; workspace()->setClientIsMoving(this); grabMouse( cursor() ); grabKeyboard(); if ( ( isMove() && options->moveMode != Options::Opaque ) || ( isResize() && options->resizeMode != Options::Opaque ) ) XGrabServer( qt_xdisplay() ); } void Client::stopMoveResize() { if ( ( isMove() && options->moveMode != Options::Opaque ) || ( isResize() && options->resizeMode != Options::Opaque ) ) XUngrabServer( qt_xdisplay() ); releaseKeyboard(); releaseMouse(); workspace()->setClientIsMoving(0); moveResizeMode = false; } /*! Manages the clients. This means handling the very first maprequest: reparenting, initial geometry, initial state, placement, etc. */ bool Client::manage( bool isMapped, bool doNotShow, bool isInitial ) { if (layout()) layout()->setResizeMode( QLayout::Minimum ); XWindowAttributes attr; if (XGetWindowAttributes(qt_xdisplay(), win, &attr)) { cmap = attr.colormap; original_geometry.setRect(attr.x, attr.y, attr.width, attr.height ); } geom = original_geometry; bool placementDone = FALSE; SessionInfo* session = workspace()->takeSessionInfo( this ); if ( session ) geom = session->geometry; QRect area = workspace()->clientArea( geom.center() ); if ( geom == workspace()->geometry() && inherits( "KWinInternal::NoBorderClient" ) ) { if ( !stays_on_top ) is_fullscreen = TRUE; may_move = FALSE; // don't let fullscreen windows be moved around } if ( isDesktop() ) { // desktops are treated slightly special geom = workspace()->geometry(); may_move = FALSE; isMapped = TRUE; } if ( isMapped || session || isTransient() ) { placementDone = TRUE; } else { bool ignorePPosition = false; XClassHint classHint; if ( XGetClassHint(qt_xdisplay(), win, &classHint) != 0 ) { if ( classHint.res_class ) ignorePPosition = ( options->ignorePositionClasses.find(QString::fromLatin1(classHint.res_class)) != options->ignorePositionClasses.end() ); XFree(classHint.res_name); XFree(classHint.res_class); } if ((xSizeHint.flags & PPosition) && ! ignorePPosition) { int tx = geom.x(); int ty = geom.y(); if (tx < 0) tx = area.right() + tx; if (ty < 0) ty = area.bottom() + ty; geom.moveTopLeft(QPoint(tx, ty)); } if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) || (xSizeHint.flags & USPosition) ) { placementDone = TRUE; if ( windowType() == NET::Normal && !area.contains( geom.topLeft() ) && may_move ) { int tx = geom.x(); int ty = geom.y(); if ( tx >= 0 && tx < area.x() ) tx = area.x(); if ( ty >= 0 && ty < area.y() ) ty = area.y(); if ( tx > area.right() || ty > area.bottom() ) placementDone = FALSE; // weird, do not trust. else geom.moveTopLeft( QPoint( tx, ty ) ); } } if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) ) { // keep in mind that we now actually have a size :-) } if (xSizeHint.flags & PMaxSize) geom.setSize( geom.size().boundedTo( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ); if (xSizeHint.flags & PMinSize) geom.setSize( geom.size().expandedTo( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) ); } windowWrapper()->resize( geom.size() ); // the clever activate() trick is necessary activateLayout(); resize ( sizeForWindowSize( geom.size() ) ); activateLayout(); // inform clients about the frame geometry NETStrut strut; QRect wr = windowWrapper()->geometry(); QRect mr = rect(); strut.left = wr.left(); strut.right = mr.right() - wr.right(); strut.top = wr.top(); strut.bottom = mr.bottom() - wr.bottom(); info->setKDEFrameStrut( strut ); move( geom.x(), geom.y() ); move(gravitate( FALSE ) ); // take the decoration size into account if ( !placementDone ) { workspace()->place( this ); placementDone = TRUE; } else if ( windowType() == NET::Normal ) { if ( geometry().right() > area.right() && width() < area.width() ) move( area.right() - width(), y() ); if ( geometry().bottom() > area.bottom() && height() < area.height() ) move( x(), area.bottom() - height() ); } XShapeSelectInput( qt_xdisplay(), win, ShapeNotifyMask ); if ( (is_shape = Shape::hasShape( win )) ) { updateShape(); } // initial state int init_state = NormalState; if ( isInitial) { if ( session ) { if ( session->iconified ) init_state = IconicState; } else { // find out the initial state. Several possibilities exist XWMHints * hints = XGetWMHints(qt_xdisplay(), win ); if (hints && (hints->flags & StateHint)) { init_state = hints->initial_state; //CT extra check for stupid jdk 1.3.1. But should make sense in general // if client has initial state set to Iconic and is transient with a parent // window that is not Iconic, set init_state to Normal if ((init_state == IconicState) && isTransient() && transientFor() != 0) { Client* client = workspace()->findClient(transientFor()); if(client == 0 || !client->isIconified()) { init_state = NormalState; } } } if (hints) XFree(hints); } } // initial desktop placement - note we don't clobber desk if it is // set to some value, in case the initial desktop code in the // constructor has already set a value for us if ( session ) { desk = session->desktop; if ( desk <= 0 ) desk = workspace()->currentDesktop(); } else if ( desk <= 0 ) { // if this window is transient, ensure that it is opened on the // same window as its parent. this is necessary when an application // starts up on a different desktop than is currently displayed // if ( isTransient() && !mainClient()->isSticky() ) desk = mainClient()->desktop(); if ( desk <= 0 ) { // assume window wants to be visible on the current desktop desk = workspace()->currentDesktop(); } else if ( !isMapped && !doNotShow && desk != workspace()->currentDesktop() && !isTopMenu() ) { //window didn't specify any specific desktop but will appear //somewhere else. This happens for example with "save data?" //dialogs on shutdown. Switch to the respective desktop in //that case. workspace()->setCurrentDesktop( desk ); } } info->setDesktop( desk ); if (isInitial) { setMappingState( init_state ); } if ( workspace()->isNotManaged( caption() ) ) doNotShow = TRUE; // other settings from the previous session if ( session ) { setSticky( session->sticky ); setStaysOnTop( session->staysOnTop ); setSkipTaskbar( session->skipTaskbar ); setSkipPager( session->skipPager ); maximize( (MaximizeMode) session->maximize ); setShade( session->shaded ); geom_restore = session->restore; } else if ( !is_fullscreen ){ // window may want to be maximized if ( (info->state() & NET::Max) == NET::Max ) maximize( Client::MaximizeFull ); else if ( info->state() & NET::MaxVert ) maximize( Client::MaximizeVertical ); else if ( info->state() & NET::MaxHoriz ) maximize( Client::MaximizeHorizontal ); if ( isMaximizable() && !isMaximized() && ( width() >= area.width() || height() >= area.height() ) ) { // window is too large for the screen, maximize in the // directions necessary and generate a suitable restore // geometry. QSize s = adjustedSize( QSize( area.width()*2/3, area.height()*2/3 ) ); if ( width() >= area.width() && height() >= area.height() ) { maximize( Client::MaximizeFull ); geom_restore.setSize( s ); geom_restore.moveCenter( geometry().center() ); } else if ( width() >= area.width() ) { maximize( Client::MaximizeHorizontal ); geom_restore.setWidth( s.width() ); geom_restore.moveCenter( geometry().center() ); } else if ( height() >= area.height() ) { maximize( Client::MaximizeVertical ); geom_restore.setHeight( s.height() ); geom_restore.moveCenter( geometry().center() ); } } } bool showMe = (state == NormalState) && isOnDesktop( workspace()->currentDesktop() ); sendSyntheticConfigureNotify(); workspace()->clientReady( this ); // will call Workspace::propagateClients() if ( showMe && !doNotShow ) { Events::raise( isTransient() ? Events::TransNew : Events::New ); if ( isMapped ) { show(); } else { // we only raise (and potentially activate) new clients if // the user does not actively work in the currently active // client. We can safely drop the activation when the new // window is not a dialog of the active client and // NET_KDE_USER_TIME of the currently active client is // defined and more recent than the one of the new client // (which we set ourselves in CreateNotify in // workspace.cpp). Of course we only do that magic if the // window does not stem from a restored session. Client* ac = workspace()->activeClient(); unsigned long usertime = 0; if ( !isTransient() && !session && ac && !ac->isDesktop() && ac->resourceClass() != resourceClass() && ( usertime = userTime() ) > 0 && ac->userTime() > usertime ) { workspace()->stackClientUnderActive( this ); show(); } else { workspace()->raiseClient( this ); show(); if ( options->focusPolicyIsReasonable() && wantsTabFocus() ) workspace()->requestFocus( this ); } } } if ( !doNotShow ) workspace()->updateClientArea(); delete session; return showMe; } /*! Updates the user time on the client window. This is called inside kwin for every action with the window that qualifies for user interaction (clicking on it, activate it externally, etc.). */ void Client::updateUserTime() { if ( window() ) { timeval tv; gettimeofday( &tv, NULL ); unsigned long now = tv.tv_sec * 10 + tv.tv_usec / 100000; XChangeProperty(qt_xdisplay(), window(), atoms->kde_net_user_time, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&now, 1); } } unsigned long Client::userTime() { unsigned long result = 0; Atom type; int format, status; unsigned long nitems = 0; unsigned long extra = 0; unsigned char *data = 0; XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler); status = XGetWindowProperty( qt_xdisplay(), window(), atoms->kde_net_user_time, 0, 10000, FALSE, XA_CARDINAL, &type, &format, &nitems, &extra, &data ); XSetErrorHandler(oldHandler); if (status == Success ) { if (data && nitems > 0) result = *((long*) data); XFree(data); } return result; } /*! Gets the client's normal WM hints and reconfigures itself respectively. */ void Client::getWmNormalHints() { // TODO keep in mind changing of fix size! if !isWithdrawn()! long msize; if (XGetWMNormalHints(qt_xdisplay(), win, &xSizeHint, &msize) == 0 ) xSizeHint.flags = 0; } /*! Fetches the window's caption (WM_NAME property). It will be stored in the client's caption(). */ void Client::fetchName() { QString s; if ( info->name() ) { s = QString::fromUtf8( info->name() ); } else { XTextProperty tp; char **text; int count; if ( XGetTextProperty( qt_xdisplay(), win, &tp, XA_WM_NAME) != 0 && tp.value != NULL ) { if ( tp.encoding == XA_STRING ) s = QString::fromLocal8Bit( (const char*) tp.value ); else if ( XmbTextPropertyToTextList( qt_xdisplay(), &tp, &text, &count) == Success && text != NULL && count > 0 ) { s = QString::fromLocal8Bit( text[0] ); XFreeStringList( text ); } XFree( tp.value ); } } if ( s != caption() ) { setCaption( "" ); if (workspace()->hasCaption( s ) ){ int i = 2; QString s2; do { s2 = s + " <" + QString::number(i) + ">"; i++; } while (workspace()->hasCaption(s2) ); s = s2; } setCaption( s ); info->setVisibleName( s.utf8() ); if ( !isWithdrawn() ) captionChange( caption() ); } } /*! Sets the client window's mapping state. Possible values are WithdrawnState, IconicState, NormalState. */ void Client::setMappingState(int s){ if ( !win) return; unsigned long data[2]; data[0] = (unsigned long) s; data[1] = (unsigned long) None; state = s; XChangeProperty(qt_xdisplay(), win, qt_wm_state, qt_wm_state, 32, PropModeReplace, (unsigned char *)data, 2); } /*! General handler for XEvents concerning the client window */ bool Client::windowEvent( XEvent * e) { unsigned int dirty = info->event( e ); // pass through the NET stuff if ( ( dirty & NET::WMName ) != 0 ) fetchName(); if ( ( dirty & NET::WMStrut ) != 0 ) workspace()->updateClientArea(); if ( ( dirty & NET::WMIcon) != 0 ) getWMHints(); switch (e->type) { case UnmapNotify: return unmapNotify( e->xunmap ); case MapRequest: return mapRequest( e->xmaprequest ); case ConfigureRequest: return configureRequest( e->xconfigurerequest ); case PropertyNotify: return propertyNotify( e->xproperty ); case ButtonPress: case ButtonRelease: break; case FocusIn: if ( e->xfocus.mode == NotifyUngrab ) break; // we don't care if ( e->xfocus.detail == NotifyPointer ) break; // we don't care setActive( TRUE ); break; case FocusOut: if ( e->xfocus.mode == NotifyGrab ) break; // we don't care if ( isShade() ) break; // we neither if ( e->xfocus.detail != NotifyNonlinear ) return TRUE; // hack for motif apps like netscape if ( QApplication::activePopupWidget() ) break; setActive( FALSE ); break; case ReparentNotify: break; case ClientMessage: return clientMessage( e->xclient ); case ColormapChangeMask: cmap = e->xcolormap.colormap; if ( isActive() ) workspace()->updateColormap(); default: if ( e->type == Shape::shapeEvent() ) updateShape(); break; } return TRUE; // we accept everything :-) } /*! Handles map requests of the client window */ bool Client::mapRequest( XMapRequestEvent& /* e */ ) { switch ( mappingState() ) { case WithdrawnState: manage(); break; case IconicState: // only show window if we're on current desktop if ( isOnDesktop( workspace()->currentDesktop() ) ) show(); else setMappingState( NormalState ); break; case NormalState: // only show window if we're on current desktop if ( isOnDesktop( workspace()->currentDesktop() ) ) show(); // for safety break; } return TRUE; } /*! Handles unmap notify events of the client window */ bool Client::unmapNotify( XUnmapEvent& e ) { if ( e.event != windowWrapper()->winId() && !e.send_event ) return TRUE; switch ( mappingState() ) { case IconicState: // only react on sent events, all others are produced by us if ( e.send_event ) withdraw(); break; case NormalState: if ( !windowWrapper()->isVisibleTo( 0 ) && !e.send_event ) return TRUE; // this event was produced by us as well // maybe we will be destroyed soon. Check this first. XEvent ev; if ( XCheckTypedWindowEvent (qt_xdisplay(), windowWrapper()->winId(), DestroyNotify, &ev) ){ Events::raise( isTransient() ? Events::TransDelete : Events::Delete ); workspace()->destroyClient( this ); return TRUE; } if ( XCheckTypedWindowEvent (qt_xdisplay(), windowWrapper()->winId(), ReparentNotify, &ev) ){ if ( ev.xreparent.window == windowWrapper()->window() && ev.xreparent.parent != windowWrapper()->winId() ) invalidateWindow(); } // fall through case WithdrawnState: // however that has been possible.... withdraw(); break; } return TRUE; } /*! Withdraws the window and destroys the client afterwards */ void Client::withdraw() { Events::raise( isTransient() ? Events::TransDelete : Events::Delete ); // remove early from client list workspace()->removeClient( this ); info->setDesktop( 0 ); desk = 0; releaseWindow(TRUE); workspace()->destroyClient( this ); } /*! Handles configure requests of the client window */ bool Client::configureRequest( XConfigureRequestEvent& e ) { if ( isResize() ) return TRUE; // we have better things to do right now if ( isDesktop() ) { setGeometry( workspace()->geometry() ); sendSyntheticConfigureNotify(); return TRUE; } if ( isShade() ) setShade( FALSE ); // compress configure requests XEvent otherEvent; while (XCheckTypedWindowEvent (qt_xdisplay(), win, ConfigureRequest, &otherEvent) ) { if (otherEvent.xconfigurerequest.value_mask == e.value_mask) e = otherEvent.xconfigurerequest; else { XPutBackEvent(qt_xdisplay(), &otherEvent); break; } } bool stacking = e.value_mask & CWStackMode; int stack_mode = e.detail; if ( e.value_mask & CWBorderWidth ) { // first, get rid of a window border XWindowChanges wc; unsigned int value_mask = 0; wc.border_width = 0; value_mask = CWBorderWidth; XConfigureWindow( qt_xdisplay(), win, value_mask, & wc ); } if ( e.value_mask & (CWX | CWY ) ) { int ox = 0; int oy = 0; int gravity = NorthWestGravity; if ( xSizeHint.flags & PWinGravity) gravity = xSizeHint.win_gravity; if ( gravity == StaticGravity ) { // only with StaticGravity according to ICCCM 4.1.5 ox = windowWrapper()->x(); oy = windowWrapper()->y(); } int nx = x() + ox; int ny = y() + oy; if ( e.value_mask & CWX ) nx = e.x; if ( e.value_mask & CWY ) ny = e.y; // clever workaround for applications like xv that want to set // the location to the current location but miscalculate the // frame size due to kwin being a double-reparenting window // manager if ( ox == 0 && oy == 0 && nx == x() + windowWrapper()->x() && ny == y() + windowWrapper()->y() ) { nx = x(); ny = y(); } QPoint np( nx-ox, ny-oy); #if 0 if ( windowType() == NET::Normal && may_move ) { // crap for broken netscape QRect area = workspace()->clientArea(); if ( !area.contains( np ) && width() < area.width() && height() < area.height() ) { if ( np.x() < area.x() ) np.rx() = area.x(); if ( np.y() < area.y() ) np.ry() = area.y(); } } #endif if ( isMaximized() ) { geom_restore.moveTopLeft( np ); } else { move( np ); } } if ( e.value_mask & (CWWidth | CWHeight ) ) { int nw = windowWrapper()->width(); int nh = windowWrapper()->height(); if ( e.value_mask & CWWidth ) nw = e.width; if ( e.value_mask & CWHeight ) nh = e.height; QSize ns = sizeForWindowSize( QSize( nw, nh ) ); //QRect area = workspace()->clientArea(); if ( isMaximizable() && isMaximized() ) { //&& ( ns.width() < area.width() || ns.height() < area.height() ) ) { if ( (e.value_mask & (CWX | CWY )) == 0 ) geom_restore.moveTopLeft( geometry().topLeft() ); geom_restore.setSize( ns ); maximize( Client::MaximizeRestore ); } else if ( !isMaximized() ) { if ( ns == size() ) return TRUE; // broken xemacs stuff (ediff) resize( ns ); } } if ( stacking ){ switch (stack_mode){ case Above: case TopIf: if ( isTopMenu() && mainClient() != this ) break; // in this case, we already do the raise workspace()->raiseClient( this ); break; case Below: case BottomIf: workspace()->lowerClient( this ); break; case Opposite: default: break; } } if ( e.value_mask & (CWX | CWY | CWWidth | CWHeight ) ) sendSyntheticConfigureNotify(); return TRUE; } /*! Handles property changes of the client window */ bool Client::propertyNotify( XPropertyEvent& e ) { switch ( e.atom ) { case XA_WM_NORMAL_HINTS: getWmNormalHints(); break; case XA_WM_NAME: fetchName(); break; case XA_WM_TRANSIENT_FOR: Window ww; if ( !XGetTransientForHint( qt_xdisplay(), (Window) win, &ww ) ) { transient_for = None; transient_for_defined = FALSE; } else { transient_for = (WId) ww; transient_for_defined = TRUE; verifyTransientFor(); } break; case XA_WM_HINTS: getWMHints(); break; default: if ( e.atom == atoms->wm_protocols ) getWindowProtocols(); else if (e.atom == atoms->wm_client_leader ) getWmClientLeader(); break; } return TRUE; } /*! Handles client messages for the client window */ bool Client::clientMessage( XClientMessageEvent& e ) { if ( e.message_type == atoms->kde_wm_change_state ) { if ( e.data.l[0] == IconicState && isNormal() ) { if ( e.data.l[1] ) blockAnimation = TRUE; iconify(); } else if ( e.data.l[0] == NormalState && isIconified() ) { if ( e.data.l[1] ) blockAnimation = TRUE; // only show window if we're on current desktop if ( isOnDesktop( workspace()->currentDesktop() ) ) show(); else setMappingState( NormalState ); } blockAnimation = FALSE; } else if ( e.message_type == atoms->wm_change_state) { if ( e.data.l[0] == IconicState && isNormal() ) iconify(); return TRUE; } return FALSE; } /*! Auxiliary function to inform the client about the current window configuration. */ void Client::sendSyntheticConfigureNotify() { XConfigureEvent c; c.type = ConfigureNotify; c.send_event = True; c.event = win; c.window = win; c.x = x() + windowWrapper()->x(); c.y = y() + windowWrapper()->y(); c.width = windowWrapper()->width(); c.height = windowWrapper()->height(); c.border_width = 0; c.above = None; c.override_redirect = 0; XSendEvent( qt_xdisplay(), c.event, TRUE, StructureNotifyMask, (XEvent*)&c ); } /*! Adjust the frame size \a frame according to he window's size hints. */ QSize Client::adjustedSize( const QSize& frame) const { // first, get the window size for the given frame size s QSize wsize( frame.width() - ( width() - wwrap->width() ), frame.height() - ( height() - wwrap->height() ) ); return sizeForWindowSize( wsize ); } /*! Calculate the appropriate frame size for the given window size \a wsize. \a wsize is adapted according to the window's size hints (minimum, maximum and incremental size changes). */ QSize Client::sizeForWindowSize( const QSize& wsize, bool ignore_height) const { int w = wsize.width(); int h = wsize.height(); if (w<1) w = 1; if (h<1) h = 1; int bw = 0; int bh = 0; if (xSizeHint.flags & PBaseSize) { bw = xSizeHint.base_width; bh = xSizeHint.base_height; if (w < xSizeHint.base_width) w = xSizeHint.base_width; if (h < xSizeHint.base_height) h = xSizeHint.base_height; } else if ( xSizeHint.flags & PMinSize ) { bw = xSizeHint.min_width; bh = xSizeHint.min_height; if (w < xSizeHint.min_width) w = xSizeHint.min_width; if (h < xSizeHint.min_height) h = xSizeHint.min_height; } if (xSizeHint.flags & PResizeInc) { if ( xSizeHint.width_inc > 0 ) { int sx = (w - bw) / xSizeHint.width_inc; w = bw + sx * xSizeHint.width_inc; } if ( xSizeHint.height_inc > 0 ) { int sy = (h - bh) / xSizeHint.height_inc; h = bh + sy * xSizeHint.height_inc; } } if (xSizeHint.flags & PMaxSize) { w = QMIN( xSizeHint.max_width, w ); h = QMIN( xSizeHint.max_height, h ); } if (xSizeHint.flags & PMinSize) { w = QMAX( xSizeHint.min_width, w ); h = QMAX( xSizeHint.min_height, h ); } w = QMAX( minimumWidth(), w ); h = QMAX( minimumHeight(), h ); int ww = wwrap->width(); int wh = 1; if ( !wwrap->isHidden() ) wh = wwrap->height(); if ( ignore_height && wsize.height() == 0 ) h = 0; return QSize( width() - ww + w, height()-wh+h ); } /*! Returns whether the window is resizable or has a fixed size. */ bool Client::isResizable() const { if ( !isMovable() ) return FALSE; if ( ( xSizeHint.flags & PMaxSize) == 0 || (xSizeHint.flags & PMinSize ) == 0 ) return TRUE; return ( xSizeHint.min_width != xSizeHint.max_width ) || ( xSizeHint.min_height != xSizeHint.max_height ); } /* Returns whether the window is maximizable or not */ bool Client::isMaximizable() const { if ( isMaximized() ) return TRUE; return isResizable() && !isTool(); } /* Returns whether the window is minimizable or not */ bool Client::isMinimizable() const { return ( !isTransient() || !workspace()->findClient( transientFor() ) ) && wantsTabFocus(); } /*! Reimplemented to provide move/resize */ void Client::mousePressEvent( QMouseEvent * e) { if (buttonDown) return; Options::MouseCommand com = Options::MouseNothing; if (e->state() & AltButton) { if ( e->button() == LeftButton ) { com = options->commandAll1(); } else if (e->button() == MidButton) { com = options->commandAll2(); } else if (e->button() == RightButton) { com = options->commandAll3(); } } else { bool active = isActive(); if ( !wantsInput() ) // we cannot be active, use it anyway active = TRUE; if ((e->button() == LeftButton && options->commandActiveTitlebar1() != Options::MouseOperationsMenu) || (e->button() == MidButton && options->commandActiveTitlebar2() != Options::MouseOperationsMenu) || (e->button() == RightButton && options->commandActiveTitlebar3() != Options::MouseOperationsMenu) ) { mouseMoveEvent( e ); buttonDown = TRUE; moveOffset = e->pos(); invertedMoveOffset = rect().bottomRight() - e->pos(); } if ( e->button() == LeftButton ) { com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1(); } else if ( e->button() == MidButton ) { com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2(); } else if ( e->button() == RightButton ) { com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3(); } } performMouseCommand( com, e->globalPos()); } /*! Reimplemented to provide move/resize */ void Client::mouseReleaseEvent( QMouseEvent * e) { if ( (e->stateAfter() & MouseButtonMask) == 0 ) { buttonDown = FALSE; if ( moveResizeMode ) { clearbound(); stopMoveResize(); setGeometry( geom ); mode = mousePosition( e->pos() ); setMouseCursor( mode ); Events::raise( isResize() ? Events::ResizeEnd : Events::MoveEnd ); } } } /*! */ void Client::resizeEvent( QResizeEvent * e) { QWidget::resizeEvent( e ); } /*! Reimplemented to provide move/resize */ void Client::mouseMoveEvent( QMouseEvent * e) { if ( !buttonDown ) { MousePosition newmode = mousePosition( e->pos() ); if( newmode != mode ) setMouseCursor( newmode ); mode = newmode; geom = geometry(); return; } if ( !isMovable() || (isShade() && mode != Center)) return; if ( !moveResizeMode ) { QPoint p( e->pos() - moveOffset ); if (p.manhattanLength() >= 6) { if ( isMaximized() ) { // in case we were maximized, reset state max_mode = MaximizeRestore; maximizeChange(FALSE ); Events::raise( Events::UnMaximize ); info->setState( 0, NET::Max ); } startMoveResize(); Events::raise( isResize() ? Events::ResizeStart : Events::MoveStart ); } else { return; } } if ( mode != Center && hover_unshade ) setShade(false); QPoint globalPos = e->globalPos(); // pos() + geometry().topLeft(); QPoint p = globalPos + invertedMoveOffset; QPoint pp = globalPos - moveOffset; QSize mpsize( geometry().right() - pp.x() + 1, geometry().bottom() - pp.y() + 1 ); mpsize = adjustedSize( mpsize ); QPoint mp( geometry().right() - mpsize.width() + 1, geometry().bottom() - mpsize.height() + 1 ); geom = geometry(); switch ( mode ) { case TopLeft: geom = QRect( mp, geometry().bottomRight() ) ; break; case BottomRight: geom = QRect( geometry().topLeft(), p ) ; break; case BottomLeft: geom = QRect( QPoint(mp.x(), geometry().y() ), QPoint( geometry().right(), p.y()) ) ; break; case TopRight: geom = QRect( QPoint(geometry().x(), mp.y() ), QPoint( p.x(), geometry().bottom()) ) ; break; case Top: geom = QRect( QPoint( geometry().left(), mp.y() ), geometry().bottomRight() ) ; break; case Bottom: geom = QRect( geometry().topLeft(), QPoint( geometry().right(), p.y() ) ) ; break; case Left: geom = QRect( QPoint( mp.x(), geometry().top() ), geometry().bottomRight() ) ; break; case Right: geom = QRect( geometry().topLeft(), QPoint( p.x(), geometry().bottom() ) ) ; break; case Center: geom.moveTopLeft( pp ); break; default: //fprintf(stderr, "KWin::mouseMoveEvent with mode = %d\n", mode); break; } QRect desktopArea = workspace()->clientArea(e->globalPos()); int marge = 5; if ( isResize() && geom.size() != size() ) { if (geom.bottom() < desktopArea.top()+marge) geom.setBottom(desktopArea.top()+marge); if (geom.top() > desktopArea.bottom()-marge) geom.setTop(desktopArea.bottom()-marge); if (geom.right() < desktopArea.left()+marge) geom.setRight(desktopArea.left()+marge); if (geom.left() > desktopArea.right()-marge) geom.setLeft(desktopArea.right()-marge); geom.setSize( adjustedSize( geom.size() ) ); if (options->resizeMode == Options::Opaque ) { setGeometry( geom ); } else if ( options->resizeMode == Options::Transparent ) { clearbound(); drawbound( geom ); } } else if ( isMove() && geom.topLeft() != geometry().topLeft() ) { geom.moveTopLeft( workspace()->adjustClientPosition( this, geom.topLeft() ) ); if (geom.bottom() < desktopArea.top()+marge) geom.moveBottomLeft( QPoint( geom.left(), desktopArea.top()+marge)); if (geom.top() > desktopArea.bottom()-marge) geom.moveTopLeft( QPoint( geom.left(), desktopArea.bottom()-marge)); if (geom.right() < desktopArea.left()+marge) geom.moveTopRight( QPoint( desktopArea.left()+marge, geom.top())); if (geom.left() > desktopArea.right()-marge) geom.moveTopLeft( QPoint( desktopArea.right()-marge, geom.top())); switch ( options->moveMode ) { case Options::Opaque: move( geom.topLeft() ); break; case Options::Transparent: clearbound(); drawbound( geom ); break; } } if ( isMove() ) workspace()->clientMoved(globalPos, qt_x_time); // QApplication::syncX(); // process our own configure events synchronously. } // these two aren't called at all ... ?! /*! Reimplemented to provide move/resize */ void Client::enterEvent( QEvent * ) { } /*! Reimplemented to provide move/resize */ void Client::leaveEvent( QEvent * ) { } /*! Reimplemented to inform the client about the new window position. */ void Client::setGeometry( int x, int y, int w, int h ) { QWidget::setGeometry(x, y, w, h); if ( !isResize() && isVisible() ) sendSyntheticConfigureNotify(); } /*! Reimplemented to inform the client about the new window position. */ void Client::move( int x, int y ) { QWidget::move( x, y ); if ( !isResize() && isVisible() ) sendSyntheticConfigureNotify(); } /*! Reimplemented to set the mapping state and to map the managed window in the window wrapper. Also takes care of deiconification of transients. */ void Client::show() { if ( isIconified() && ( !isTransient() || mainClient() == this ) ) { animateIconifyOrDeiconify( FALSE ); //CT and unshade it if (isShade()) setShade(false); } setMappingState( NormalState ); QWidget::show(); windowWrapper()->map(); } /*! Reimplemented to unmap the managed window in the window wrapper. Also informs the workspace. */ void Client::hide() { QWidget::hide(); workspace()->clientHidden( this ); windowWrapper()->unmap(); } /*! Late initialialize the client after the window has been managed. Ensure to call the superclasses init() implementation when subclassing. */ void Client::init() { } /*!\fn captionChange( const QString& name ) Indicates that the caption (the window title) changed to \a name. Subclasses shall then repaint the title string in a clever, fast mannor. The default implementation calls repaint( FALSE ); */ void Client::captionChange( const QString& ) { repaint( FALSE ); } /*!\fn activeChange( bool act ) Indicates that the activation state changed to \a act. Subclasses may want to indicate the new state graphically in a clever, fast mannor. The default implementation calls repaint( FALSE ); */ void Client::activeChange( bool ) { repaint( FALSE ); } /*! Indicates that the application's icon changed to \a act. Subclasses may want to indicate the new state graphically in a clever, fast mannor. The default implementation calls repaint( FALSE ); */ void Client::iconChange() { repaint( FALSE ); } /*!\fn maximizeChange( bool max ) Indicates that the window was maximized or demaximized. \a max is set respectively. Subclasses may want to indicate the new state graphically, for example with a different icon. */ void Client::maximizeChange( bool ) { } /*!\fn stickyChange( bool sticky ) Indicates that the window was made sticky or unsticky. \a sticky is set respectively. Subclasses may want to indicate the new state graphically, for example with a different icon. */ void Client::stickyChange( bool ) { } /*!\fn shadeChange( bool shaded ) Indicates that the window was shaded or unshaded. \a shaded is set respectively. Subclasses may want to indicate the new state graphically, for example with a different icon. */ void Client::shadeChange( bool ) { } /*! Paints a client window. The default implementation does nothing. To be implemented by subclasses. */ void Client::paintEvent( QPaintEvent * ) { } /*! Releases the window. The client has done its job and the window is still existing. If withdraw is TRUE, the function also sets the mapping state of the window to WithdrawnState */ void Client::releaseWindow( bool withdraw ) { if ( win ) { move(gravitate(TRUE)); if ( withdraw ) XUnmapWindow( qt_xdisplay(), win ); windowWrapper()->releaseWindow(); if ( withdraw ) setMappingState( WithdrawnState ); win = 0; } } /*! Invalidates the window to avoid the client accessing it again. This function is called by the workspace when the window has been destroyed. */ void Client::invalidateWindow() { win = 0; windowWrapper()->invalidateWindow(); } /*! Iconifies this client plus its transients */ void Client::iconify() { if ( !isMinimizable() ) return; setMappingState( IconicState ); Events::raise( Events::Iconify ); if ( (!isTransient() || mainClient() == this ) && isVisible() ) animateIconifyOrDeiconify( TRUE ); hide(); workspace()->iconifyOrDeiconifyTransientsOf( this ); } /*! Closes the window by either sending a delete_window message or using XKill. */ void Client::closeWindow() { Events::raise( Events::Close ); if ( Pdeletewindow ){ sendClientMessage( win, atoms->wm_protocols, atoms->wm_delete_window); } else { // client will not react on wm_delete_window. We have not choice // but destroy his connection to the XServer. Events::raise( isTransient() ? Events::TransDelete : Events::Delete ); XKillClient(qt_xdisplay(), win ); workspace()->destroyClient( this ); } } /*! Kills the window via XKill */ void Client::killWindow() { // not sure if we need an Events::Kill or not.. until then, use // Events::Close Events::raise( Events::Close ); // always kill this client at the server XKillClient(qt_xdisplay(), win ); workspace()->destroyClient( this ); } /*! Sets the maximization according to \a vertically and \a horizontally */ void Client::maximizeRaw( bool vertically, bool horizontally ) { if ( !vertically && !horizontally ) { maximize ( MaximizeRestore ); } else { MaximizeMode m = MaximizeRestore; if ( vertically && horizontally ) m = MaximizeFull; else if ( vertically ) m = MaximizeVertical; else if (horizontally ) m = MaximizeHorizontal; if ( m != max_mode ) { if ( isMaximized() ) max_mode = MaximizeAdjust; maximize( m ); } } } /*! Maximizes the client according to mode \a m. If the client is already maximized with the same mode, it gets restored. Does some smart magic like vertically + horizontally = full. This is the slot to connect to from your client subclass. */ void Client::maximize( MaximizeMode m) { if ( !isMaximizable() ) return; QRect clientArea = workspace()->clientArea(geometry().center()); if (isShade()) setShade( FALSE ); if ( m == MaximizeAdjust ) { m = max_mode; } else { if ( max_mode == m ) m = MaximizeRestore; if ( m == max_mode ) return; // nothing to do if ( m != MaximizeRestore && max_mode != MaximizeAdjust ) { if ( max_mode == MaximizeRestore ) geom_restore = geometry(); else if ( m != MaximizeFull) m = (MaximizeMode ) ( (max_mode & MaximizeFull) ^ (m & MaximizeFull) ); Events::raise( Events::Maximize ); } } switch (m) { case MaximizeVertical: setGeometry( QRect(QPoint(geom_restore.x(), clientArea.top()), adjustedSize(QSize(geom_restore.width(), clientArea.height()))) ); info->setState( NET::MaxVert, NET::Max ); break; case MaximizeHorizontal: setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()), adjustedSize(QSize(clientArea.width(), geom_restore.height()))) ); info->setState( NET::MaxHoriz, NET::Max ); break; case MaximizeRestore: { Events::raise( Events::UnMaximize ); setGeometry(geom_restore); max_mode = MaximizeRestore; info->setState( 0, NET::Max ); } break; case MaximizeFull: { QRect r = QRect(clientArea.topLeft(), adjustedSize(clientArea.size())); // hide right and left border of maximized windows if ( !options->moveResizeMaximizedWindows ) { if ( r.left() == 0 ) r.setLeft( r.left() - windowWrapper()->x() ); if ( r.right() == workspace()->geometry().right() ) r.setRight( r.right() + width() - windowWrapper()->geometry().right() ); } setGeometry( r ); info->setState( NET::Max, NET::Max ); } break; default: break; } max_mode = m; maximizeChange( m != MaximizeRestore ); } void Client::toggleSticky() { setSticky( !isSticky() ); } void Client::toggleShade() { setShade( !isShade() ); } void Client::maximize() { if ( isMaximized() ) maximize( MaximizeRestore ); else maximize( MaximizeFull ); } /*! Catch events of the WindowWrapper */ bool Client::eventFilter( QObject *o, QEvent * e) { if ( o != wwrap ) return FALSE; switch ( e->type() ) { case QEvent::Show: windowWrapperShowEvent( (QShowEvent*)e ); break; case QEvent::Hide: windowWrapperHideEvent( (QHideEvent*)e ); break; default: break; } return FALSE; } const QPoint Client::gravitate( bool invert ) const { int gravity, dx, dy; dx = dy = 0; gravity = NorthWestGravity; if ( xSizeHint.flags & PWinGravity) gravity = xSizeHint.win_gravity; switch (gravity) { case NorthWestGravity: dx = 0; dy = 0; break; case NorthGravity: dx = windowWrapper()->x(); dy = 0; break; case NorthEastGravity: dx = width() - windowWrapper()->width(); dy = 0; break; case WestGravity: dx = 0; dy = windowWrapper()->y(); break; case CenterGravity: case StaticGravity: dx = windowWrapper()->x(); dy = windowWrapper()->y(); break; case EastGravity: dx = width() - windowWrapper()->width(); dy = windowWrapper()->y(); break; case SouthWestGravity: dx = 0; dy = height() - windowWrapper()->height(); break; case SouthGravity: dx = windowWrapper()->x(); dy = height() - windowWrapper()->height(); break; case SouthEastGravity: dx = width() - windowWrapper()->width(); dy = height() - windowWrapper()->height(); break; } if (invert) return QPoint( x() + dx, y() + dy ); else return QPoint( x() - dx, y() - dy ); } /*! Reimplement to handle crossing events (qt should provide xroot, yroot) Crossing events are necessary for the focus-follows-mouse focus policies, to do proper activation and deactivation. */ bool Client::x11Event( XEvent * e) { if ( e->type == EnterNotify && ( e->xcrossing.mode == NotifyNormal || ( !options->focusPolicyIsReasonable() && e->xcrossing.mode == NotifyUngrab ) ) ) { if (options->shadeHover && isShade() && !isDesktop()) { delete shadeHoverTimer; shadeHoverTimer = new QTimer( this ); connect( shadeHoverTimer, SIGNAL( timeout() ), this, SLOT( shadeHover() )); shadeHoverTimer->start( options->shadeHoverInterval, TRUE ); } if ( options->focusPolicy == Options::ClickToFocus ) return TRUE; if ( options->autoRaise && !isDesktop() && !isDock() && !isTopMenu() && workspace()->focusChangeEnabled() && workspace()->topClientOnDesktop() != this ) { delete autoRaiseTimer; autoRaiseTimer = new QTimer( this ); connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) ); autoRaiseTimer->start( options->autoRaiseInterval, TRUE ); } if ( options->focusPolicy != Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) ) return TRUE; workspace()->requestFocus( this ); return TRUE; } if ( e->type == LeaveNotify && e->xcrossing.mode == NotifyNormal ) { if ( !buttonDown ) { mode = Nowhere; setCursor( arrowCursor ); } bool lostMouse = !rect().contains( QPoint( e->xcrossing.x, e->xcrossing.y ) ); if ( lostMouse ) { delete autoRaiseTimer; autoRaiseTimer = 0; delete shadeHoverTimer; shadeHoverTimer = 0; if ( hover_unshade && !moveResizeMode && !buttonDown ) setShade( TRUE, 1 ); } if ( options->focusPolicy == Options::FocusStrictlyUnderMouse ) if ( isActive() && lostMouse ) workspace()->requestFocus( 0 ) ; return TRUE; } return FALSE; } /*! Returns a logical mouse position for the cursor position \a p. Possible positions are: Nowhere, TopLeft , BottomRight, BottomLeft, TopRight, Top, Bottom, Left, Right, Center */ Client::MousePosition Client::mousePosition( const QPoint& p ) const { const int range = 16; const int border = 4; MousePosition m = Nowhere; if ( ( p.x() > border && p.x() < width() - border ) && ( p.y() > border && p.y() < height() - border ) ) return Center; if ( p.y() <= range && p.x() <= range) m = TopLeft; else if ( p.y() >= height()-range && p.x() >= width()-range) m = BottomRight; else if ( p.y() >= height()-range && p.x() <= range) m = BottomLeft; else if ( p.y() <= range && p.x() >= width()-range) m = TopRight; else if ( p.y() <= border ) m = Top; else if ( p.y() >= height()-border ) m = Bottom; else if ( p.x() <= border ) m = Left; else if ( p.x() >= width()-border ) m = Right; else m = Center; return m; } /*! Sets an appropriate cursor shape for the logical mouse position \a m \sa QWidget::setCursor() */ void Client::setMouseCursor( MousePosition m ) { if ( !isResizable() || isShade() ) { setCursor( arrowCursor ); return; } switch ( m ) { case TopLeft: case BottomRight: setCursor( sizeFDiagCursor ); break; case BottomLeft: case TopRight: setCursor( sizeBDiagCursor ); break; case Top: case Bottom: setCursor( sizeVerCursor ); break; case Left: case Right: setCursor( sizeHorCursor ); break; default: setCursor( arrowCursor ); break; } } bool Client::isShade() const { return shaded; } void Client::setShade( bool s, int hus ) { /* This case if when we think we are: 1. Getting shaded 2. Were already unshaded because of hover unshading 3. and this is not a hover shade operation. This can happen when a window is hover unshaded and the user double clicks on the title bar and wants the window to be unshaded */ if ( s && hover_unshade && !hus) { hover_unshade = 0; return; } hover_unshade = hus; if ( shaded == s ) return; shaded = s; if ( isVisible() ) Events::raise( s ? Events::ShadeDown : Events::ShadeUp ); int as = options->animateShade? 10 : 1; if ( shaded ) { int h = height(); QSize s( sizeForWindowSize( QSize( windowWrapper()->width(), 0), TRUE ) ); windowWrapper()->hide(); repaint( FALSE ); bool wasStaticContents = testWFlags( WStaticContents ); setWFlags( WStaticContents ); int step = QMAX( 4, QABS( h - s.height() ) / as )+1; do { h -= step; resize ( s.width(), h ); QApplication::syncX(); } while ( h > s.height() + step ); if ( !wasStaticContents ) clearWFlags( WStaticContents ); resize (s ); if (hus) workspace()->requestFocus( NULL ); } else { int h = height(); QSize s( sizeForWindowSize( windowWrapper()->size(), TRUE ) ); bool wasStaticContents = testWFlags( WStaticContents ); setWFlags( WStaticContents ); int step = QMAX( 4, QABS( h - s.height() ) / as )+1; do { h += step; resize ( s.width(), h ); // assume a border // we do not have time to wait for X to send us paint events repaint( 0, h - step-5, width(), step+5, TRUE); QApplication::syncX(); } while ( h < s.height() - step ); if ( !wasStaticContents ) clearWFlags( WStaticContents ); resize ( s ); if (hus) setActive( TRUE ); windowWrapper()->show(); activateLayout(); if ( isActive() ) workspace()->requestFocus( this ); } if(!hus) info->setState( shaded?NET::Shaded:0, NET::Shaded ); workspace()->iconifyOrDeiconifyTransientsOf( this ); shadeChange( shaded ); } /*! Sets the client's active state to \a act. This function does only change the visual appearance of the client, it does not change the focus setting. Use Workspace::activateClient() or Workspace::requestFocus() instead. If a client receives or looses the focus, it calls setActive() on its own. */ void Client::setActive( bool act) { windowWrapper()->setActive( act ); if ( act ) workspace()->setActiveClient( this ); if ( active == act ) return; active = act; if ( active ) Events::raise( Events::Activate ); if ( !active && autoRaiseTimer ) { delete autoRaiseTimer; autoRaiseTimer = 0; } activeChange( active ); } /*! Sets the window's sticky property to b */ void Client::setSticky( bool b ) { if ( is_sticky == b ) return; is_sticky = b; if ( isVisible() ) { if ( is_sticky ) Events::raise( Events::Sticky ); else Events::raise( Events::UnSticky ); } if ( !is_sticky ) setDesktop( workspace()->currentDesktop() ); else info->setDesktop( NETWinInfo::OnAllDesktops ); workspace()->setStickyTransientsOf( this, b ); stickyChange( is_sticky ); } /*! Let the window stay on top or not, depending on \a b \sa Workspace::setClientOnTop() */ void Client::setStaysOnTop( bool b ) { if ( b == staysOnTop() ) return; stays_on_top = b; info->setState( b?NET::StaysOnTop:0, NET::StaysOnTop ); } void Client::setSkipTaskbar( bool b ) { if ( b == skipTaskbar() ) return; skip_taskbar = b; info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar ); } void Client::setSkipPager( bool b ) { if ( b == skipPager() ) return; skip_pager = b; info->setState( b?NET::SkipPager:0, NET::SkipPager ); } void Client::setDesktop( int desktop) { desk = desktop; info->setDesktop( desktop ); } void Client::getWMHints() { // get the icons, allow scaling icon_pix = KWin::icon( win, 32, 32, TRUE ); miniicon_pix = KWin::icon( win, 16, 16, TRUE ); if ( icon_pix.isNull() && mainClient() != this ) { icon_pix = mainClient()->icon_pix; miniicon_pix = mainClient()->miniicon_pix; } if ( !isWithdrawn() ) iconChange(); input = TRUE; XWMHints *hints = XGetWMHints(qt_xdisplay(), win ); if ( hints ) { if ( hints->flags & InputHint ) input = hints->input; XFree((char*)hints); } } void Client::getWindowProtocols(){ Atom *p; int i,n; Pdeletewindow = 0; Ptakefocus = 0; Pcontexthelp = 0; if (XGetWMProtocols(qt_xdisplay(), win, &p, &n)){ for (i = 0; i < n; i++) if (p[i] == atoms->wm_delete_window) Pdeletewindow = 1; else if (p[i] == atoms->wm_take_focus) Ptakefocus = 1; else if (p[i] == atoms->net_wm_context_help) Pcontexthelp = 1; if (n>0) XFree(p); } } /*! Puts the focus on this window. Clients should never calls this themselves, instead they should use Workspace::requestFocus(). */ void Client::takeFocus( bool force ) { if ( !force && ( isTopMenu() || isDock() ) ) return; // toplevel menus and dock windows don't take focus if not forced if ( input ) { // Matthias Ettrich says to comment it so that we avoid two consecutive setActive // We will have to look after it anyways, in case people will get problems //setActive( TRUE ); // Qt may delay the mapping which may cause XSetInputFocus to fail, force show window QApplication::sendPostedEvents( windowWrapper(), QEvent::ShowWindowRequest ); XSetInputFocus( qt_xdisplay(), win, RevertToPointerRoot, qt_x_time ); } if ( Ptakefocus ) sendClientMessage(win, atoms->wm_protocols, atoms->wm_take_focus); } /*!\reimp */ void Client::setMask( const QRegion & reg) { mask = reg; QWidget::setMask( reg ); } /*! Returns the main client. For normal windows, this is the window itself. For transient windows, it is the parent. */ Client* Client::mainClient() { if ( !isTransient() && transientFor() != 0 ) return this; ClientList saveset; Client *n, *c = this; do { saveset.append( c ); n = workspace()->findClient( c->transientFor() ); if ( !n ) break; c = n; } while ( c && c->isTransient() && !saveset.contains( c ) ); return c?c:this; } /*! Returns whether the window provides context help or not. If it does, you should show a help menu item or a help button lie '?' and call contextHelp() if this is invoked. \sa contextHelp() */ bool Client::providesContextHelp() const { return Pcontexthelp; } /*! Invokes context help on the window. Only works if the window actually provides context help. \sa providesContextHelp() */ void Client::contextHelp() { if ( Pcontexthelp ) { sendClientMessage(win, atoms->wm_protocols, atoms->net_wm_context_help); QWhatsThis::enterWhatsThisMode(); } } /*! Performs a mouse command on this client (see options.h) */ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPos) { bool replay = FALSE; switch (command) { case Options::MouseRaise: workspace()->raiseClient( this ); break; case Options::MouseLower: workspace()->lowerClient( this ); break; case Options::MouseShade : setShade(!isShade()); break; case Options::MouseOperationsMenu: if ( isActive() & ( options->focusPolicy != Options::ClickToFocus && options->clickRaise ) ) autoRaise(); workspace()->clientPopup( this )->exec( globalPos ); workspace()->requestFocus( this ); break; case Options::MouseToggleRaiseAndLower: if ( workspace()->topClientOnDesktop() == this ) workspace()->lowerClient( this ); else workspace()->raiseClient( this ); break; case Options::MouseActivateAndRaise: workspace()->requestFocus( this ); workspace()->raiseClient( this ); break; case Options::MouseActivateAndLower: workspace()->requestFocus( this ); workspace()->lowerClient( this ); break; case Options::MouseActivate: workspace()->requestFocus( this ); break; case Options::MouseActivateRaiseAndPassClick: workspace()->requestFocus( this ); workspace()->raiseClient( this ); replay = TRUE; break; case Options::MouseActivateAndPassClick: workspace()->requestFocus( this ); replay = TRUE; break; case Options::MouseMove: if (!isMovable()) break; mode = Center; geom=geometry(); if ( isMaximized() ) { // in case we were maximized, reset state max_mode = MaximizeRestore; maximizeChange(FALSE ); Events::raise( Events::UnMaximize ); info->setState( 0, NET::Max ); } buttonDown = TRUE; moveOffset = mapFromGlobal( globalPos ); invertedMoveOffset = rect().bottomRight() - moveOffset; setMouseCursor( mode ); startMoveResize(); break; case Options::MouseResize: { if (!isMovable()) break; geom=geometry(); if ( isMaximized() ) { // in case we were maximized, reset state max_mode = MaximizeRestore; maximizeChange(FALSE ); Events::raise( Events::UnMaximize ); info->setState( 0, NET::Max ); } buttonDown = TRUE; moveOffset = mapFromGlobal( globalPos ); int x = moveOffset.x(), y = moveOffset.y(); bool left = x < width() / 3; bool right = x >= 2 * width() / 3; bool top = y < height() / 3; bool bot = y >= 2 * height() / 3; if (top) mode = left ? TopLeft : (right ? TopRight : Top); else if (bot) mode = left ? BottomLeft : (right ? BottomRight : Bottom); else mode = (x < width() / 2) ? Left : Right; invertedMoveOffset = rect().bottomRight() - moveOffset; setMouseCursor( mode ); startMoveResize(); } break; case Options::MouseNothing: // fall through default: replay = TRUE; break; } return replay; } // performs _NET_WM_MOVERESIZE void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction ) { if( direction == NET::Move ) performMouseCommand( Options::MouseMove, QPoint( x_root, y_root )); else if( direction >= NET::TopLeft && direction <= NET::Left ) { static const MousePosition convert[] = { TopLeft, Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left }; if (!isMovable()) return; geom=geometry(); if ( isMaximized() ) { // in case we were maximized, reset state max_mode = MaximizeRestore; maximizeChange(FALSE ); Events::raise( Events::UnMaximize ); info->setState( 0, NET::Max ); } buttonDown = TRUE; moveOffset = mapFromGlobal( QPoint( x_root, y_root )); invertedMoveOffset = rect().bottomRight() - moveOffset; mode = convert[ direction ]; setMouseCursor( mode ); startMoveResize(); } } void Client::keyPressEvent( uint key_code ) { if ( !isMove() && !isResize() ) return; bool is_control = key_code & Qt::CTRL; key_code = key_code & 0xffff; int delta = is_control?1:8; QPoint pos = QCursor::pos(); switch ( key_code ) { case Key_Left: pos.rx() -= delta; break; case Key_Right: pos.rx() += delta; break; case Key_Up: pos.ry() -= delta; break; case Key_Down: pos.ry() += delta; break; case Key_Space: case Key_Return: case Key_Enter: case Key_Escape: clearbound(); stopMoveResize(); setGeometry( geom ); buttonDown = FALSE; break; default: return; } QCursor::setPos( pos ); } static QCString getStringProperty(WId w, Atom prop, char separator=0) { Atom type; int format, status; unsigned long nitems = 0; unsigned long extra = 0; unsigned char *data = 0; QCString result = ""; XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler); status = XGetWindowProperty( qt_xdisplay(), w, prop, 0, 10000, FALSE, XA_STRING, &type, &format, &nitems, &extra, &data ); XSetErrorHandler(oldHandler); if ( status == Success) { if (data && separator) { for (int i=0; i<(int)nitems; i++) if (!data[i] && i+1<(int)nitems) data[i] = separator; } if (data) result = (const char*) data; XFree(data); } return result; } /*! Returns WINDOW_ROLE property for a given window. */ QCString Client::staticWindowRole(WId w) { return getStringProperty(w, qt_window_role); } /*! Returns SM_CLIENT_ID property for a given window. */ QCString Client::staticSessionId(WId w) { return getStringProperty(w, qt_sm_client_id); } /*! Returns WM_COMMAND property for a given window. */ QCString Client::staticWmCommand(WId w) { return getStringProperty(w, XA_WM_COMMAND, ' '); } /*! Returns WM_CLIENT_MACHINE property for a given window. Local machine is always returned as "localhost". */ QCString Client::staticWmClientMachine(WId w) { QCString result = getStringProperty(w, XA_WM_CLIENT_MACHINE); if (result.isEmpty()) { result = "localhost"; } else { // special name for the local machine (localhost) char hostnamebuf[80]; if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) { hostnamebuf[sizeof(hostnamebuf)-1] = 0; if (result == hostnamebuf) result = "localhost"; char *dot = strchr(hostnamebuf, '.'); if (dot && !(*dot = 0) && result == hostnamebuf) result = "localhost"; } } return result; } /*! Returns WM_CLIENT_LEADER property for a given window. */ Window Client::staticWmClientLeader(WId w) { Atom type; int format, status; unsigned long nitems = 0; unsigned long extra = 0; unsigned char *data = 0; Window result = w; XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler); status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000, FALSE, XA_WINDOW, &type, &format, &nitems, &extra, &data ); XSetErrorHandler(oldHandler); if (status == Success ) { if (data && nitems > 0) result = *((Window*) data); XFree(data); } return result; } void Client::getWmClientLeader() { wmClientLeaderWin = staticWmClientLeader(win); } /*! Returns WINDOW_ROLE for this client */ QCString Client::windowRole() { return staticWindowRole(win); } /*! Returns sessionId for this client, taken either from its window or from the leader window. */ QCString Client::sessionId() { QCString result = staticSessionId(win); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=win) result = staticSessionId(wmClientLeaderWin); return result; } /*! Returns the classhint resource name for this client, */ QCString Client::resourceName() { return resource_name; } /*! Returns the classhint resource class for this client, */ QCString Client::resourceClass() { return resource_class; } /*! Returns command property for this client, taken either from its window or from the leader window. */ QCString Client::wmCommand() { QCString result = staticWmCommand(win); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=win) result = staticWmCommand(wmClientLeaderWin); return result; } /*! Returns client machine for this client, taken either from its window or from the leader window. */ QCString Client::wmClientMachine() { QCString result = staticWmClientMachine(win); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=win) result = staticWmClientMachine(wmClientLeaderWin); return result; } /*! Returns client leader window for this client. Returns the client window itself if no leader window is defined. */ Window Client::wmClientLeader() { if (wmClientLeaderWin) return wmClientLeaderWin; return win; } void Client::activateLayout() { if ( layout() ) layout()->activate(); } NET::WindowType Client::windowType() const { NET::WindowType wt = info->windowType(); if ( wt == NET::Unknown ) wt = NET::Normal; if ( wt == NET::Menu ) { // ugly hack to support the times when NET::Menu meant NET::TopMenu // if it's as wide as the screen, not very high and has its upper-left // corner a bit above the screen's upper-left cornet, it's a topmenu if( x() == 0 && y() < 0 && y() > -10 && height() < 100 && width() == workspace()->geometry().width()) wt = NET::TopMenu; } return wt; } bool Client::wantsTabFocus() const { return (windowType() == NET::Normal || windowType() == NET::Dialog || windowType() == NET::Override ) && ( input || Ptakefocus ) && !skip_taskbar; } bool Client::wantsInput() const { return input; } /*! Returns whether the window is moveable or has a fixed position. !isMovable implies !isResizable. */ bool Client::isMovable() const { return may_move && ( windowType() == NET::Normal || windowType() == NET::Dialog || windowType() == NET::Toolbar || windowType() == NET::Menu ) && ( !isMaximized() || ( options->moveResizeMaximizedWindows || max_mode != MaximizeFull ) ); } bool Client::isDesktop() const { return windowType() == NET::Desktop; } bool Client::isDock() const { return windowType() == NET::Dock; } bool Client::isTopMenu() const { return windowType() == NET::TopMenu; } bool Client::isTool() const { return windowType() == NET::Tool; } /*! Returns \a area with the client's strut taken into account. Used from Workspace in updateClientArea. */ QRect Client::adjustedClientArea( const QRect& area ) const { QRect r = area; NETStrut strut = info->strut(); if ( strut.left > 0 ) r.setLeft( r.left() + (int) strut.left ); if ( strut.top > 0 ) r.setTop( r.top() + (int) strut.top ); if ( strut.right > 0 ) r.setRight( r.right() - (int) strut.right ); if ( strut.bottom > 0 ) r.setBottom( r.bottom() - (int) strut.bottom ); return r; } void Client::animateIconifyOrDeiconify( bool iconify) { if ( blockAnimation ) return; if ( !options->animateMinimize ) return; // the function is a bit tricky since it will ensure that an // animation action needs always the same time regardless of the // performance of the machine or the X-Server. float lf,rf,tf,bf,step; int speed = options->animateMinimizeSpeed; if ( speed > 10 ) speed = 10; if ( speed < 0 ) speed = 0; step = 40. * (11 - speed ); NETRect r = info->iconGeometry(); QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height ); if ( !icongeom.isValid() ) return; QPixmap pm = animationPixmap( iconify ? width() : icongeom.width() ); QRect before, after; if ( iconify ) { before = QRect( x(), y(), width(), pm.height() ); after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); } else { before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); after = QRect( x(), y(), width(), pm.height() ); } lf = (after.left() - before.left())/step; rf = (after.right() - before.right())/step; tf = (after.top() - before.top())/step; bf = (after.bottom() - before.bottom())/step; XGrabServer( qt_xdisplay() ); QRect area = before; QRect area2; QPixmap pm2; QTime t; t.start(); float diff; QPainter p ( workspace()->desktopWidget() ); bool need_to_clear = FALSE; QPixmap pm3; do { if (area2 != area){ pm = animationPixmap( area.width() ); pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() ); p.drawPixmap( area.x(), area.y(), pm ); if ( need_to_clear ) { p.drawPixmap( area2.x(), area2.y(), pm3 ); need_to_clear = FALSE; } area2 = area; } XFlush(qt_xdisplay()); XSync( qt_xdisplay(), FALSE ); diff = t.elapsed(); if (diff > step) diff = step; area.setLeft(before.left() + int(diff*lf)); area.setRight(before.right() + int(diff*rf)); area.setTop(before.top() + int(diff*tf)); area.setBottom(before.bottom() + int(diff*bf)); if (area2 != area ) { if ( area2.intersects( area ) ) p.drawPixmap( area2.x(), area2.y(), pm2 ); else { // no overlap, we can clear later to avoid flicker pm3 = pm2; need_to_clear = TRUE; } } } while ( t.elapsed() < step); if (area2 == area || need_to_clear ) p.drawPixmap( area2.x(), area2.y(), pm2 ); p.end(); XUngrabServer( qt_xdisplay() ); } /*! The pixmap shown during iconify/deiconify animation */ QPixmap Client::animationPixmap( int w ) { QFont font = options->font(isActive()); QFontMetrics fm( font ); QPixmap pm( w, fm.lineSpacing() ); pm.fill( options->color(Options::TitleBar, isActive() || isIconified() ) ); QPainter p( &pm ); p.setPen(options->color(Options::Font, isActive() || isIconified() )); p.setFont(options->font(isActive())); p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() ); return pm; } void Client::autoRaise() { workspace()->raiseClient( this ); delete autoRaiseTimer; autoRaiseTimer = 0; } void Client::shadeHover() { setShade(FALSE, 1); delete shadeHoverTimer; shadeHoverTimer = 0; } /*! Clones settings from other client. Used in Workspace::slotResetAllClients() */ void Client::cloneMode(Client *client) { shaded = client->shaded; geom_restore = client->geom_restore; max_mode = client->max_mode; state = client->state; QString caption = client->caption(); setCaption(caption); info->setVisibleName( caption.utf8() ); } NETWinInfo * Client::netWinInfo() { return static_cast(info); } /*! The transient_for window may be embedded in another application, so kwin cannot see it. Try to find the managed client for the window and fix the transient_for property if possible. */ void Client::verifyTransientFor() { unsigned int nwins; Window root_return, parent_return, *wins; if ( transient_for == 0 || transient_for == win ) return; WId old_transient_for = transient_for; while ( transient_for && transient_for != workspace()->rootWin() && !workspace()->findClient( transient_for ) ) { wins = 0; int r = XQueryTree(qt_xdisplay(), transient_for, &root_return, &parent_return, &wins, &nwins); if ( wins ) XFree((void *) wins); if ( r == 0) break; transient_for = parent_return; } if ( old_transient_for != transient_for && workspace()->findClient( transient_for ) ) XSetTransientForHint( qt_xdisplay(), win, transient_for ); else transient_for = old_transient_for; // nice try } /*!\reimp */ QString Client::caption() const { return cap; } /*!\reimp */ void Client::setCaption( const QString & c) { cap = c; } NoBorderClient::NoBorderClient( Workspace *ws, WId w, QWidget *parent, const char *name ) : Client( ws, w, parent, name ) { QHBoxLayout* h = new QHBoxLayout( this ); h->addWidget( windowWrapper() ); } NoBorderClient::~NoBorderClient() { } QPixmap * kwin_get_menu_pix_hack() { static QPixmap p; if ( p.isNull() ) p = SmallIcon( "bx2" ); return &p; } #include "client.moc" diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp index 45651b888d..798dc76b3e 100644 --- a/kwin/workspace.cpp +++ b/kwin/workspace.cpp @@ -1,4228 +1,4228 @@ /***************************************************************** kwin - the KDE window manager Copyright (C) 1999, 2000 Matthias Ettrich ******************************************************************/ //#define QT_CLEAN_NAMESPACE #define select kwin_hide_select #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "placement.h" #include "workspace.h" #include "client.h" #include "tabbox.h" #include "popupinfo.h" #include "atoms.h" #include "plugins.h" #include "events.h" #include "killwindow.h" #include #include #include #include #include #include #include #include #include #include #include const int XIconicState = IconicState; #undef IconicState #include #include // Possible protoypes for select() were hidden as `kwin_hide_select. // Undo the hiding definition and defines an acceptable prototype. // This is how Qt does this. It should work where Qt works. #ifdef HAVE_SYS_SELECT_H #include #endif #undef select extern "C" int select(int,void*,void*,void*,struct timeval*); namespace KWinInternal { // NET WM Protocol handler class class RootInfo : public NETRootInfo { public: RootInfo( KWinInternal::Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr, int scr= -1) : NETRootInfo( dpy, w, name, pr, scr ) { workspace = ws; } ~RootInfo() {} void changeNumberOfDesktops(int n) { workspace->setNumberOfDesktops( n ); } void changeCurrentDesktop(int d) { workspace->setCurrentDesktop( d ); } void changeActiveWindow(Window w) { KWinInternal::Client* c = workspace->findClient( (WId) w ); if ( c ) workspace->activateClient( c ); } void closeWindow(Window w) { KWinInternal::Client* c = workspace->findClient( (WId) w ); if ( c ) { c->closeWindow(); } } void moveResize(Window w, int x_root, int y_root, unsigned long direction) { KWinInternal::Client* c = workspace->findClient( (WId) w ); if ( c ) { c->NETMoveResize( x_root, y_root, (Direction)direction); } } private: KWinInternal::Workspace* workspace; }; class WorkspacePrivate { public: WorkspacePrivate() : 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), electric_time_first(0), electric_time_last(0), movingClient(0), layoutOrientation(Qt::Vertical), layoutX(-1), layoutY(2) { }; ~WorkspacePrivate() {}; KStartupInfo* startup; bool electric_have_borders; int electric_current_border; WId electric_top_border; WId electric_bottom_border; WId electric_left_border; WId electric_right_border; int electricLeft; int electricRight; int electricTop; int electricBottom; Time electric_time_first; Time electric_time_last; QPoint electric_push_point; Client *movingClient; Qt::Orientation layoutOrientation; int layoutX; int layoutY; Placement *initPositioning; }; }; using namespace KWinInternal; extern int kwin_screen_number; QString Workspace::desktopName( int desk ) { return QString::fromUtf8( rootInfo->desktopName( desk ) ); } extern Time qt_x_time; extern void kwin_updateTime(); // used to store the return values of // XShapeQueryExtension. // Necessary since shaped window are an extension to X static int kwin_has_shape = 0; static int kwin_shape_event = 0; static bool block_focus = FALSE; static Window null_focus_window = 0; // does the window w need a shape combine mask around it? bool Shape::hasShape( WId w){ int xws, yws, xbs, ybs; unsigned int wws, hws, wbs, hbs; int boundingShaped = 0, clipShaped = 0; if (!kwin_has_shape) return FALSE; XShapeQueryExtents(qt_xdisplay(), w, &boundingShaped, &xws, &yws, &wws, &hws, &clipShaped, &xbs, &ybs, &wbs, &hbs); return boundingShaped != 0; } int Shape::shapeEvent() { return kwin_shape_event; } bool Motif::noBorder( WId w ) { struct MwmHints { ulong flags; ulong functions; ulong decorations; long input_mode; ulong status; }; Atom type; int format; unsigned long length, after; unsigned char* data; MwmHints* hints = 0; if ( XGetWindowProperty( qt_xdisplay(), w, atoms->motif_wm_hints, 0, 5, FALSE, atoms->motif_wm_hints, &type, &format, &length, &after, &data ) == Success ) { if ( data ) hints = (MwmHints*) data; } bool result = FALSE; if ( hints ) { if ( hints->flags & (1L << 1 ) ) { // // MWM_HINTS_DECORATIONS; if ( hints->decorations == 0 ) result = TRUE; } XFree( data ); } return result; } /*! Creates a new client for window \a w, depending on certain hints (like Motif hints and the NET_WM_TYPE. Shaped windows always get a NoBorderClient. */ Client* Workspace::clientFactory( WId w ) { NETWinInfo ni( qt_xdisplay(), w, root, NET::WMWindowType ); if ( (ni.windowType() == NET::Normal || ni.windowType() == NET::Unknown) && Motif::noBorder( w ) ) return new NoBorderClient( this, w ); switch ( ni.windowType() ) { // when adding new window types, add a fallback for them in PluginMgr::createClient() case NET::Desktop: { XLowerWindow( qt_xdisplay(), w ); Client * c = new NoBorderClient( this, w); c->setSticky( TRUE ); return c; } case NET::Dock: { Client * c = new NoBorderClient( this, w ); c->setSticky( TRUE ); return c; } case NET::TopMenu: { Client* c = new NoBorderClient( this, w ); c->setStaysOnTop( true ); return c; } case NET::Override: return new NoBorderClient( this, w); case NET::Menu: { Window dummy1; int x, y; unsigned int width, height, dummy2, dummy3; XGetGeometry( qt_xdisplay(), w, &dummy1, &x, &y, &width, &height, &dummy2, &dummy3 ); // ugly hack to support the times when NET::Menu meant NET::TopMenu // if it's as wide as the screen, not very high and has its upper-left // corner a bit above the screen's upper-left cornet, it's a topmenu if( x == 0 && y < 0 && y > -10 && height < 100 && int(width) == geometry().width()) { Client* c = new NoBorderClient( this, w); c->setStaysOnTop( true ); return c; } break; } case NET::Tool: break; default: break; } if ( Shape::hasShape( w ) ){ return new NoBorderClient( this, w ); } return ( mgr->createClient( this, w, ni.windowType()) ); } Workspace *Workspace::_self = 0; // 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 dissagree 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 ) : QObject (0, "workspace"), DCOPObject ("KWinInterface"), current_desktop (0), number_of_desktops(0), desktop_widget (0), active_client (0), last_active_client (0), should_get_focus (0), most_recently_raised (0), control_grab (false), tab_grab (false), mouse_emulation (false), focus_change (true), tab_box (0), popupinfo (0), popup (0), desk_popup (0), keys (0), root (0) { _self = this; d = new WorkspacePrivate; mgr = new PluginMgr; root = qt_xrootwin(); default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() ); installed_colormap = default_colormap; session.setAutoDelete( TRUE ); if ( restore ) loadSessionInfo(); loadFakeSessionInfo(); (void) QApplication::desktop(); // trigger creation of desktop widget desktop_widget = new QWidget( 0, "desktop_widget", Qt::WType_Desktop | Qt::WPaintUnclipped ); // call this before XSelectInput() on the root window d->startup = new KStartupInfo( false, this ); // select windowmanager privileges XSelectInput(qt_xdisplay(), root, KeyPressMask | PropertyChangeMask | ColormapChangeMask | SubstructureRedirectMask | SubstructureNotifyMask ); int dummy; kwin_has_shape = XShapeQueryExtension(qt_xdisplay(), &kwin_shape_event, &dummy); // compatibility long data = 1; XChangeProperty( qt_xdisplay(), qt_xrootwin(), atoms->kwin_running, atoms->kwin_running, 32, PropModeAppend, (unsigned char*) &data, 1 ); initShortcuts(); tab_box = new TabBox( this ); popupinfo = new PopupInfo( ); init(); } void Workspace::init() { QRect r = QApplication::desktop()->geometry(); d->electricTop = r.top(); d->electricBottom = r.bottom(); d->electricLeft = r.left(); d->electricRight = r.right(); d->electric_current_border = 0; if (options->electricBorders() == Options::ElectricAlways) createBorderWindows(); supportWindow = new QWidget; unsigned long protocols = NET::Supported | NET::SupportingWMCheck | NET::ClientList | NET::ClientListStacking | NET::NumberOfDesktops | NET::CurrentDesktop | NET::ActiveWindow | NET::WorkArea | NET::CloseWindow | NET::DesktopNames | NET::KDESystemTrayWindows | NET::CloseWindow | NET::WMName | NET::WMVisibleName | NET::WMDesktop | NET::WMWindowType | NET::WMState | NET::WMStrut | NET::WMIconGeometry | NET::WMIcon | NET::WMPid | NET::WMMoveResize | NET::WMKDESystemTrayWinFor | NET::WMKDEFrameStrut ; rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin", protocols, qt_xscreen() ); loadDesktopSettings(); setCurrentDesktop( 1 ); // now we know how many desktops we'll, thus, we initialise the positioning object d->initPositioning = new Placement(this); unsigned int i, nwins; Window root_return, parent_return, *wins; XWindowAttributes attr; connect(&resetTimer, SIGNAL(timeout()), this, SLOT(slotResetAllClients())); connect(&reconfigureTimer, SIGNAL(timeout()), this, SLOT(slotReconfigure())); connect(mgr, SIGNAL(resetAllClients()), this, SLOT(slotResetAllClients())); connect(kapp, SIGNAL(appearanceChanged()), this, SLOT(slotReconfigure())); connect(kapp, SIGNAL(settingsChanged(int)), this, SLOT(slotSettingsChanged(int))); connect(&focusEnsuranceTimer, SIGNAL(timeout()), this, SLOT(focusEnsurance())); XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins); for (i = 0; i < nwins; i++) { XGetWindowAttributes(qt_xdisplay(), wins[i], &attr); if (attr.override_redirect ) continue; if (attr.map_state != IsUnmapped) { if ( addSystemTrayWin( wins[i] ) ) continue; Client* c = clientFactory( wins[i] ); addClient( c ); c->manage( TRUE ); if ( !c->wantsTabFocus() ) focus_chain.remove( c ); if ( root != qt_xrootwin() ) { // TODO may use QWidget:.create XReparentWindow( qt_xdisplay(), c->winId(), root, 0, 0 ); c->move(0,0); } } } if ( wins ) XFree((void *) wins); propagateClients(); updateClientArea(); raiseElectricBorders(); // NETWM spec says we have to set it to (0,0) if we don't support it rootInfo->setDesktopViewport( 1, NETPoint()); } Workspace::~Workspace() { for ( ClientList::ConstIterator it = desktops.fromLast(); it != desktops.end(); --it) { WId win = (*it)->window(); delete (*it); XMapWindow( qt_xdisplay(), win ); XLowerWindow( qt_xdisplay(), win ); } for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { storeFakeSessionInfo( *it ); WId win = (*it)->window(); delete (*it); XMapWindow( qt_xdisplay(), win ); } delete desktop_widget; delete tab_box; delete popupinfo; delete popup; if ( root == qt_xrootwin() ) XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running); writeFakeSessionInfo(); KGlobal::config()->sync(); delete rootInfo; delete supportWindow; delete mgr; delete d->startup; delete d->initPositioning; delete d; _self = 0; } /*! Handles workspace specific XEvents */ bool Workspace::workspaceEvent( XEvent * e ) { if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) ) { mouse_emulation = FALSE; XUngrabKeyboard( qt_xdisplay(), qt_x_time ); } if ( e->type == PropertyNotify || e->type == ClientMessage ) { if ( netCheck( e ) ) return TRUE; } if ( e->type == FocusIn ) focusEnsuranceTimer.stop(); else if ( e->type == FocusOut ) focusEnsuranceTimer.start(50); Client * c = findClient( e->xany.window ); if ( c ) return c->windowEvent( e ); switch (e->type) { case ButtonPress: if ( tab_grab || control_grab ) { XUngrabKeyboard(qt_xdisplay(), qt_x_time); XUngrabPointer( qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); tab_grab = control_grab = false; return TRUE; } case ButtonRelease: case MotionNotify: break; case CreateNotify: if ( e->xcreatewindow.parent == root && !QWidget::find( e->xcreatewindow.window) ) { timeval tv; gettimeofday( &tv, NULL ); unsigned long now = tv.tv_sec * 10 + tv.tv_usec / 100000; XChangeProperty(qt_xdisplay(), e->xcreatewindow.window, atoms->kde_net_user_time, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&now, 1); } break; case UnmapNotify: // this is special due to // SubstructureNotifyMask. e->xany.window is the window the // event is reported to. Take care not to confuse Qt. c = findClient( e->xunmap.window ); if ( c ) return c->windowEvent( e ); // check for system tray windows if ( removeSystemTrayWin( e->xunmap.window ) ) { // If the system tray gets destroyed, the system tray // icons automatically get unmapped, reparented and mapped // again to the closest non-client ancestor due to // QXEmbed's SaveSet feature. Unfortunatly with kicker // this closest ancestor is not the root window, but our // decoration, so we reparent explicitely back to the root // window. XEvent ev; WId w = e->xunmap.window; if ( XCheckTypedWindowEvent (qt_xdisplay(), w, ReparentNotify, &ev) ){ if ( ev.xreparent.parent != root ) { XReparentWindow( qt_xdisplay(), w, root, 0, 0 ); addSystemTrayWin( w ); } } return TRUE; } return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt case MapNotify: return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt case ReparentNotify: c = findClient( e->xreparent.window ); if ( c ) (void) c->windowEvent( e ); //do not confuse Qt with these events. After all, _we_ are the //window manager who does the reparenting. return TRUE; case DestroyNotify: if ( removeSystemTrayWin( e->xdestroywindow.window ) ) return TRUE; return destroyClient( findClient( e->xdestroywindow.window ) ); case MapRequest: kwin_updateTime(); checkStartOnDesktop( e->xmaprequest.window ); c = findClient( e->xmaprequest.window ); if ( !c ) { if ( e->xmaprequest.parent == root ) { //###TODO store previously destroyed client ids if ( addSystemTrayWin( e->xmaprequest.window ) ) return TRUE; c = clientFactory( e->xmaprequest.window ); if ( root != qt_xrootwin() ) { // TODO may use QWidget:.create XReparentWindow( qt_xdisplay(), c->winId(), root, 0, 0 ); } addClient( c ); } } if ( c ) { bool b = c->windowEvent( e ); if ( !c->wantsTabFocus() ) focus_chain.remove( c ); return b; } break; case EnterNotify: if ( QWhatsThis::inWhatsThisMode() ) { QWidget* w = QWidget::find( e->xcrossing.window ); if ( w ) QWhatsThis::leaveWhatsThisMode(); } if (d->electric_have_borders && (e->xcrossing.window == d->electric_top_border || e->xcrossing.window == d->electric_left_border || e->xcrossing.window == d->electric_bottom_border || e->xcrossing.window == d->electric_right_border)) { // the user entered an electric border electricBorder(e); } break; case LeaveNotify: if ( !QWhatsThis::inWhatsThisMode() ) break; c = findClientWidthId( e->xcrossing.window ); if ( c && e->xcrossing.detail != NotifyInferior ) QWhatsThis::leaveWhatsThisMode(); break; case ConfigureRequest: c = findClient( e->xconfigurerequest.window ); if ( c ) return c->windowEvent( e ); else if ( e->xconfigurerequest.parent == root ) { XWindowChanges wc; unsigned int value_mask = 0; wc.border_width = 0; wc.x = e->xconfigurerequest.x; wc.y = e->xconfigurerequest.y; wc.width = e->xconfigurerequest.width; wc.height = e->xconfigurerequest.height; wc.sibling = None; wc.stack_mode = Above; value_mask = e->xconfigurerequest.value_mask | CWBorderWidth; XConfigureWindow( qt_xdisplay(), e->xconfigurerequest.window, value_mask, & wc ); return TRUE; } break; case KeyPress: if ( mouse_emulation ) return keyPressMouseEmulation( e->xkey ); return keyPress(e->xkey); case KeyRelease: if ( mouse_emulation ) return FALSE; return keyRelease(e->xkey); break; case FocusIn: break; case FocusOut: break; default: if ( e->type == Shape::shapeEvent() ) { c = findClient( ((XShapeEvent *)e)->window ); if ( c ) c->updateShape(); } break; } return FALSE; } bool Workspace::hasClient(Client* c) { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it) == c ) return TRUE; } return FALSE; } /*! Finds the client that embedds the window \a w */ Client* Workspace::findClient( WId w ) const { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->window() == w ) return *it; } for ( ClientList::ConstIterator it = desktops.begin(); it != desktops.end(); ++it) { if ( (*it)->window() == w ) return *it; } return 0; } /*! Finds the client with window id \a w */ Client* Workspace::findClientWidthId( WId w ) const { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->winId() == w ) return *it; } return 0; } /*! Returns the workspace's geometry \sa clientArea() */ QRect Workspace::geometry() const { if ( root == qt_xrootwin() ) return QRect( QPoint(0, 0), QApplication::desktop()->size() ); else { // todo caching, keep track of configure notify etc. QRect r; XWindowAttributes attr; if (XGetWindowAttributes(qt_xdisplay(), root, &attr)){ r.setRect(0, 0, attr.width, attr.height ); } return r; } } void Workspace::removeClient( Client* c) { clients.remove( c ); stacking_order.remove( c ); focus_chain.remove( c ); propagateClients(); } /* Destroys the client \a c */ bool Workspace::destroyClient( Client* c) { if ( !c ) return FALSE; - if (c == popup_client && popup) + if (c == active_client && popup) popup->close(); storeFakeSessionInfo( c ); if (clients.contains(c)) removeClient(c); c->invalidateWindow(); clientHidden( c ); if ( desktops.contains(c) ) desktops.remove(c); if ( c == most_recently_raised ) most_recently_raised = 0; if ( c == should_get_focus ) should_get_focus = 0; if ( c == active_client ) active_client = 0; if ( c == last_active_client ) last_active_client = 0; delete c; if (tab_grab) tab_box->repaint(); updateClientArea(); return TRUE; } /*! Handles alt-tab / control-tab */ #include bool areKeySymXsDepressed( bool bAll, int nKeySyms, ... ) { va_list args; char keymap[32]; kdDebug(125) << "areKeySymXsDepressed: " << (bAll ? "all of " : "any of ") << nKeySyms << endl; va_start( args, nKeySyms ); XQueryKeymap( qt_xdisplay(), keymap ); for( int iKeySym = 0; iKeySym < nKeySyms; iKeySym++ ) { uint keySymX = va_arg( args, uint ); uchar keyCodeX = XKeysymToKeycode( qt_xdisplay(), keySymX ); int i = keyCodeX / 8; char mask = 1 << (keyCodeX - (i * 8)); kdDebug(125) << iKeySym << ": keySymX=0x" << QString::number( keySymX, 16 ) << " i=" << i << " mask=0x" << QString::number( mask, 16 ) << " keymap[i]=0x" << QString::number( keymap[i], 16 ) << endl; // Abort if bad index value, if( i < 0 || i >= 32 ) return false; // If ALL keys passed need to be depressed, if( bAll ) { if( (keymap[i] & mask) == 0 ) return false; } else { // If we are looking for ANY key press, and this key is depressed, if( keymap[i] & mask ) return true; } } // If we were looking for ANY key press, then none was found, return false, // If we were looking for ALL key presses, then all were found, return true. return bAll; } bool areModKeysDepressed( const KShortcut& cut ) { uint rgKeySyms[10]; int nKeySyms = 0; int mod = cut.seq(0).key(0).modFlags(); if ( mod & KKey::SHIFT ) { rgKeySyms[nKeySyms++] = XK_Shift_L; rgKeySyms[nKeySyms++] = XK_Shift_R; } if ( mod & KKey::CTRL ) { rgKeySyms[nKeySyms++] = XK_Control_L; rgKeySyms[nKeySyms++] = XK_Control_R; } if( mod & KKey::ALT ) { rgKeySyms[nKeySyms++] = XK_Alt_L; rgKeySyms[nKeySyms++] = XK_Alt_R; } if( mod & KKey::WIN ) { // HACK: it would take a lot of code to determine whether the Win key // is associated with Super or Meta, so check for both rgKeySyms[nKeySyms++] = XK_Super_L; rgKeySyms[nKeySyms++] = XK_Super_R; rgKeySyms[nKeySyms++] = XK_Meta_L; rgKeySyms[nKeySyms++] = XK_Meta_R; } // Is there a better way to push all 8 integer onto the stack? return areKeySymXsDepressed( false, nKeySyms, rgKeySyms[0], rgKeySyms[1], rgKeySyms[2], rgKeySyms[3], rgKeySyms[4], rgKeySyms[5], rgKeySyms[6], rgKeySyms[7] ); } void Workspace::slotWalkThroughWindows() { if ( root != qt_xrootwin() ) return; if ( tab_grab || control_grab ) return; if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable() ) { //XUngrabKeyboard(qt_xdisplay(), qt_x_time); // need that because of accelerator raw mode // CDE style raise / lower CDEWalkThroughWindows( true ); } else { if ( areModKeysDepressed( cutWalkThroughWindows ) ) { if ( startKDEWalkThroughWindows() ) KDEWalkThroughWindows( true ); } else // if the shortcut has no modifiers, don't show the tabbox, but // simply go to the next window; if the shortcut has no modifiers, // the only sane thing to do is to release the key immediately // anyway, so the tabbox wouldn't appear anyway // it's done this way without grabbing because with grabbing // the keyboard wasn't ungrabbed and I really have no idea why // KDEOneStepThroughWindows( true ); } } void Workspace::slotWalkBackThroughWindows() { if ( root != qt_xrootwin() ) return; if( tab_grab || control_grab ) return; if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable() ) { // CDE style raise / lower CDEWalkThroughWindows( true ); } else { if ( areModKeysDepressed( cutWalkThroughWindowsReverse ) ) { if ( startKDEWalkThroughWindows() ) KDEWalkThroughWindows( false ); } else { KDEOneStepThroughWindows( false ); } } } void Workspace::slotWalkThroughDesktops() { if ( root != qt_xrootwin() ) return; if( tab_grab || control_grab ) return; if ( areModKeysDepressed( cutWalkThroughDesktops ) ) { if ( startWalkThroughDesktops() ) walkThroughDesktops( true ); } else { oneStepThroughDesktops( true ); } } void Workspace::slotWalkBackThroughDesktops() { if ( root != qt_xrootwin() ) return; if( tab_grab || control_grab ) return; if ( areModKeysDepressed( cutWalkThroughDesktopsReverse ) ) { if ( startWalkThroughDesktops() ) walkThroughDesktops( false ); } else { oneStepThroughDesktops( false ); } } void Workspace::slotWalkThroughDesktopList() { if ( root != qt_xrootwin() ) return; if( tab_grab || control_grab ) return; if ( areModKeysDepressed( cutWalkThroughDesktopList ) ) { if ( startWalkThroughDesktopList() ) walkThroughDesktops( true ); } else { oneStepThroughDesktopList( true ); } } void Workspace::slotWalkBackThroughDesktopList() { if ( root != qt_xrootwin() ) return; if( tab_grab || control_grab ) return; if ( areModKeysDepressed( cutWalkThroughDesktopListReverse ) ) { if ( startWalkThroughDesktopList() ) walkThroughDesktops( false ); } else { oneStepThroughDesktopList( false ); } } bool Workspace::startKDEWalkThroughWindows() { if ( XGrabPointer( qt_xdisplay(), root, TRUE, (uint)(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask), GrabModeAsync, GrabModeAsync, None, None, qt_x_time ) != GrabSuccess ) { return FALSE; } if ( XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, qt_x_time) != GrabSuccess ) { XUngrabPointer( qt_xdisplay(), qt_x_time); return FALSE; } tab_grab = TRUE; keys->setEnabled( false ); tab_box->setMode( TabBox::WindowsMode ); tab_box->reset(); return TRUE; } bool Workspace::startWalkThroughDesktops( int mode ) { if ( XGrabPointer( qt_xdisplay(), root, TRUE, (uint)(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask), GrabModeAsync, GrabModeAsync, None, None, qt_x_time ) != GrabSuccess ) { return FALSE; } if ( XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, qt_x_time) != GrabSuccess ) { XUngrabPointer( qt_xdisplay(), qt_x_time); return FALSE; } control_grab = TRUE; keys->setEnabled( false ); tab_box->setMode( (TabBox::Mode) mode ); tab_box->reset(); return TRUE; } bool Workspace::startWalkThroughDesktops() { return startWalkThroughDesktops( TabBox::DesktopMode ); } bool Workspace::startWalkThroughDesktopList() { return startWalkThroughDesktops( TabBox::DesktopListMode ); } void Workspace::KDEWalkThroughWindows( bool forward ) { tab_box->nextPrev( forward ); tab_box->delayedShow(); } void Workspace::walkThroughDesktops( bool forward ) { tab_box->nextPrev( forward ); tab_box->delayedShow(); } void Workspace::CDEWalkThroughWindows( bool forward ) { Client* c = topClientOnDesktop(); Client* nc = c; if ( !forward ){ do { nc = previousStaticClient(nc); } while (nc && nc != c && (!nc->isOnDesktop(currentDesktop()) || nc->isIconified() || !nc->wantsTabFocus() ) ); } else do { nc = nextStaticClient(nc); } while (nc && nc != c && (!nc->isOnDesktop(currentDesktop()) || nc->isIconified() || !nc->wantsTabFocus() ) ); if (c && c != nc) lowerClient( c ); if (nc) { if ( options->focusPolicyIsReasonable() ) activateClient( nc ); else raiseClient( nc ); } } void Workspace::KDEOneStepThroughWindows( bool forward ) { tab_box->setMode( TabBox::WindowsMode ); tab_box->reset(); tab_box->nextPrev( forward ); if ( tab_box->currentClient() ){ activateClient( tab_box->currentClient() ); } } void Workspace::oneStepThroughDesktops( bool forward, int mode ) { tab_box->setMode( (TabBox::Mode) mode ); tab_box->reset(); tab_box->nextPrev( forward ); if ( tab_box->currentDesktop() != -1 ) setCurrentDesktop( tab_box->currentDesktop() ); } void Workspace::oneStepThroughDesktops( bool forward ) { oneStepThroughDesktops( forward, TabBox::DesktopMode ); } void Workspace::oneStepThroughDesktopList( bool forward ) { oneStepThroughDesktops( forward, TabBox::DesktopListMode ); } /*! Handles holding alt-tab / control-tab */ bool Workspace::keyPress(XKeyEvent& ev) { if ( root != qt_xrootwin() ) return FALSE; KKeyNative keyX( (XEvent*)&ev ); uint keyQt = keyX.keyCodeQt(); kdDebug(125) << "Workspace::keyPress( " << keyX.key().toString() << " )" << endl; if (d->movingClient) { d->movingClient->keyPressEvent(keyQt); return TRUE; } if (tab_grab){ bool forward = cutWalkThroughWindows.contains( keyX ); bool backward = cutWalkThroughWindowsReverse.contains( keyX ); if (forward || backward){ kdDebug(125) << "== " << cutWalkThroughWindows.toString() << " or " << cutWalkThroughWindowsReverse.toString() << endl; KDEWalkThroughWindows( forward ); } } else if (control_grab){ bool forward = cutWalkThroughDesktops.contains( keyX ) || cutWalkThroughDesktopList.contains( keyX ); bool backward = cutWalkThroughDesktopsReverse.contains( keyX ) || cutWalkThroughDesktopListReverse.contains( keyX ); if (forward || backward) walkThroughDesktops(forward); } if (control_grab || tab_grab){ if ((keyQt & 0xffff) == Qt::Key_Escape){ XUngrabKeyboard(qt_xdisplay(), qt_x_time); XUngrabPointer( qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); tab_grab = FALSE; control_grab = FALSE; } return TRUE; } return FALSE; } /*! Handles alt-tab / control-tab releasing */ bool Workspace::keyRelease(XKeyEvent& ev) { if ( root != qt_xrootwin() ) return FALSE; if( !tab_grab && !control_grab ) return FALSE; unsigned int mk = ev.state & (KKeyNative::modX(KKey::SHIFT) | KKeyNative::modX(KKey::CTRL) | KKeyNative::modX(KKey::ALT) | KKeyNative::modX(KKey::WIN)); // ev.state is state before the key release, so just checking mk being 0 isn't enough // using XQueryPointer() also doesn't seem to work well, so the check that all // modifiers are released: only one modifier is active and the currently released // key is this modifier - if yes, release the grab int mod_index = -1; for( int i = ShiftMapIndex; i <= Mod5MapIndex; ++i ) if(( mk & ( 1 << i )) != 0 ) { if( mod_index >= 0 ) return FALSE; mod_index = i; } bool release = false; if( mod_index == -1 ) release = true; else { XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay()); for (int i=0; imax_keypermod; i++) if (xmk->modifiermap[xmk->max_keypermod * mod_index + i] == ev.keycode) release = true; XFreeModifiermap(xmk); } if( !release ) return FALSE; if (tab_grab){ XUngrabPointer( qt_xdisplay(), qt_x_time); XUngrabKeyboard(qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); tab_grab = false; if ( tab_box->currentClient() ){ activateClient( tab_box->currentClient() ); } } if (control_grab){ XUngrabPointer( qt_xdisplay(), qt_x_time); XUngrabKeyboard(qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); control_grab = False; if ( tab_box->currentDesktop() != -1 ) { setCurrentDesktop( tab_box->currentDesktop() ); // popupinfo->showInfo( desktopName(currentDesktop()) ); // AK - not sure } } return FALSE; } //#undef XMODMASK int Workspace::nextDesktop( int iDesktop ) const { int i = desktop_focus_chain.find( iDesktop ); if( i >= 0 && i+1 < (int)desktop_focus_chain.size() ) return desktop_focus_chain[i+1]; else if( desktop_focus_chain.size() > 0 ) return desktop_focus_chain[ 0 ]; else return 1; } int Workspace::previousDesktop( int iDesktop ) const { int i = desktop_focus_chain.find( iDesktop ); if( i-1 >= 0 ) return desktop_focus_chain[i-1]; else if( desktop_focus_chain.size() > 0 ) return desktop_focus_chain[desktop_focus_chain.size()-1]; else return numberOfDesktops(); } void Workspace::circulateDesktopApplications() { if ( desktops.count() <= 1 ) return; Client* first = desktops.first(); desktops.remove( first ); desktops.append( first ); Window* new_stack = new Window[ desktops.count() + 1 ]; int i = 0; for ( ClientList::ConstIterator it = desktops.fromLast(); it != desktops.end(); --it) new_stack[i++] = (*it)->winId(); XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; } void Workspace::addClient( Client* c ) { if ( c->isDesktop() ) { if ( !desktops.isEmpty() ) { Client* first = desktops.first(); Window stack[2]; stack[0] = first->winId(); stack[1] = c->winId(); XRestackWindows( qt_xdisplay(), stack, 2 ); desktops.prepend( c ); circulateDesktopApplications(); } else { c->lower(); desktops.append( c ); } } else { if ( c->wantsTabFocus() ) focus_chain.append( c ); clients.append( c ); stacking_order.append( c ); } } /*! auxiliary functions to travers all clients according the focus order. Useful for kwm´s Alt-tab feature. */ Client* Workspace::nextClient( Client* c ) const { if ( focus_chain.isEmpty() ) return 0; ClientList::ConstIterator it = focus_chain.find( c ); if ( it == focus_chain.end() ) return focus_chain.last(); if ( it == focus_chain.begin() ) return focus_chain.last(); --it; return *it; } /*! auxiliary functions to travers all clients according the focus order. Useful for kwm´s Alt-tab feature. */ Client* Workspace::previousClient( Client* c ) const { if ( focus_chain.isEmpty() ) return 0; ClientList::ConstIterator it = focus_chain.find( c ); if ( it == focus_chain.end() ) return focus_chain.first(); ++it; if ( it == focus_chain.end() ) return focus_chain.first(); return *it; } /*! auxiliary functions to travers all clients according the static order. Useful for the CDE-style Alt-tab feature. */ Client* Workspace::nextStaticClient( Client* c ) const { if ( !c || clients.isEmpty() ) return 0; ClientList::ConstIterator it = clients.find( c ); if ( it == clients.end() ) return clients.first(); ++it; if ( it == clients.end() ) return clients.first(); return *it; } /*! auxiliary functions to travers all clients according the static order. Useful for the CDE-style Alt-tab feature. */ Client* Workspace::previousStaticClient( Client* c ) const { if ( !c || clients.isEmpty() ) return 0; ClientList::ConstIterator it = clients.find( c ); if ( it == clients.end() ) return clients.last(); if ( it == clients.begin() ) return clients.last(); --it; return *it; } /*! Returns topmost visible client. Windows on the dock, the desktop or of any other special kind are excluded. */ Client* Workspace::topClientOnDesktop() const { if ( most_recently_raised && stacking_order.contains( most_recently_raised ) && most_recently_raised->isVisible() ) return most_recently_raised; for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { if ( !(*it)->isDesktop() && (*it)->isVisible() && (*it)->wantsTabFocus() ) return *it; } return 0; } /*! Informs the workspace about the active client, i.e. the client that has the focus (or None if no client has the focus). This functions is called by the client itself that gets focus. It has no other effect than fixing the focus chain and the return value of activeClient(). And of course, to propagate the active client to the world. */ void Workspace::setActiveClient( Client* c ) { if ( active_client == c ) return; + if( popup ) + popup->close(); if ( active_client ) { active_client->setActive( FALSE ); if ( active_client->isFullScreen() && active_client->staysOnTop() && c && c->mainClient() != active_client->mainClient() ) { active_client->setStaysOnTop( FALSE ); lowerClient( active_client ); } } active_client = c; last_active_client = active_client; if ( active_client ) { if ( active_client->isFullScreen() && !active_client->staysOnTop() ) { active_client->setStaysOnTop( TRUE ); raiseClient( active_client ); } focus_chain.remove( c ); if ( c->wantsTabFocus() ) focus_chain.append( c ); } // toplevel menubar handling Client* main = 0; if ( active_client ) main = active_client->mainClient(); // show the new menu bar first... Client* menubar = 0; bool has_full_screen = false; for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->isTopMenu() && (*it)->mainClient() == main ) { menubar = *it; } if ( (*it)->isVisible() && (*it)->isFullScreen() && !(*it)->isDesktop() && (*it)->staysOnTop() ) { has_full_screen = true; } } if ( !menubar && !has_full_screen) { // Find the menubar of the desktop if ( !desktops.isEmpty() ) { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->isTopMenu() && (*it)->mainClient()->isDesktop() ) { menubar = *it; break; } } } #if 0 // I don't like this - why to show a menubar belonging to another application? // either show the app's menubar, or the desktop's one, otherwise none at all else { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->isTopMenu() && (*it)->mainClient() == (*it) && (*it)->isOnDesktop( currentDesktop())) { menubar = *it; break; } } } #endif } if ( menubar ) { menubar->show(); menubar->raise(); // better for FocusFollowsMouse than raiseClient(menubar) raiseElectricBorders(); } // ... then hide the other ones. Avoids flickers. // Leave the desktop menubars always visible. Helps visually when the app doesn't show // its menubar immediately. for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->isTopMenu() && (*it) != menubar && !(*it)->mainClient()->isDesktop() ) (*it)->hide(); } rootInfo->setActiveWindow( active_client? active_client->window() : 0 ); updateColormap(); } /*! Tries to activate the client \a c. This function performs what you expect when clicking the respective entry in a taskbar: showing and raising the client (this may imply switching to the another virtual desktop) and putting the focus onto it. Once X really gave focus to the client window as requested, the client itself will call setActiveClient() and the operation is complete. This may not happen with certain focus policies, though. \sa stActiveClient(), requestFocus() */ void Workspace::activateClient( Client* c, bool force ) { raiseClient( c ); if ( c->isIconified() ) Events::raise( Events::DeIconify ); c->show(); iconifyOrDeiconifyTransientsOf( c ); if ( options->focusPolicyIsReasonable() ) { requestFocus( c, force ); } if (!c->isOnDesktop(currentDesktop()) ) { setCurrentDesktop( c->desktop() ); // popupinfo->showInfo( desktopName(currentDesktop()) ); // AK - not sure } c->updateUserTime(); } void Workspace::iconifyOrDeiconifyTransientsOf( Client* c ) { if ( c->isIconified() || c->isShade() ) { bool exclude_topmenu = !c->isIconified(); for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->transientFor() == c->window() && !(*it)->isIconified() && !(*it)->isShade() && ( !exclude_topmenu || !(*it)->isTopMenu() ) ) { (*it)->setMappingState( XIconicState ); (*it)->hide(); iconifyOrDeiconifyTransientsOf( (*it) ); } } } else { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->transientFor() == c->window() && !(*it)->isVisible() ) { (*it)->show(); iconifyOrDeiconifyTransientsOf( (*it) ); } } } } /*! Sets the client \a c's transient windows' sticky property to \a sticky. */ void Workspace::setStickyTransientsOf( Client* c, bool sticky ) { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->transientFor() == c->window() && (*it)->isSticky() != sticky ) (*it)->setSticky( sticky ); } } /*! Returns whether a client with the specified \a caption exists, or not. */ bool Workspace::hasCaption( const QString& caption ) { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->caption() == caption ) return TRUE; } return FALSE; } /*! Tries to activate the client by asking X for the input focus. This function does not perform any show, raise or desktop switching. See Workspace::activateClient() instead. \sa Workspace::activateClient() */ void Workspace::requestFocus( Client* c, bool force ) { if (!focusChangeEnabled() && ( c != active_client) ) return; //TODO will be different for non-root clients. (subclassing?) if ( !c ) { focusToNull(); return; } - if ( !popup || !popup->isVisible() ) - popup_client = c; - if ( c->isVisible() && !c->isShade() ) { c->takeFocus( force ); should_get_focus = c; focus_chain.remove( c ); if ( c->wantsTabFocus() ) focus_chain.append( c ); } else if ( c->isShade() ) { // client cannot accept focus, but at least the window should be active (window menu, et. al. ) focusToNull(); if ( c->wantsInput() ) c->setActive( 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; } } /*! Informs the workspace that the client \a c has been hidden. If it was the active client, the workspace activates another one. \a c may already be destroyed */ void Workspace::clientHidden( Client* c ) { if ( c != active_client && ( active_client || c != should_get_focus ) ) return; + if( popup ) + popup->close(); active_client = 0; should_get_focus = 0; c->setActive( FALSE ); // clear the state in the client if (!block_focus ) { if ( c->wantsTabFocus() && focus_chain.contains( c ) ) { focus_chain.remove( c ); focus_chain.prepend( c ); } if ( options->focusPolicyIsReasonable() && !focus_chain.isEmpty() ) { for (ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) { if ((*it)->isVisible()) { requestFocus(*it); return; } } } if ( !c->isDesktop() && !desktops.isEmpty() ) requestFocus( desktops.last() ); else focusToNull(); } else { // if blocking focus, move focus to the desktop later if needed // in order to avoid flickering focusToNull(); } } -QPopupMenu* Workspace::clientPopup( Client* c ) +// KDE4 - remove the unused argument +QPopupMenu* Workspace::clientPopup( Client* ) { - popup_client = c; if ( !popup ) { popup = new QPopupMenu; popup->setCheckable( TRUE ); popup->setFont(KGlobalSettings::menuFont()); connect( popup, SIGNAL( aboutToShow() ), this, SLOT( clientPopupAboutToShow() ) ); connect( popup, SIGNAL( activated(int) ), this, SLOT( clientPopupActivated(int) ) ); // PluginMenu *deco = new PluginMenu(mgr, popup); // deco->setFont(KGlobalSettings::menuFont()); popup->insertItem( SmallIconSet( "move" ), i18n("&Move")+'\t'+keys->shortcut("Window Move").toString(), Options::MoveOp ); popup->insertItem( i18n("&Size")+'\t'+keys->shortcut("Window Resize").toString(), Options::ResizeOp ); popup->insertItem( i18n("Mi&nimize")+'\t'+keys->shortcut("Window Minimize").toString(), Options::IconifyOp ); popup->insertItem( i18n("Ma&ximize")+'\t'+keys->shortcut("Window Maximize").toString(), Options::MaximizeOp ); popup->insertItem( i18n("Sh&ade")+'\t'+keys->shortcut("Window Shade").toString(), Options::ShadeOp ); popup->insertItem( SmallIconSet( "attach" ), i18n("Always &on Top"), Options::StaysOnTopOp ); popup->insertItem( SmallIconSet( "filesave" ), i18n("Sto&re Settings"), Options::ToggleStoreSettingsOp ); popup->insertSeparator(); popup->insertItem(SmallIconSet( "configure" ), i18n("Configur&e..."), this, SLOT( configureWM() )); popup->insertSeparator(); popup->insertItem( SmallIconSet( "fileclose" ), i18n("&Close")+'\t'+keys->shortcut("Window Close").toString(), Options::CloseOp ); } return popup; } void Workspace::initDesktopPopup() { if (desk_popup) return; desk_popup = new QPopupMenu( popup ); desk_popup->setCheckable( TRUE ); desk_popup->setFont(KGlobalSettings::menuFont()); connect( desk_popup, SIGNAL( activated(int) ), this, SLOT( sendToDesktop(int) ) ); connect( desk_popup, SIGNAL( aboutToShow() ), this, SLOT( desktopPopupAboutToShow() ) ); popup->insertItem(i18n("To &Desktop"), desk_popup, -1, 8 ); } -void Workspace::showWindowMenuAt( unsigned long id, int x, int y ) +// KDE4 remove me +void Workspace::showWindowMenuAt( unsigned long, int, int ) { - Client *target = findClient( id ); - - if (!target) - return; - - Client* c = active_client; - QPopupMenu* p = clientPopup( target ); - p->exec( QPoint( x, y ) ); - if ( hasClient( c ) ) - requestFocus( c ); + slotWindowOperations(); } void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) { if ( !c ) return; if (op == Options::MoveOp) QCursor::setPos( c->geometry().center() ); if (op == Options::ResizeOp) QCursor::setPos( c->geometry().bottomRight()); switch ( op ) { case Options::MoveOp: c->performMouseCommand( Options::MouseMove, QCursor::pos() ); break; case Options::ResizeOp: c->performMouseCommand( Options::MouseResize, QCursor::pos() ); break; case Options::CloseOp: c->closeWindow(); break; case Options::MaximizeOp: c->maximize(); break; case Options::IconifyOp: c->iconify(); break; case Options::ShadeOp: c->setShade( !c->isShade() ); break; case Options::StickyOp: c->setSticky( !c->isSticky() ); break; case Options::StaysOnTopOp: c->setStaysOnTop( !c->staysOnTop() ); raiseClient( c ); break; case Options::ToggleStoreSettingsOp: c->setStoreSettings( !c->storeSettings() ); break; case Options::HMaximizeOp: c->maximize(Client::MaximizeHorizontal); break; case Options::VMaximizeOp: c->maximize(Client::MaximizeVertical); break; case Options::LowerOp: lowerClient(c); break; default: break; } } void Workspace::clientPopupActivated( int id ) { - performWindowOperation( popup_client, (Options::WindowOperation) id ); + performWindowOperation( active_client, (Options::WindowOperation) id ); } /*! Asks the internal positioning object to place a client */ void Workspace::place(Client* c) { d->initPositioning->place(c); } /*! Marks the client as being moved around by the user. */ void Workspace::setClientIsMoving( Client *c ) { Q_ASSERT(!c || !d->movingClient); // Catch attempts to move a second // window while still moving the first one. d->movingClient = c; if (d->movingClient) focus_change = false; else focus_change = true; } /*! Cascades all clients on the current desktop */ void Workspace::cascadeDesktop() { ClientList::Iterator it(stacking_order.begin()); bool re_init_cascade_at_first_client = true; for (; it != stacking_order.end(); ++it) { if((!(*it)->isOnDesktop(currentDesktop())) || ((*it)->isIconified()) || ((*it)->isSticky()) || (!(*it)->isMovable()) ) continue; d->initPositioning->placeCascaded(*it, re_init_cascade_at_first_client); //CT is an if faster than an attribution?? if (re_init_cascade_at_first_client) re_init_cascade_at_first_client = false; } } /*! Unclutters the current desktop by smart-placing all clients again. */ void Workspace::unclutterDesktop() { ClientList::Iterator it(clients.fromLast()); for (; it != clients.end(); --it) { if((!(*it)->isOnDesktop(currentDesktop())) || ((*it)->isIconified()) || ((*it)->isSticky()) || (!(*it)->isMovable()) ) continue; d->initPositioning->placeSmart(*it); } } void Workspace::reconfigure() { reconfigureTimer.start(200, true); } /*! Reread settings */ void Workspace::slotReconfigure() { kdDebug(1212) << "Workspace::slotReconfigure()" << endl; reconfigureTimer.stop(); KGlobal::config()->reparseConfiguration(); options->reload(); tab_box->reconfigure(); popupinfo->reconfigure(); readShortcuts(); mgr->updatePlugin(); if (options->electricBorders() == Options::ElectricAlways) createBorderWindows(); else destroyBorderWindows(); } /*! avoids managing a window with title \a title */ void Workspace::doNotManage( QString title ) { doNotManageList.append( title ); } /*! Hack for java applets */ bool Workspace::isNotManaged( const QString& title ) { for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) { QRegExp r( (*it) ); if (r.search(title) != -1) { doNotManageList.remove( it ); return TRUE; } } return FALSE; } /*! Lowers the client \a c taking stays-on-top flags, layers, transient windows and window groups into account. */ void Workspace::lowerClient( Client* c ) { if ( !c ) return; if ( c->isDesktop() ) return; // deny ClientList saveset; if ( c->transientFor() ) { /* search for a non-transient managed window, which this client is transient to (possibly over many ways) */ saveset.append( c ); Client* t = findClient( c->transientFor() ); Client* tmp; while ( t && !saveset.contains( t ) && t->transientFor() ) { tmp = findClient( t->transientFor() ); if ( !tmp ) break; saveset.append( t ); t = tmp; } if ( t && !saveset.contains( t ) ) { lowerClient( t ); return; } } saveset.clear(); saveset.append( c ); lowerTransientsOf(saveset, c ); stacking_order.remove(c); stacking_order.prepend(c); stacking_order = constrainedStackingOrder( stacking_order ); Window* new_stack = new Window[ stacking_order.count() + 1 ]; int i = 0; for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) new_stack[i++] = (*it)->winId(); XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; propagateClients( TRUE ); if ( c == most_recently_raised ) most_recently_raised = 0; } /*! Raises the client \a c taking layers, stays-on-top flags, transient windows and window groups into account. */ void Workspace::raiseClient( Client* c ) { if ( !c ) return; ClientList saveset; if ( c->isDesktop() ) { saveset.clear(); saveset.append( c ); raiseTransientsOf(saveset, c ); return; // deny } most_recently_raised = c; stacking_order.remove( c ); stacking_order.append( c ); if ( c->transientFor() ) { saveset.append( c ); Client* t = findClient( c->transientFor() ); Client* tmp; while ( t && !saveset.contains( t ) && t->transientFor() ) { tmp = findClient( t->transientFor() ); if ( !tmp ) break; saveset.append( t ); t = tmp; } if ( t && !saveset.contains( t ) ) { raiseClient( t ); most_recently_raised = c; return; } } saveset.clear(); saveset.append( c ); raiseTransientsOf(saveset, c ); stacking_order = constrainedStackingOrder( stacking_order ); /* workaround to help broken full-screen applications to keep (modal) dialogs visible */ if ( c->isTransient() && c->mainClient() == c ) { bool has_full_screen = false; for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { if ( (*it) == c ) break; if ( (*it)->isVisible() && (*it)->isFullScreen() && !(*it)->isDesktop() && (*it)->staysOnTop() ) { has_full_screen = true; break; } } if ( has_full_screen ) { stacking_order.remove( c ); stacking_order.append( c ); saveset.clear(); saveset.append( c ); raiseTransientsOf( saveset, c); } } /* end workaround */ Window* new_stack = new Window[ stacking_order.count() + 1 ]; int i = 0; for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) new_stack[i++] = (*it)->winId(); XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; propagateClients( TRUE ); if ( tab_box->isVisible() ) tab_box->raise(); if ( popupinfo->isVisible() ) popupinfo->raise(); raiseElectricBorders(); } void Workspace::stackClientUnderActive( Client* c ) { if ( !active_client || !c || active_client == c ) return; ClientList::Iterator it = stacking_order.find( active_client ); if ( it == stacking_order.end() ) return; stacking_order.remove( c ); stacking_order.insert( it, c ); stacking_order = constrainedStackingOrder( stacking_order ); Window* new_stack = new Window[ stacking_order.count() + 1 ]; int i = 0; for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { new_stack[i++] = (*it)->winId(); } XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; propagateClients( TRUE ); } void Workspace::raiseOrLowerClient( Client *c) { if (!c) return; if (c == most_recently_raised) { lowerClient(c); } else { raiseClient(c); } } /*! Private auxiliary function used in raiseClient() */ void Workspace::raiseTransientsOf( ClientList& safeset, Client* c ) { ClientList local = stacking_order; for ( ClientList::ConstIterator it = local.begin(); it != local.end(); ++it) { if ( (*it)->transientFor() == c->window() && !safeset.contains( *it ) ) { safeset.append( *it ); stacking_order.remove( *it ); stacking_order.append( *it ); raiseTransientsOf( safeset, *it ); } } } /*! Private auxiliary function used in lowerClient() */ void Workspace::lowerTransientsOf( ClientList& safeset, Client* c ) { ClientList local = stacking_order; for ( ClientList::ConstIterator it = local.fromLast(); it!=local.end(); --it) { if ((*it)->transientFor() == c->window() && !safeset.contains(*it)) { safeset.append( *it ); lowerTransientsOf( safeset, *it ); stacking_order.remove( *it ); stacking_order.prepend( *it ); } } } /*! Returns a stacking order based upon \a list that fulfills certain contained. Currently it obeyes the staysOnTop flag only. \sa Client::staysOnTop() */ ClientList Workspace::constrainedStackingOrder( const ClientList& list ) { ClientList result; ClientList::ConstIterator it; for ( it = list.begin(); it!=list.end(); ++it) { if ( !(*it)->staysOnTop() && !(*it)->mainClient()->staysOnTop()) result.append( *it ); } for ( it = list.begin(); it!=list.end(); ++it) { if ( (*it)->staysOnTop() || (*it)->mainClient()->staysOnTop() ) result.append( *it ); } return result; } /*! Puts the focus on a dummy window Just using XSetInputFocus() with None would block keyboard input */ void Workspace::focusToNull(){ int mask; XSetWindowAttributes attr; if (null_focus_window == 0) { mask = CWOverrideRedirect; attr.override_redirect = 1; null_focus_window = XCreateWindow(qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, mask, &attr); XMapWindow(qt_xdisplay(), null_focus_window); } XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time ); if( !block_focus ) setActiveClient( NULL ); } /*! Refreshes all the client windows */ void Workspace::refresh() { QWidget w( 0, 0, WX11BypassWM ); w.setGeometry( QApplication::desktop()->geometry() ); w.show(); w.hide(); QApplication::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: QValueList obscuring_windows; static QValueList* cached; static unsigned int max_cache_size; }; QValueList* ObscuringWindows::cached = NULL; unsigned int ObscuringWindows::max_cache_size = 0; void ObscuringWindows::create( Client* c ) { if( cached == NULL ) cached = new QValueList; 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->winId(); 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 = QMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1; for( QValueList::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 */ void Workspace::setCurrentDesktop( int new_desktop ){ if (new_desktop < 1 || new_desktop > number_of_desktops ) return; Client* old_active_client = active_client; active_client = 0; + if( popup ) + popup->close(); block_focus = TRUE; // ClientList mapList; // ClientList unmapList; if (new_desktop != current_desktop) { /* optimized Desktop switching: unmapping done from back to front mapping done from front to back => less exposure events */ Events::raise((Events::Event) (Events::DesktopChange+new_desktop)); ObscuringWindows obs_wins; if (d->movingClient && !d->movingClient->isSticky()) { d->movingClient->setDesktop(-1); // All desktops } for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { if ( (*it)->isVisible() && !(*it)->isOnDesktop( new_desktop ) ) { obs_wins.create( *it ); (*it)->hide(); // unmapList += (*it); } } current_desktop = new_desktop; rootInfo->setCurrentDesktop( current_desktop ); // propagate befor the shows below for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { if ( (*it)->isOnDesktop( new_desktop ) && !(*it)->isIconified() ) { (*it)->show(); // mapList += (*it); } } if (d->movingClient && !d->movingClient->isSticky()) { d->movingClient->setDesktop(new_desktop); } } current_desktop = new_desktop; rootInfo->setCurrentDesktop( current_desktop ); // restore the focus on this desktop block_focus = FALSE; Client* c = 0; if ( options->focusPolicyIsReasonable()) { // Search in focus chain if ( focus_chain.contains( old_active_client ) && old_active_client->isVisible() ) { c = old_active_client; active_client = c; // the requestFocus below will fail, as the client is already active } if ( !c ) { for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) { if ( (*it)->isVisible() && !(*it)->isSticky() ) { c = *it; break; } } } if ( !c ) { for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) { if ( (*it)->isVisible() ) { c = *it; break; } } } /* if (!c) { // this is useless - these windows don't accept focus // Search top-most visible window for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { if ( (*it)->isVisible() ) { c = *it; break; } } }*/ } //if "unreasonable focus policy" // and active_client is sticky and under mouse (hence == old_active_client), // conserve focus (thanks to Volker Schatz ) else if( old_active_client && old_active_client->isVisible() ) c= old_active_client; if ( c ) { requestFocus( c ); // don't let the panel cover fullscreen windows on desktop switches if ( c->isFullScreen() && !c->isDesktop() && c->staysOnTop() ) raiseClient( c ); } else { focusToNull(); } if( !desktops.isEmpty() ) { Window w_tmp; int i_tmp; XGetInputFocus( qt_xdisplay(), &w_tmp, &i_tmp ); if( w_tmp == null_focus_window ) requestFocus( desktops.last() ); } // Update focus chain: // If input: chain = { 1, 2, 3, 4 } and current_desktop = 3, // Output: chain = { 3, 1, 2, 4 }. // kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n") // .arg(current_desktop).arg(desktop_focus_chain.find( current_desktop )); for( int i = desktop_focus_chain.find( current_desktop ); i > 0; i-- ) desktop_focus_chain[i] = desktop_focus_chain[i-1]; desktop_focus_chain[0] = current_desktop; // QString s = "desktop_focus_chain[] = { "; // for( uint i = 0; i < desktop_focus_chain.size(); i++ ) // s += QString::number(desktop_focus_chain[i]) + ", "; // kdDebug(1212) << s << "}\n"; } void Workspace::nextDesktop() { int desktop = currentDesktop() + 1; setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::previousDesktop() { int desktop = currentDesktop() - 1; setCurrentDesktop(desktop ? desktop : numberOfDesktops()); popupinfo->showInfo( desktopName(currentDesktop()) ); } /*! 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. */ QWidget* Workspace::desktopWidget() { return desktop_widget; } /*! 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; rootInfo->setNumberOfDesktops( number_of_desktops ); saveDesktopSettings(); // 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)->isSticky() && (*it)->desktop() > numberOfDesktops()) sendClientToDesktop( *it, numberOfDesktops()); } } if( currentDesktop() > numberOfDesktops()) setCurrentDesktop( numberOfDesktops()); // 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; } /*! Handles client messages sent to the workspace */ bool Workspace::netCheck( XEvent* e ) { unsigned int dirty = rootInfo->event( e ); if ( dirty & NET::DesktopNames ) saveDesktopSettings(); return dirty != 0; } /*! Called when a newly mapped client is ready ( has properties set correctly ) */ void Workspace::clientReady( Client* ) { propagateClients(); } /*! Propagates the managed clients to the world */ void Workspace::propagateClients( bool onlyStacking ) { Window *cl; // MW we should not assume WId and Window to be compatible // when passig pointers around. int i; if ( !onlyStacking ) { cl = new Window[ clients.count()]; i = 0; for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it ) cl[i++] = (*it)->window(); rootInfo->setClientList( cl, i ); delete [] cl; } cl = new Window[ stacking_order.count()]; i = 0; for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) cl[i++] = (*it)->window(); rootInfo->setClientListStacking( cl, i ); delete [] cl; } /*! 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 ) { if ( !systemTrayWins.contains( w ) ) return FALSE; systemTrayWins.remove( 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; } /*! Create the global accel object \c keys. */ void Workspace::initShortcuts(){ keys = new KGlobalAccel( this ); #include "kwinbindings.cpp" readShortcuts(); } void Workspace::readShortcuts(){ keys->readSettings(); cutWalkThroughDesktops = keys->shortcut("Walk Through Desktops"); cutWalkThroughDesktopsReverse = keys->shortcut("Walk Through Desktops (Reverse)"); cutWalkThroughDesktopList = keys->shortcut("Walk Through Desktop List"); cutWalkThroughDesktopListReverse = keys->shortcut("Walk Through Desktop List (Reverse)"); cutWalkThroughWindows = keys->shortcut("Walk Through Windows"); cutWalkThroughWindowsReverse = keys->shortcut("Walk Through Windows (Reverse)"); keys->updateConnections(); } void Workspace::slotSwitchDesktopNext(){ int d = currentDesktop() + 1; if ( d > numberOfDesktops() ) { if ( options->rollOverDesktops ) { d = 1; } else { return; } } setCurrentDesktop(d); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopPrevious(){ int d = currentDesktop() - 1; if ( d <= 0 ) { if ( options->rollOverDesktops ) d = numberOfDesktops(); else return; } setCurrentDesktop(d); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::setDesktopLayout(int o, int x, int y) { d->layoutOrientation = (Qt::Orientation) o; d->layoutX = x; d->layoutY = y; } void Workspace::calcDesktopLayout(int &x, int &y) { x = d->layoutX; y = d->layoutY; if ((x == -1) && (y > 0)) x = (numberOfDesktops()+y-1) / y; else if ((y == -1) && (x > 0)) y = (numberOfDesktops()+x-1) / x; if (x == -1) x = 1; if (y == -1) y = 1; } void Workspace::slotSwitchDesktopRight() { int x,y; calcDesktopLayout(x,y); int dt = currentDesktop()-1; if (d->layoutOrientation == Qt::Vertical) { dt += y; if ( dt >= numberOfDesktops() ) { if ( options->rollOverDesktops ) dt -= numberOfDesktops(); else return; } } else { int d = (dt % x) + 1; if ( d >= x ) { if ( options->rollOverDesktops ) d -= x; else return; } dt = dt - (dt % x) + d; } setCurrentDesktop(dt+1); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopLeft(){ int x,y; calcDesktopLayout(x,y); int dt = currentDesktop()-1; if (d->layoutOrientation == Qt::Vertical) { dt -= y; if ( dt < 0 ) { if ( options->rollOverDesktops ) dt += numberOfDesktops(); else return; } } else { int d = (dt % x) - 1; if ( d < 0 ) { if ( options->rollOverDesktops ) d += x; else return; } dt = dt - (dt % x) + d; } setCurrentDesktop(dt+1); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopUp(){ int x,y; calcDesktopLayout(x,y); int dt = currentDesktop()-1; if (d->layoutOrientation == Qt::Horizontal) { dt -= x; if ( dt < 0 ) { if ( options->rollOverDesktops ) dt += numberOfDesktops(); else return; } } else { int d = (dt % y) - 1; if ( d < 0 ) { if ( options->rollOverDesktops ) d += y; else return; } dt = dt - (dt % y) + d; } setCurrentDesktop(dt+1); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopDown(){ int x,y; calcDesktopLayout(x,y); int dt = currentDesktop()-1; if (d->layoutOrientation == Qt::Horizontal) { dt += x; if ( dt >= numberOfDesktops() ) { if ( options->rollOverDesktops ) dt -= numberOfDesktops(); else return; } } else { int d = (dt % y) + 1; if ( d >= y ) { if ( options->rollOverDesktops ) d -= y; else return; } dt = dt - (dt % y) + d; } setCurrentDesktop(dt+1); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchToDesktop( int i ) { setCurrentDesktop( i ); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotWindowToDesktop( int i ) { - if( i >= 1 && i <= numberOfDesktops() && popup_client - && ( popup_client->windowType() == NET::Normal - || popup_client->windowType() == NET::Dialog )) - sendClientToDesktop( popup_client, i ); + if( i >= 1 && i <= numberOfDesktops() && active_client + && !active_client->isDesktop() + && !active_client->isDock() + && !active_client->isTopMenu()) + sendClientToDesktop( active_client, i ); } /*! Maximizes the popup client */ void Workspace::slotWindowMaximize() { - if ( popup_client ) - popup_client->maximize( Client::MaximizeFull ); + if ( active_client ) + active_client->maximize( Client::MaximizeFull ); } /*! Maximizes the popup client vertically */ void Workspace::slotWindowMaximizeVertical() { - if ( popup_client ) - popup_client->maximize( Client::MaximizeVertical ); + if ( active_client ) + active_client->maximize( Client::MaximizeVertical ); } /*! Maximizes the popup client horiozontally */ void Workspace::slotWindowMaximizeHorizontal() { - if ( popup_client ) - popup_client->maximize( Client::MaximizeHorizontal ); + if ( active_client ) + active_client->maximize( Client::MaximizeHorizontal ); } /*! Iconifies the popup client */ void Workspace::slotWindowIconify() { - performWindowOperation( popup_client, Options::IconifyOp ); + performWindowOperation( active_client, Options::IconifyOp ); } // This should probably be removed now that there is a "Show Desktop" binding. void Workspace::slotWindowIconifyAll() { int iDesktop = currentDesktop(); for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if( (*it)->isOnDesktop( iDesktop ) && !(*it)->isIconified() ) performWindowOperation( *it, Options::IconifyOp ); } } /*! Shades/unshades the popup client respectively */ void Workspace::slotWindowShade() { - performWindowOperation( popup_client, Options::ShadeOp ); + performWindowOperation( active_client, Options::ShadeOp ); } /*! Raises the popup client */ void Workspace::slotWindowRaise() { - if ( popup_client ) - raiseClient( popup_client ); + if ( active_client ) + raiseClient( active_client ); } /*! Lowers the popup client */ void Workspace::slotWindowLower() { - if ( popup_client ) - lowerClient( popup_client ); + if ( active_client ) + lowerClient( active_client ); } /*! Does a toggle-raise-and-lower on the popup client; */ void Workspace::slotWindowRaiseOrLower() { - if ( popup_client ) - raiseOrLowerClient( popup_client ); + if ( active_client ) + raiseOrLowerClient( active_client ); } /*! Move window to next desktop */ void Workspace::slotWindowToNextDesktop(){ int d = currentDesktop() + 1; if ( d > numberOfDesktops() ) d = 1; - if (popup_client) - sendClientToDesktop(popup_client,d); + if (active_client && !active_client->isDesktop() + && !active_client->isDock() && !active_client->isTopMenu()) + sendClientToDesktop(active_client,d); setCurrentDesktop(d); popupinfo->showInfo( desktopName(currentDesktop()) ); } /*! Move window to previous desktop */ void Workspace::slotWindowToPreviousDesktop(){ int d = currentDesktop() - 1; if ( d <= 0 ) d = numberOfDesktops(); - if (popup_client) - sendClientToDesktop(popup_client,d); + if (active_client && !active_client->isDesktop() + && !active_client->isDock() && !active_client->isTopMenu()) + sendClientToDesktop(active_client,d); setCurrentDesktop(d); popupinfo->showInfo( desktopName(currentDesktop()) ); } /*! Invokes keyboard mouse emulation */ void Workspace::slotMouseEmulation() { if ( mouse_emulation ) { XUngrabKeyboard(qt_xdisplay(), qt_x_time); mouse_emulation = FALSE; return; } if ( XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, qt_x_time) == GrabSuccess ) { mouse_emulation = TRUE; mouse_emulation_state = 0; mouse_emulation_window = 0; } } /*! Kill Window feature, similar to xkill */ void Workspace::slotKillWindow() { KillWindow kill( this ); kill.start(); } /*! Kills the window at position \a x, \a y */ void Workspace::killWindowAtPosition(int x, int y) { ClientList::ConstIterator it(stacking_order.fromLast()); for ( ; it != stacking_order.end(); --it) { Client *client = (*it); if ( client->frameGeometry().contains(QPoint(x, y)) && client->isOnDesktop( currentDesktop() ) && !client->isTopMenu() && !client->isDesktop() && !client->isIconified() ) { client->killWindow(); return; } } } /*! Takes a screenshot of the current window and puts it in the clipboard. */ void Workspace::slotGrabWindow() { if ( active_client ) { QPixmap p = QPixmap::grabWindow( active_client->winId() ); QClipboard *cb = QApplication::clipboard(); cb->setPixmap( p ); } else slotGrabDesktop(); } /*! Takes a screenshot of the whole desktop and puts it in the clipboard. */ void Workspace::slotGrabDesktop() { QPixmap p = QPixmap::grabWindow( qt_xrootwin() ); QClipboard *cb = QApplication::clipboard(); cb->setPixmap( p ); } /*! Adjusts the desktop popup to the current values and the location of the popup client. */ void Workspace::desktopPopupAboutToShow() { if ( !desk_popup ) return; desk_popup->clear(); desk_popup->insertItem( i18n("&All Desktops"), 0 ); - if ( popup_client && popup_client->isSticky() ) + if ( active_client && active_client->isSticky() ) desk_popup->setItemChecked( 0, TRUE ); desk_popup->insertSeparator( -1 ); int id; const int BASE = 10; for ( int i = 1; i <= numberOfDesktops(); i++ ) { QString basic_name("%1 %2"); if (iinsertItem( basic_name .arg(i) .arg( desktopName(i).replace( QRegExp("&"), "&&" )), i ); - if ( popup_client && - !popup_client->isSticky() && popup_client->desktop() == i ) + if ( active_client && + !active_client->isSticky() && active_client->desktop() == i ) desk_popup->setItemChecked( id, TRUE ); } } /*! The client popup menu will become visible soon. Adjust the items according to the respective popup client. */ void Workspace::clientPopupAboutToShow() { - if ( !popup_client || !popup ) + if ( !active_client || !popup ) return; if ( numberOfDesktops() == 1 ) { delete desk_popup; desk_popup = 0; } else { initDesktopPopup(); } - popup->setItemEnabled( Options::ResizeOp, popup_client->isResizable() ); - popup->setItemEnabled( Options::MoveOp, popup_client->isMovable() ); - popup->setItemEnabled( Options::MaximizeOp, popup_client->isMaximizable() ); - popup->setItemChecked( Options::MaximizeOp, popup_client->isMaximized() ); - popup->setItemChecked( Options::ShadeOp, popup_client->isShade() ); - popup->setItemChecked( Options::StaysOnTopOp, popup_client->staysOnTop() ); - popup->setItemEnabled( Options::IconifyOp, popup_client->isMinimizable() ); - popup->setItemEnabled( Options::ToggleStoreSettingsOp, !popup_client->isTransient() ); - popup->setItemChecked( Options::ToggleStoreSettingsOp, popup_client->storeSettings() ); + popup->setItemEnabled( Options::ResizeOp, active_client->isResizable() ); + popup->setItemEnabled( Options::MoveOp, active_client->isMovable() ); + popup->setItemEnabled( Options::MaximizeOp, active_client->isMaximizable() ); + popup->setItemChecked( Options::MaximizeOp, active_client->isMaximized() ); + popup->setItemChecked( Options::ShadeOp, active_client->isShade() ); + popup->setItemChecked( Options::StaysOnTopOp, active_client->staysOnTop() ); + popup->setItemEnabled( Options::IconifyOp, active_client->isMinimizable() ); + popup->setItemEnabled( Options::ToggleStoreSettingsOp, !active_client->isTransient() ); + popup->setItemChecked( Options::ToggleStoreSettingsOp, active_client->storeSettings() ); } /*! Sends client \a c to desktop \a desk. Takes care of transients as well. */ void Workspace::sendClientToDesktop( Client* c, int desk ) { if ( c->isSticky() && desk != NETWinInfo::OnAllDesktops ) c->setSticky( FALSE ); if ( c->isOnDesktop( desk ) ) return; c->setDesktop( desk ); if( desk == NETWinInfo::OnAllDesktops ) c->setSticky( true ); else //CT 01Jul2002 - the old position is most probably inappropriate on the new desktop // thus we place the client again, before we show it, but only if it's not OnAllDesktops place(c); if ( c->isOnDesktop( currentDesktop() ) ) { c->show(); if ( c->wantsTabFocus() && options->focusPolicyIsReasonable() ) { requestFocus( c ); } } else { c->hide(); raiseClient( c ); focus_chain.remove( c ); if ( c->wantsTabFocus() ) focus_chain.append( c ); } for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->transientFor() == c->window() ) { sendClientToDesktop( *it, desk ); } } } /*! Sends the popup client to desktop \a desk Internal slot for the window operation menu */ void Workspace::sendToDesktop( int desk ) { - if ( !popup_client ) + if ( !active_client ) return; if ( desk == 0 ) { - popup_client->setSticky( !popup_client->isSticky() ); + active_client->setSticky( !active_client->isSticky() ); return; } - sendClientToDesktop( popup_client, desk ); + sendClientToDesktop( active_client, desk ); } /*! Shows the window operations popup menu for the activeClient() */ void Workspace::slotWindowOperations() { if ( !active_client ) return; - if ( active_client->isDesktop()) + if ( active_client->isDesktop() + || active_client->isDock() + || active_client->isTopMenu()) return; QPopupMenu* p = clientPopup( active_client ); - Client* c = active_client; +// Client* c = active_client; p->exec( active_client->mapToGlobal( active_client->windowWrapper()->geometry().topLeft() ) ); - if ( hasClient( c ) ) - requestFocus( c ); +// if ( hasClient( c ) ) +// requestFocus( c ); } /*! Closes the popup client */ void Workspace::slotWindowClose() { if ( tab_box->isVisible() || popupinfo->isVisible() ) return; - performWindowOperation( popup_client, Options::CloseOp ); + performWindowOperation( active_client, Options::CloseOp ); } /*! Starts keyboard move mode for the popup client */ void Workspace::slotWindowMove() { - performWindowOperation( popup_client, Options::MoveOp ); + performWindowOperation( active_client, Options::MoveOp ); } /*! Starts keyboard resize mode for the popup client */ void Workspace::slotWindowResize() { - performWindowOperation( popup_client, Options::ResizeOp ); + performWindowOperation( active_client, Options::ResizeOp ); } /*! Client \a c is moved around to position \a pos. This gives the workspace the opportunity to interveniate and to implement snap-to-windows functionality. */ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) { //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone //CT adapted for kwin on 25Nov1999 //aleXXX 02Nov2000 added second snapping mode if (options->windowSnapZone || options->borderSnapZone ) { bool sOWO=options->snapOnlyWhenOverlapping; QRect maxRect = clientArea(MovementArea, pos+c->rect().center()); int xmin = maxRect.left(); int xmax = maxRect.right()+1; //desk size int ymin = maxRect.top(); int ymax = maxRect.bottom()+1; int cx(pos.x()); int cy(pos.y()); int cw(c->width()); int ch(c->height()); int rx(cx+cw); int ry(cy+ch); //these don't change int nx(cx), ny(cy); //buffers int deltaX(xmax); int deltaY(ymax); //minimum distance to other clients int lx, ly, lrx, lry; //coords and size for the comparison client, l // border snap int snap = options->borderSnapZone; //snap trigger if (snap) { if ((sOWO?(cxxmax):true) && (QABS(rx-xmax)ymax):true) && (QABS(ry-ymax)windowSnapZone; if (snap) { QValueList::ConstIterator l; for (l = clients.begin();l != clients.end();++l ) { if ((*l)->isOnDesktop(currentDesktop()) && !(*l)->isIconified() #if 0 && (*l)->transientFor() == None #endif && (*l) != c ) { lx = (*l)->x(); ly = (*l)->y(); lrx = lx + (*l)->width(); lry = ly + (*l)->height(); if ( (( cy <= lry ) && ( cy >= ly )) || (( ry >= ly ) && ( ry <= lry )) || (( cy <= ly ) && ( ry >= lry )) ) { if ((sOWO?(cxlx):true) && (QABS(rx-lx)= lx )) || (( rx >= lx ) && ( rx <= lrx )) || (( cx <= lx ) && ( rx >= lrx )) ) { if ((sOWO?(cyly):true) && (QABS(ry-ly)isActive() ) activateClient( c ); return (WId) w; } /*! Sends a faked mouse event to the specified window. Returns the new button state. */ unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation type, int button, unsigned int state ) { if ( !w ) return state; QWidget* widget = QWidget::find( w ); if ( (!widget || widget->inherits("QToolButton") ) && !findClient( 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 XMotionEvent e; e.type = MotionNotify; e.window = w; e.root = qt_xrootwin(); e.subwindow = w; e.time = qt_x_time; e.x = x; e.y = y; e.x_root = pos.x(); e.y_root = pos.y(); e.state = state; e.is_hint = NotifyNormal; XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, (XEvent*)&e ); } else { XButtonEvent e; e.type = type == EmuRelease ? ButtonRelease : ButtonPress; e.window = w; e.root = qt_xrootwin(); e.subwindow = w; e.time = qt_x_time; e.x = x; e.y = y; e.x_root = pos.x(); e.y_root = pos.y(); e.state = state; e.button = button; XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, (XEvent*)&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; QPoint pos = QCursor::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(), qt_x_time); mouse_emulation = FALSE; return TRUE; default: return FALSE; } QCursor::setPos( pos ); if ( mouse_emulation_state ) mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0, mouse_emulation_state ); return TRUE; } void Workspace::slotResetAllClientsDelayed() { resetTimer.start(200, true); } /*! Puts a new decoration frame around every client. Used to react on style changes. */ void Workspace::slotResetAllClients() { QWidget curtain( 0, 0, WX11BypassWM ); curtain.setBackgroundMode( NoBackground ); curtain.setGeometry( QApplication::desktop()->geometry() ); curtain.show(); resetTimer.stop(); ClientList stack = stacking_order; Client* active = activeClient(); block_focus = TRUE; Client* prev = 0; for (ClientList::Iterator it = stack.fromLast(); it != stack.end(); --it) { Client *oldClient = (*it); Client::MaximizeMode oldMaxMode = oldClient->maximizeMode(); oldClient->hide(); WId w = oldClient->window(); XUnmapWindow( qt_xdisplay(), w ); oldClient->releaseWindow(); // Replace oldClient with newClient in all lists Client *newClient = clientFactory (w); if ( oldClient == active ) active = newClient; ClientList::Iterator jt = clients.find (oldClient); (*jt) = newClient; jt = stacking_order.find (oldClient); (*jt) = newClient; jt = focus_chain.find (oldClient); (*jt) = newClient; newClient->cloneMode(oldClient); delete oldClient; bool showIt = newClient->manage( TRUE, TRUE, FALSE ); Window stack[2]; stack[0] = prev ? prev->winId() : curtain.winId(); stack[1] = newClient->winId(); XRestackWindows( qt_xdisplay(), stack, 2 ); if ( showIt ) newClient->show(); if( oldMaxMode != Client::MaximizeRestore ) { newClient->maximize(Client::MaximizeRestore); // needed newClient->maximize(oldMaxMode); } prev = newClient; } block_focus = FALSE; if ( active ) requestFocus( active ); else if( !desktops.isEmpty() ) requestFocus( desktops.last() ); else focusToNull(); // Emit a DCOP signal to allow other apps to know when the kwin client // has been changed by via the titlebar decoration selection. emit dcopResetAllClients(); } void Workspace::slotSettingsChanged(int category) { kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl; if( category == (int) KApplication::SETTINGS_SHORTCUTS ) readShortcuts(); } /* * Legacy session management */ #ifndef NO_LEGACY_SESSION_MANAGEMENT #define WM_SAVE_YOURSELF_TIMEOUT 4000 typedef QMap WindowMap; #define HAS_ERROR 0 #define HAS_WMCOMMAND 1 #define HAS_WMSAVEYOURSELF 2 static WindowMap *windowMapPtr = 0; static int winsErrorHandler(Display *, XErrorEvent *ev) { if (windowMapPtr) { WindowMap::Iterator it = windowMapPtr->find(ev->resourceid); if (it != windowMapPtr->end()) it.data() = HAS_ERROR; } return 0; } /*! Stores legacy session management data */ void Workspace::storeLegacySession( KConfig* config ) { // Setup error handler WindowMap wins; windowMapPtr = &wins; XErrorHandler oldHandler = XSetErrorHandler(winsErrorHandler); // Compute set of leader windows that need legacy session management // and determine which style (WM_COMMAND or WM_SAVE_YOURSELF) for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); WId leader = c->wmClientLeader(); if (!wins.contains(leader) && c->sessionId().isEmpty()) { int wtype = HAS_WMCOMMAND; int nprotocols = 0; Atom *protocols = 0; XGetWMProtocols(qt_xdisplay(), leader, &protocols, &nprotocols); for (int i=0; iwm_save_yourself) { wtype = HAS_WMSAVEYOURSELF; break; } XFree((void*) protocols); wins.insert(leader, wtype); } } // Open fresh display for sending WM_SAVE_YOURSELF XSync(qt_xdisplay(), False); Display *newdisplay = XOpenDisplay(DisplayString(qt_xdisplay())); if (!newdisplay) return; WId root = DefaultRootWindow(newdisplay); XGrabKeyboard(newdisplay, root, False, GrabModeAsync, GrabModeAsync, CurrentTime); XGrabPointer(newdisplay, root, False, Button1Mask|Button2Mask|Button3Mask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); // Send WM_SAVE_YOURSELF messages XEvent ev; int awaiting_replies = 0; for (WindowMap::Iterator it = wins.begin(); it != wins.end(); ++it) { if ( it.data() == HAS_WMSAVEYOURSELF ) { WId w = it.key(); awaiting_replies += 1; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = atoms->wm_protocols; ev.xclient.format = 32; ev.xclient.data.l[0] = atoms->wm_save_yourself; ev.xclient.data.l[1] = qt_x_time; XSelectInput(newdisplay, w, PropertyChangeMask|StructureNotifyMask); XSendEvent(newdisplay, w, False, 0, &ev); } } // Wait for change in WM_COMMAND with timeout XFlush(newdisplay); QTime start = QTime::currentTime(); while (awaiting_replies > 0) { if (XPending(newdisplay)) { /* Process pending event */ XNextEvent(newdisplay, &ev); if ( ( ev.xany.type == UnmapNotify ) || ( ev.xany.type == PropertyNotify && ev.xproperty.atom == XA_WM_COMMAND ) ) { WindowMap::Iterator it = wins.find( ev.xany.window ); if ( it != wins.end() && it.data() != HAS_WMCOMMAND ) { awaiting_replies -= 1; if ( it.data() != HAS_ERROR ) it.data() = HAS_WMCOMMAND; } } } else { /* Check timeout */ int msecs = start.elapsed(); if (msecs >= WM_SAVE_YOURSELF_TIMEOUT) break; /* Wait for more events */ fd_set fds; FD_ZERO(&fds); int fd = ConnectionNumber(newdisplay); FD_SET(fd, &fds); struct timeval tmwait; tmwait.tv_sec = (WM_SAVE_YOURSELF_TIMEOUT - msecs) / 1000; tmwait.tv_usec = ((WM_SAVE_YOURSELF_TIMEOUT - msecs) % 1000) * 1000; ::select(fd+1, &fds, NULL, &fds, &tmwait); } } // Terminate work in new display XAllowEvents(newdisplay, ReplayPointer, CurrentTime); XAllowEvents(newdisplay, ReplayKeyboard, CurrentTime); XSync(newdisplay, False); XCloseDisplay(newdisplay); // Write LegacySession data config->setGroup("LegacySession" ); int count = 0; for (WindowMap::Iterator it = wins.begin(); it != wins.end(); ++it) { if (it.data() != HAS_ERROR) { WId w = it.key(); QCString wmCommand = Client::staticWmCommand(w); QCString wmClientMachine = Client::staticWmClientMachine(w); if ( !wmCommand.isEmpty() && !wmClientMachine.isEmpty() ) { count++; QString n = QString::number(count); config->writeEntry( QString("command")+n, wmCommand.data() ); config->writeEntry( QString("clientMachine")+n, wmClientMachine.data() ); } } } config->writeEntry( "count", count ); // Restore old error handler XSync(qt_xdisplay(), False); XSetErrorHandler(oldHandler); // Process a few events to update the client list. // All events should be there because of the XSync above. kapp->processEvents(10); } #endif /*! Restores legacy session management data (i.e. restart applications) */ void Workspace::restoreLegacySession( KConfig* config ) { if (config) { config->setGroup("LegacySession" ); int count = config->readNumEntry( "count" ); for ( int i = 1; i <= count; i++ ) { QString n = QString::number(i); QCString wmCommand = config->readEntry( QString("command")+n ).latin1(); QCString wmClientMachine = config->readEntry( QString("clientMachine")+n ).latin1(); if ( !wmCommand.isEmpty() && !wmClientMachine.isEmpty() ) { KProcess proc; proc.setUseShell(true); if ( wmClientMachine != "localhost" ) proc << "xon" << wmClientMachine; proc << QString::fromLatin1( wmCommand ); proc.start(KProcess::DontCare); } } } } /*! Stores the current session in the config file \sa loadSessionInfo() */ void Workspace::storeSession( KConfig* config ) { #ifndef NO_LEGACY_SESSION_MANAGEMENT storeLegacySession(config); #endif config->setGroup("Session" ); int count = 0; for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); QCString sessionId = c->sessionId(); QCString wmCommand = c->wmCommand(); if ( sessionId.isEmpty() ) #ifndef NO_LEGACY_SESSION_MANAGEMENT // This is the only connection between the determination of legacy // session managed applications (storeLegacySession) and the // recollection of the window geometries (this function). if ( wmCommand.isEmpty() ) #endif continue; count++; QString n = QString::number(count); config->writeEntry( QString("sessionId")+n, sessionId.data() ); config->writeEntry( QString("windowRole")+n, c->windowRole().data() ); config->writeEntry( QString("wmCommand")+n, wmCommand.data() ); config->writeEntry( QString("wmClientMachine")+n, c->wmClientMachine().data() ); config->writeEntry( QString("resourceName")+n, c->resourceName().data() ); config->writeEntry( QString("resourceClass")+n, c->resourceClass().data() ); config->writeEntry( QString("geometry")+n, QRect( c->gravitate(TRUE), c->windowWrapper()->size() ) ); config->writeEntry( QString("restore")+n, c->geometryRestore() ); config->writeEntry( QString("maximize")+n, (int) c->maximizeMode() ); config->writeEntry( QString("desktop")+n, c->desktop() ); config->writeEntry( QString("iconified")+n, c->isIconified() ); config->writeEntry( QString("sticky")+n, c->isSticky() ); config->writeEntry( QString("shaded")+n, c->isShade() ); config->writeEntry( QString("staysOnTop")+n, c->staysOnTop() ); config->writeEntry( QString("skipTaskbar")+n, c->skipTaskbar() ); config->writeEntry( QString("skipPager")+n, c->skipPager() ); } config->writeEntry( "count", count ); } /*! Loads the session information from the config file. \sa storeSession() */ void Workspace::loadSessionInfo() { session.clear(); KConfig* config = kapp->sessionConfig(); config->setGroup("Session" ); int count = config->readNumEntry( "count" ); for ( int i = 1; i <= count; i++ ) { QString n = QString::number(i); SessionInfo* info = new SessionInfo; session.append( info ); info->sessionId = config->readEntry( QString("sessionId")+n ).latin1(); info->windowRole = config->readEntry( QString("windowRole")+n ).latin1(); info->wmCommand = config->readEntry( QString("wmCommand")+n ).latin1(); info->wmClientMachine = config->readEntry( QString("wmClientMachine")+n ).latin1(); info->resourceName = config->readEntry( QString("resourceName")+n ).latin1(); info->resourceClass = config->readEntry( QString("resourceClass")+n ).latin1(); info->geometry = config->readRectEntry( QString("geometry")+n ); info->restore = config->readRectEntry( QString("restore")+n ); info->maximize = config->readNumEntry( QString("maximize")+n, 0 ); info->desktop = config->readNumEntry( QString("desktop")+n, 0 ); info->iconified = config->readBoolEntry( QString("iconified")+n, FALSE ); info->sticky = config->readBoolEntry( QString("sticky")+n, FALSE ); info->shaded = config->readBoolEntry( QString("shaded")+n, FALSE ); info->staysOnTop = config->readBoolEntry( QString("staysOnTop")+n, FALSE ); info->skipTaskbar = config->readBoolEntry( QString("skipTaskbar")+n, FALSE ); info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE ); } } void Workspace::loadFakeSessionInfo() { fakeSession.clear(); KConfig *config = KGlobal::config(); config->setGroup("FakeSession" ); int count = config->readNumEntry( "count" ); for ( int i = 1; i <= count; i++ ) { QString n = QString::number(i); SessionInfo* info = new SessionInfo; fakeSession.append( info ); info->resourceName = config->readEntry( QString("resourceName")+n ).latin1(); info->resourceClass = config->readEntry( QString("resourceClass")+n ).latin1(); info->wmClientMachine = config->readEntry( QString("clientMachine")+n ).latin1(); info->geometry = config->readRectEntry( QString("geometry")+n ); info->restore = config->readRectEntry( QString("restore")+n ); info->maximize = config->readNumEntry( QString("maximize")+n, 0 ); info->desktop = config->readNumEntry( QString("desktop")+n, 0 ); info->iconified = config->readBoolEntry( QString("iconified")+n, FALSE ); info->sticky = config->readBoolEntry( QString("sticky")+n, FALSE ); info->shaded = config->readBoolEntry( QString("shaded")+n, FALSE ); info->staysOnTop = config->readBoolEntry( QString("staysOnTop")+n, FALSE ); info->skipTaskbar = config->readBoolEntry( QString("skipTaskbar")+n, FALSE ); info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE ); } } void Workspace::storeFakeSessionInfo( Client* c ) { if ( !c->storeSettings() ) return; SessionInfo* info = new SessionInfo; fakeSession.append( info ); info->resourceName = c->resourceName(); info->resourceClass = c->resourceClass(); info->wmClientMachine = c->wmClientMachine(); info->geometry = QRect( c->gravitate(TRUE), c->windowWrapper()->size() ) ; info->restore = c->geometryRestore(); info->maximize = (int)c->maximizeMode(); info->desktop = c->desktop(); info->iconified = c->isIconified(); info->sticky = c->isSticky(); info->shaded = c->isShade(); info->staysOnTop = c->staysOnTop(); info->skipTaskbar = c->skipTaskbar(); info->skipPager = c->skipPager(); } void Workspace::writeFakeSessionInfo() { KConfig *config = KGlobal::config(); config->setGroup("FakeSession" ); int count = 0; for ( SessionInfo* info = fakeSession.first(); info; info = fakeSession.next() ) { count++; QString n = QString::number(count); config->writeEntry( QString("resourceName")+n, info->resourceName.data() ); config->writeEntry( QString("resourceClass")+n, info->resourceClass.data() ); config->writeEntry( QString("clientMachine")+n, info->wmClientMachine.data() ); config->writeEntry( QString("geometry")+n, info->geometry ); config->writeEntry( QString("restore")+n, info->restore ); config->writeEntry( QString("maximize")+n, info->maximize ); config->writeEntry( QString("desktop")+n, info->desktop ); config->writeEntry( QString("iconified")+n, info->iconified ); config->writeEntry( QString("sticky")+n, info->sticky ); config->writeEntry( QString("shaded")+n, info->shaded ); config->writeEntry( QString("staysOnTop")+n, info->staysOnTop ); config->writeEntry( QString("skipTaskbar")+n, info->skipTaskbar ); config->writeEntry( QString("skipPager")+n, info->skipPager ); } config->writeEntry( "count", count ); } /*! Returns a SessionInfo for client \a c. The returned session info is removed from the storage. It's up to the caller to delete it. This function is called when a new window is mapped and must be managed. We try to find a matching entry in the session. We also try to find a matching entry in the fakeSession to see if the user had seclected the ``store settings'' menu entry. May return 0 if there's no session info for the client. */ SessionInfo* Workspace::takeSessionInfo( Client* c ) { SessionInfo *realInfo = 0; SessionInfo *fakeInfo = 0; QCString sessionId = c->sessionId(); QCString windowRole = c->windowRole(); QCString wmCommand = c->wmCommand(); QCString wmClientMachine = c->wmClientMachine(); QCString resourceName = c->resourceName(); QCString resourceClass = c->resourceClass(); // First search ``session'' if (! sessionId.isEmpty() ) { // look for a real session managed client (algorithm suggested by ICCCM) for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() ) if ( info->sessionId == sessionId ) { if ( ! windowRole.isEmpty() ) { if ( info->windowRole == windowRole ) realInfo = session.take(); } else { if ( info->windowRole.isEmpty() && info->resourceName == resourceName && info->resourceClass == resourceClass ) realInfo = session.take(); } } } else { // look for a sessioninfo with matching features. for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() ) if ( info->resourceName == resourceName && info->resourceClass == resourceClass && info->wmClientMachine == wmClientMachine ) if ( wmCommand.isEmpty() || info->wmCommand == wmCommand ) realInfo = session.take(); } // Now search ``fakeSession'' for (SessionInfo* info = fakeSession.first(); info && !fakeInfo; info = fakeSession.next() ) if ( info->resourceName == resourceName && info->resourceClass == resourceClass && info->wmClientMachine == wmClientMachine ) fakeInfo = fakeSession.take(); // Reconciliate if (fakeInfo) c->setStoreSettings( TRUE ); if (fakeInfo && realInfo) delete fakeInfo; if (realInfo) return realInfo; if (fakeInfo) return fakeInfo; return 0; } /*! Updates the current client area according to the current clients. If the area changes, the new area is propagate to the world. The client area is the area that is available for clients (that which is not taken by windows like panels, the top-of-screen menu etc). \sa clientArea() */ void Workspace::updateClientArea() { QRect all = QApplication::desktop()->geometry(); QRect a = all; for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { a = a.intersect( (*it)->adjustedClientArea( all ) ); } if ( area != a ) { area = a; NETRect r; r.pos.x = area.x(); r.pos.y = area.y(); r.size.width = area.width(); r.size.height = area.height(); for( int i = 1; i <= numberOfDesktops(); i++) { rootInfo->setWorkArea( i, r ); } for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->isMaximized() ) (*it)->maximize( Client::MaximizeAdjust ); } } } /*! returns the area available for clients. This is the desktop geometry minus windows on the dock. Placement algorithms should refer to this rather than geometry(). \sa geometry() */ QRect Workspace::clientArea(clientAreaOption opt, const QPoint& p) { QRect rect = QApplication::desktop()->geometry(); QDesktopWidget *desktop = KApplication::desktop(); switch (opt) { case MaximizeArea: if (options->xineramaMaximizeEnabled) rect = desktop->screenGeometry(desktop->screenNumber(p)); break; case PlacementArea: if (options->xineramaPlacementEnabled) rect = desktop->screenGeometry(desktop->screenNumber(p)); break; case MovementArea: if (options->xineramaMovementEnabled) rect = desktop->screenGeometry(desktop->screenNumber(p)); break; } if (area.isNull()) return rect; return area.intersect(rect); } QRect Workspace::clientArea(const QPoint& p) { int screenNum = QApplication::desktop()->screenNumber(p); QRect rect = QApplication::desktop()->screenGeometry(screenNum); if (area.isNull()) return rect; return area.intersect(rect); } void Workspace::loadDesktopSettings() { KConfig c("kdeglobals"); QCString groupname; if (kwin_screen_number == 0) groupname = "Desktops"; else groupname.sprintf("Desktops-screen-%d", kwin_screen_number); c.setGroup(groupname); int n = c.readNumEntry("Number", 4); number_of_desktops = n; rootInfo->setNumberOfDesktops( number_of_desktops ); desktop_focus_chain.resize( n ); for(int i = 1; i <= n; i++) { QString s = c.readEntry(QString("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("kdeglobals"); QCString groupname; if (kwin_screen_number == 0) groupname = "Desktops"; else groupname.sprintf("Desktops-screen-%d", kwin_screen_number); c.setGroup(groupname); c.writeEntry("Number", number_of_desktops ); for(int i = 1; i <= number_of_desktops; i++) { QString s = desktopName( i ); QString defaultvalue = i18n("Desktop %1").arg(i); if ( s.isEmpty() ) { s = defaultvalue; rootInfo->setDesktopName( i, s.utf8().data() ); } if (s != defaultvalue) { c.writeEntry( QString("Name_%1").arg(i), s ); } else { QString currentvalue = c.readEntry(QString("Name_%1").arg(i)); if (currentvalue != defaultvalue) c.writeEntry( QString("Name_%1").arg(i), "" ); } } } /*! Checks whether focus is in nirvana, and activates a client instead. */ void Workspace::focusEnsurance() { Window focus; int revert; XGetInputFocus( qt_xdisplay(), &focus, &revert ); if ( focus == None || focus == PointerRoot ) { Window root_return; Window child = root; int root_x, root_y, lx, ly; uint state; if ( ! XQueryPointer( qt_xdisplay(), root, &root_return, &child, &root_x, &root_y, &lx, &ly, &state ) ) return; // cursor is on another screen, so do not play with focus if ( !last_active_client ) last_active_client = topClientOnDesktop(); if ( last_active_client && last_active_client->isVisible() ) { qt_x_time = CurrentTime; requestFocus( last_active_client ); } } } void Workspace::configureWM() { QStringList args; args << "kwindecoration" << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced"; KApplication::kdeinitExec( "kcmshell", args ); } void Workspace::checkStartOnDesktop( WId w ) { KStartupInfoData data; if( d->startup->checkStartup( w, data ) != KStartupInfo::Match || data.desktop() == 0 ) return; NETWinInfo info( qt_xdisplay(), w, root, NET::WMDesktop ); if( info.desktop() == 0 ) info.setDesktop( data.desktop()); } // 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::createBorderWindows() { if ( d->electric_have_borders ) return; d->electric_have_borders = true; d->electric_current_border = 0; QRect r = QApplication::desktop()->geometry(); XSetWindowAttributes attributes; unsigned long valuemask; attributes.override_redirect = True; attributes.event_mask = (EnterWindowMask | LeaveWindowMask | VisibilityChangeMask); valuemask= (CWOverrideRedirect | CWEventMask | CWCursor ); attributes.cursor = XCreateFontCursor(qt_xdisplay(), XC_sb_up_arrow); d->electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 0,0, r.width(),1, 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); XMapWindow(qt_xdisplay(), d->electric_top_border); attributes.cursor = XCreateFontCursor(qt_xdisplay(), XC_sb_down_arrow); d->electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 0,r.height()-1, r.width(),1, 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); XMapWindow(qt_xdisplay(), d->electric_bottom_border); attributes.cursor = XCreateFontCursor(qt_xdisplay(), XC_sb_left_arrow); d->electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 0,0, 1,r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); XMapWindow(qt_xdisplay(), d->electric_left_border); attributes.cursor = XCreateFontCursor(qt_xdisplay(), XC_sb_right_arrow); d->electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), r.width()-1,0, 1,r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); XMapWindow(qt_xdisplay(), d->electric_right_border); } // 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( !d->electric_have_borders) return; d->electric_have_borders = false; if(d->electric_top_border) XDestroyWindow(qt_xdisplay(),d->electric_top_border); if(d->electric_bottom_border) XDestroyWindow(qt_xdisplay(),d->electric_bottom_border); if(d->electric_left_border) XDestroyWindow(qt_xdisplay(),d->electric_left_border); if(d->electric_right_border) XDestroyWindow(qt_xdisplay(),d->electric_right_border); d->electric_top_border = None; d->electric_bottom_border = None; d->electric_left_border = None; d->electric_right_border = None; } // Do we have a proper timediff function?? static int TimeDiff(unsigned long a, unsigned long b) { if (a > b) return a-b; else return b-a; } void Workspace::clientMoved(const QPoint &pos, unsigned long now) { if ((pos.x() != d->electricLeft) && (pos.x() != d->electricRight) && (pos.y() != d->electricTop) && (pos.y() != d->electricBottom)) return; if (options->electricBorders() == Options::ElectricDisabled) return; int treshold_set = options->electricBorderDelay(); // set timeout int treshold_reset = 250; // reset timeout int distance_reset = 10; // Mouse should not move more than this many pixels int border = 0; if (pos.x() == d->electricLeft) border = 1; else if (pos.x() == d->electricRight) border = 2; else if (pos.y() == d->electricTop) border = 3; else if (pos.y() == d->electricBottom) border = 4; if ((d->electric_current_border == border) && (TimeDiff(d->electric_time_last, now) < treshold_reset) && ((pos-d->electric_push_point).manhattanLength() < distance_reset)) { d->electric_time_last = now; if (TimeDiff(d->electric_time_first, now) > treshold_set) { d->electric_current_border = 0; QRect r = QApplication::desktop()->geometry(); int offset; int desk_before = currentDesktop(); switch(border) { case 1: slotSwitchDesktopLeft(); if (currentDesktop() != desk_before) { offset = r.width() / 5; QCursor::setPos(r.width() - offset, pos.y()); } break; case 2: slotSwitchDesktopRight(); if (currentDesktop() != desk_before) { offset = r.width() / 5; QCursor::setPos(offset, pos.y()); } break; case 3: slotSwitchDesktopUp(); if (currentDesktop() != desk_before) { offset = r.height() / 5; QCursor::setPos(pos.x(), r.height() - offset); } break; case 4: slotSwitchDesktopDown(); if (currentDesktop() != desk_before) { offset = r.height() / 5; QCursor::setPos(pos.x(), offset); } break; } return; } } else { d->electric_current_border = border; d->electric_time_first = now; d->electric_time_last = now; d->electric_push_point = pos; } int mouse_warp = 1; // reset the pointer to find out wether the user is really pushing switch( border) { case 1: QCursor::setPos(pos.x()+mouse_warp, pos.y()); break; case 2: QCursor::setPos(pos.x()-mouse_warp, pos.y()); break; case 3: QCursor::setPos(pos.x(), pos.y()+mouse_warp); break; case 4: QCursor::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 void Workspace::electricBorder(XEvent *e) { Time now = e->xcrossing.time; QPoint p(e->xcrossing.x_root, e->xcrossing.y_root); clientMoved(p, now); } // 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(d->electric_have_borders){ XRaiseWindow(qt_xdisplay(), d->electric_top_border); XRaiseWindow(qt_xdisplay(), d->electric_left_border); XRaiseWindow(qt_xdisplay(), d->electric_bottom_border); XRaiseWindow(qt_xdisplay(), d->electric_right_border); } } #include "workspace.moc" diff --git a/kwin/workspace.h b/kwin/workspace.h index 4b89898ada..15ecea7795 100644 --- a/kwin/workspace.h +++ b/kwin/workspace.h @@ -1,463 +1,473 @@ /***************************************************************** kwin - the KDE window manager Copyright (C) 1999, 2000 Matthias Ettrich ******************************************************************/ #ifndef KWIN_WORKSPACE_H #define KWIN_WORKSPACE_H #include #include #include #include #include #include #include #include "options.h" #include "KWinInterface.h" #include #include #include class KConfig; class KGlobalAccel; namespace KWinInternal { class Client; class TabBox; class PopupInfo; class RootInfo; class PluginMgr; typedef QValueList ClientList; class SystemTrayWindow { public: SystemTrayWindow() : win(0),winFor(0) {} SystemTrayWindow( WId w ) : win(w),winFor(0) {} SystemTrayWindow( WId w, WId wf ) : win(w),winFor(wf) {} bool operator==( const SystemTrayWindow& other ) { return win == other.win; } WId win; WId winFor; }; typedef QValueList SystemTrayWindowList; struct SessionInfoPrivate; struct SessionInfo { QCString sessionId; QCString windowRole; QCString wmCommand; QCString wmClientMachine; QCString resourceName; QCString resourceClass; QRect geometry; QRect restore; int maximize; int desktop; bool iconified; bool sticky; bool shaded; bool staysOnTop; bool skipTaskbar; bool skipPager; private: SessionInfoPrivate* d; }; class Shape { public: static bool hasShape( WId w); static int shapeEvent(); }; class Motif { public: static bool noBorder( WId w ); }; class WorkspacePrivate; // NOTE: this class has to keep binary compatibility, just like other // KWin classes accessible from the plugins class Workspace : public QObject, virtual public KWinInterface { Q_OBJECT public: Workspace( bool restore = FALSE ); virtual ~Workspace(); static Workspace * self() { return _self; } virtual bool workspaceEvent( XEvent * ); bool hasClient(Client *); Client* findClient( WId w ) const; QRect geometry() const; enum clientAreaOption { PlacementArea, MovementArea, MaximizeArea }; // default is MaximizeArea QRect clientArea(clientAreaOption, const QPoint& p); QRect clientArea(const QPoint& p); inline QRect clientArea(clientAreaOption opt) { return clientArea(opt, QCursor::pos()); } void removeClient( Client* ); bool destroyClient( Client* ); void killWindowAtPosition(int x, int y); void killWindow() { slotKillWindow(); } WId rootWin() const; /** * Returns the active client, i.e. the client that has the focus (or None * if no client has the focus) */ Client* activeClient() const; void setActiveClient( Client* ); void activateClient( Client*, bool force = FALSE ); void requestFocus( Client* c, bool force = FALSE ); void updateColormap(); void setFocusChangeEnabled(bool b) { focus_change = b; } // KDE 3.0: No longer used bool focusChangeEnabled() { return focus_change; } // KDE 3.0: No longer used /** * Indicates that the client c is being moved around by the user. */ void setClientIsMoving( Client *c ); void place(Client *c); QPoint adjustClientPosition( Client* c, QPoint pos ); void raiseClient( Client* c ); void lowerClient( Client* c ); void stackClientUnderActive( Client* ); void raiseOrLowerClient( Client * ); void reconfigure(); void clientHidden( Client* ); void clientReady( Client* ); void clientMoved(const QPoint &pos, unsigned long time); /** * Returns the current virtual desktop of this workspace */ int currentDesktop() const; int nextDesktop( int iDesktop ) const; int previousDesktop( int iDesktop ) const; /** * Returns the number of virtual desktops of this workspace */ int numberOfDesktops() const; void setNumberOfDesktops( int n ); QWidget* desktopWidget(); Client* nextClient(Client*) const; Client* previousClient(Client*) const; Client* nextStaticClient(Client*) const; Client* previousStaticClient(Client*) const; /** * Returns the list of clients sorted in stacking order, with topmost client * at the last position */ const ClientList& stackingOrder() const; Client* topClientOnDesktop() const; void sendClientToDesktop( Client* c, int desktop ); + /** + * The Client* argument is ignored, the popup is always returned + * for the active client + */ QPopupMenu* clientPopup( Client* ); + + /** + * @deprecated Use slotWindowOperations() instead. + */ + // KDE4 remove me - and it's also in the DCOP interface :( void showWindowMenuAt( unsigned long id, int x, int y ); void iconifyOrDeiconifyTransientsOf( Client* ); void setStickyTransientsOf( Client*, bool sticky ); bool hasCaption( const QString& caption ); void performWindowOperation( Client* c, Options::WindowOperation op ); void restoreLegacySession( KConfig* config ); void storeLegacySession( KConfig* config ); void storeSession( KConfig* config ); SessionInfo* takeSessionInfo( Client* ); virtual void updateClientArea(); // dcop interface void cascadeDesktop(); void unclutterDesktop(); void doNotManage(QString); void setCurrentDesktop( int new_desktop ); void nextDesktop(); void previousDesktop(); void circulateDesktopApplications(); QString desktopName( int desk ); void setDesktopLayout(int o, int x, int y); bool isNotManaged( const QString& title ); public slots: void refresh(); // keybindings void slotSwitchDesktopNext(); void slotSwitchDesktopPrevious(); void slotSwitchDesktopRight(); void slotSwitchDesktopLeft(); void slotSwitchDesktopUp(); void slotSwitchDesktopDown(); void slotSwitchToDesktop( int ); //void slotSwitchToWindow( int ); void slotWindowToDesktop( int ); //void slotWindowToListPosition( int ); void slotWindowMaximize(); void slotWindowMaximizeVertical(); void slotWindowMaximizeHorizontal(); void slotWindowIconify(); void slotWindowIconifyAll(); void slotWindowShade(); void slotWindowRaise(); void slotWindowLower(); void slotWindowRaiseOrLower(); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); void slotWalkThroughDesktopList(); void slotWalkBackThroughDesktopList(); void slotWalkThroughWindows(); void slotWalkBackThroughWindows(); void slotWindowOperations(); void slotWindowClose(); void slotWindowMove(); void slotWindowResize(); void slotWindowToNextDesktop(); void slotWindowToPreviousDesktop(); void slotMouseEmulation(); void slotResetAllClientsDelayed(); void slotResetAllClients(); void slotSettingsChanged( int category ); void slotReconfigure(); void slotKillWindow(); void slotGrabWindow(); void slotGrabDesktop(); private slots: void desktopPopupAboutToShow(); void clientPopupAboutToShow(); void sendToDesktop( int ); void clientPopupActivated( int ); void configureWM(); void focusEnsurance(); protected: bool keyPress( XKeyEvent& ev ); bool keyRelease( XKeyEvent& ev ); bool keyPressMouseEmulation( XKeyEvent& ev ); bool netCheck( XEvent* e ); void checkStartOnDesktop( WId w ); private: void init(); void initShortcuts(); void readShortcuts(); void initDesktopPopup(); bool startKDEWalkThroughWindows(); bool startWalkThroughDesktops( int mode ); // TabBox::Mode::DesktopMode | DesktopListMode bool startWalkThroughDesktops(); bool startWalkThroughDesktopList(); void KDEWalkThroughWindows( bool forward ); void CDEWalkThroughWindows( bool forward ); void walkThroughDesktops( bool forward ); void KDEOneStepThroughWindows( bool forward ); void oneStepThroughDesktops( bool forward, int mode ); // TabBox::Mode::DesktopMode | DesktopListMode void oneStepThroughDesktops( bool forward ); void oneStepThroughDesktopList( bool forward ); ClientList constrainedStackingOrder( const ClientList& list ); Client* clientFactory(WId w); void raiseTransientsOf( ClientList& safeset, Client* c ); void lowerTransientsOf( ClientList& safeset, Client* c ); void randomPlacement(Client* c); void smartPlacement(Client* c); void cascadePlacement(Client* c, bool re_init = false); void focusToNull(); Client* findClientWidthId( WId w ) const; void propagateClients( bool onlyStacking = FALSE); bool addSystemTrayWin( WId w ); bool removeSystemTrayWin( WId w ); void propagateSystemTrayWins(); SystemTrayWindow findSystemTrayWin( WId w ); // desktop names and number of desktops void loadDesktopSettings(); void saveDesktopSettings(); // mouse emulation WId getMouseEmulationWindow(); enum MouseEmulation { EmuPress, EmuRelease, EmuMove }; unsigned int sendFakedMouseEvent( QPoint pos, WId win, MouseEmulation type, int button, unsigned int state ); // returns the new state // electric borders void createBorderWindows(); void destroyBorderWindows(); void electricBorder(XEvent * e); void raiseElectricBorders(); // ------------------ void calcDesktopLayout(int &x, int &y); SystemTrayWindowList systemTrayWins; int current_desktop; int number_of_desktops; QMemArray desktop_focus_chain; - QGuardedPtr popup_client; + // KDE4 remove me - unused + QGuardedPtr popup_client__; void loadSessionInfo(); QWidget* desktop_widget; QPtrList session; QPtrList fakeSession; void loadFakeSessionInfo(); void storeFakeSessionInfo( Client* c ); void writeFakeSessionInfo(); Client* active_client; Client* last_active_client; Client* should_get_focus; Client* most_recently_raised; ClientList clients; ClientList desktops; ClientList stacking_order; ClientList focus_chain; bool control_grab; bool tab_grab; //KKeyNative walkThroughDesktopsKeycode, walkBackThroughDesktopsKeycode; //KKeyNative walkThroughDesktopListKeycode, walkBackThroughDesktopListKeycode; //KKeyNative walkThroughWindowsKeycode, walkBackThroughWindowsKeycode; KShortcut cutWalkThroughDesktops, cutWalkThroughDesktopsReverse; KShortcut cutWalkThroughDesktopList, cutWalkThroughDesktopListReverse; KShortcut cutWalkThroughWindows, cutWalkThroughWindowsReverse; bool mouse_emulation; unsigned int mouse_emulation_state; WId mouse_emulation_window; bool focus_change; TabBox* tab_box; PopupInfo* popupinfo; QPopupMenu *popup; QPopupMenu *desk_popup; KGlobalAccel *keys; WId root; PluginMgr *mgr; RootInfo *rootInfo; QWidget* supportWindow; QRect area; // swallowing QStringList doNotManageList; // colormap handling Colormap default_colormap; Colormap installed_colormap; // Timer to collect requests for 'ResetAllClients' QTimer resetTimer; // Timer to collect requests for 'reconfigure' QTimer reconfigureTimer; QTimer focusEnsuranceTimer; WorkspacePrivate* d; static Workspace *_self; void addClient( Client* c ); }; inline WId Workspace::rootWin() const { return root; } inline Client* Workspace::activeClient() const { return active_client; } inline int Workspace::currentDesktop() const { return current_desktop; } inline int Workspace::numberOfDesktops() const { return number_of_desktops; } inline const ClientList& Workspace::stackingOrder() const { return stacking_order; } }; #endif