diff --git a/atoms.cpp b/atoms.cpp index c28d6b3c8d..a4e63b6180 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -45,6 +45,9 @@ Atoms::Atoms() atoms[n] = &kde_wm_change_state; names[n++] = (char *) "_KDE_WM_CHANGE_STATE"; + atoms[n] = &kde_net_user_time; + names[n++] = (char *) "_KDE_NET_USER_TIME"; + Atom fake; atoms[n] = &fake; names[n++] = (char *) "_DT_SM_WINDOW_INFO"; diff --git a/atoms.h b/atoms.h index 081aea1518..49dc1978e7 100644 --- a/atoms.h +++ b/atoms.h @@ -23,6 +23,7 @@ public: Atom motif_wm_hints; Atom net_wm_context_help; Atom kde_wm_change_state; + Atom kde_net_user_time; }; diff --git a/client.cpp b/client.cpp index 412a1aa584..76d2a5c551 100644 --- a/client.cpp +++ b/client.cpp @@ -103,10 +103,15 @@ public: // to resolve them properly extern Atom qt_wm_state; -extern Time kwin_time; +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 resizeHorizontalDirectionFixed = FALSE; @@ -157,7 +162,7 @@ static void sendClientMessage(Window w, Atom a, long x){ ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = x; - ev.xclient.data.l[1] = kwin_time; + ev.xclient.data.l[1] = qt_x_time; mask = 0L; if (w == qt_xrootwin()) mask = SubstructureRedirectMask; /* magic! */ @@ -399,24 +404,24 @@ void WindowWrapper::releaseWindow() bool WindowWrapper::x11Event( XEvent * e) { switch ( e->type ) { - case ButtonPress: - { - uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ? - KKeyNative::modX(KKey::WIN) : - KKeyNative::modX(KKey::ALT); - bool bModKeyHeld = e->xbutton.state & keyModX; + 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 & 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 ); - } + 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) { + Options::MouseCommand com = Options::MouseNothing; + if ( bModKeyHeld ){ + switch (e->xbutton.button) { case Button1: com = options->commandAll1(); break; @@ -426,9 +431,9 @@ bool WindowWrapper::x11Event( XEvent * e) case Button3: com = options->commandAll3(); break; - } - } else { - switch (e->xbutton.button) { + } + } else { + switch (e->xbutton.button) { case Button1: com = options->commandWindow1(); break; @@ -440,22 +445,21 @@ bool WindowWrapper::x11Event( XEvent * e) break; default: com = Options::MouseActivateAndPassClick; - } - } - bool replay = ( (Client*)parentWidget() )->performMouseCommand( com, - QPoint( e->xbutton.x_root, e->xbutton.y_root) ); + } + } + 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::Override ) - replay = TRUE; + if ( ((Client*)parentWidget())->windowType() != NET::Normal && + ((Client*)parentWidget())->windowType() != NET::Dialog && + ((Client*)parentWidget())->windowType() != NET::Override ) + replay = TRUE; - XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //kwin_time); - return TRUE; - } - break; + XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //qt_x_time); + return TRUE; + } break; case ButtonRelease: - XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //kwin_time); + XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time); break; default: break; @@ -490,7 +494,8 @@ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags NET::WMWindowType | NET::WMStrut | NET::WMName | - NET::WMIconGeometry + NET::WMIconGeometry | + NET::WMPid ; info = new WinInfo( this, qt_xdisplay(), win, qt_xrootwin(), properties ); @@ -826,10 +831,23 @@ bool Client::manage( bool isMapped, bool doNotShow, bool isInitial ) if ( isMapped ) { show(); } else { - workspace()->raiseClient( this ); // ensure constrains - show(); - if ( options->focusPolicyIsReasonable() && wantsTabFocus() ) - workspace()->requestFocus( this ); + // 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 + // 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) + Client* ac = workspace()->activeClient(); + if ( ac && ac->userTime() <= userTime() ) { + workspace()->raiseClient( this ); + show(); + if ( options->focusPolicyIsReasonable() && wantsTabFocus() ) + workspace()->requestFocus( this ); + } else { + workspace()->stackClientUnderActive( this ); + show(); + } } } @@ -840,6 +858,46 @@ bool Client::manage( bool isMapped, bool doNotShow, bool isInitial ) } + +/*! + 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. */ @@ -1611,7 +1669,7 @@ void Client::mouseMoveEvent( QMouseEvent * e) break; } } - workspace()->clientMoved(globalPos, kwin_time); + workspace()->clientMoved(globalPos, qt_x_time); // QApplication::syncX(); // process our own configure events synchronously. } @@ -2061,7 +2119,10 @@ void Client::gravitate( bool invert ) */ bool Client::x11Event( XEvent * e) { - if ( e->type == EnterNotify && ( e->xcrossing.mode == NotifyNormal || e->xcrossing.mode == NotifyUngrab ) ) { + if ( e->type == EnterNotify && + ( e->xcrossing.mode == NotifyNormal || + ( !options->focusPolicyIsReasonable() && + e->xcrossing.mode == NotifyUngrab ) ) ) { if (options->shadeHover && isShade() && !isDesktop()) { delete shadeHoverTimer; @@ -2073,8 +2134,9 @@ bool Client::x11Event( XEvent * e) if ( options->focusPolicy == Options::ClickToFocus ) return TRUE; - if ( options->autoRaise && !isDesktop() && !isDock() && !isMenu() && workspace()->focusChangeEnabled() - && workspace()->topClientOnDesktop() != this ) { + if ( options->autoRaise && !isDesktop() && + !isDock() && !isMenu() && workspace()->focusChangeEnabled() && + workspace()->topClientOnDesktop() != this ) { delete autoRaiseTimer; autoRaiseTimer = new QTimer( this ); connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) ); @@ -2420,7 +2482,7 @@ void Client::takeFocus( bool force ) // Qt may delay the mapping which may cause XSetInputFocus to fail, force show window QApplication::sendPostedEvents( windowWrapper(), QEvent::ShowWindowRequest ); - XSetInputFocus( qt_xdisplay(), win, RevertToPointerRoot, kwin_time ); + XSetInputFocus( qt_xdisplay(), win, RevertToPointerRoot, qt_x_time ); } if ( Ptakefocus ) sendClientMessage(win, atoms->wm_protocols, atoms->wm_take_focus); @@ -2650,11 +2712,6 @@ void Client::keyPressEvent( uint key_code ) QCursor::setPos( pos ); } -static int nullErrorHandler(Display *, XErrorEvent *) -{ - return 0; -} - static QCString getStringProperty(WId w, Atom prop, char separator=0) { Atom type; diff --git a/client.h b/client.h index c224d39e29..9718e2c86b 100644 --- a/client.h +++ b/client.h @@ -211,6 +211,8 @@ public: void keyPressEvent( uint key_code ); + void updateUserTime(); + public slots: void iconify(); void closeWindow(); @@ -276,6 +278,8 @@ private: void fetchName(); void gravitate( bool invert ); + unsigned long userTime(); + void startMoveResize(); void stopMoveResize(); diff --git a/main.cpp b/main.cpp index 4180a403df..66da861853 100644 --- a/main.cpp +++ b/main.cpp @@ -43,8 +43,7 @@ Options* options; Atoms* atoms; -extern Time qt_x_time; // workaround for Qt < 2.3.2 -Time kwin_time = CurrentTime; +extern Time qt_x_time; int kwin_screen_number = -1; static bool initting = FALSE; @@ -88,7 +87,7 @@ int x11ErrorHandler(Display *d, XErrorEvent *e){ } /*! - Updates kwin_time by receiving a current timestamp from the server. + Updates qt_x_time by receiving a current timestamp from the server. Use this function only when really necessary. Keep in mind that it's a roundtrip to the X-Server. @@ -103,8 +102,7 @@ void kwin_updateTime() PropModeAppend, (unsigned char*) &data, 1); XEvent ev; XWindowEvent( qt_xdisplay(), w->winId(), PropertyChangeMask, &ev ); - kwin_time = ev.xproperty.time; - qt_x_time = kwin_time; + qt_x_time = ev.xproperty.time; } @@ -143,36 +141,10 @@ Application::~Application() } + bool Application::x11EventFilter( XEvent *e ) { - switch ( e->type ) { - case ButtonPress: - case ButtonRelease: - kwin_time = e->xbutton.time; - qt_x_time = kwin_time; // workaround for Qt < 2.3.2 - break; - case MotionNotify: - kwin_time = e->xmotion.time; - qt_x_time = kwin_time; // workaround for Qt < 2.3.2 - break; - case KeyPress: - case KeyRelease: - kwin_time = e->xkey.time; - qt_x_time = kwin_time; // workaround for Qt < 2.3.2 - break; - case PropertyNotify: - kwin_time = e->xproperty.time; - qt_x_time = kwin_time; // workaround for Qt < 2.3.2 - break; - case EnterNotify: - case LeaveNotify: - kwin_time = e->xcrossing.time; - qt_x_time = kwin_time; // workaround for Qt < 2.3.2 - default: - break; - } - - if ( Workspace::self()->workspaceEvent( e ) ) + if ( Workspace::self()->workspaceEvent( e ) ) return TRUE; return KApplication::x11EventFilter( e ); } diff --git a/workspace.cpp b/workspace.cpp index 786a646009..1c31e075be 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -46,6 +46,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich #include #include #include +#include const int XIconicState = IconicState; #undef IconicState @@ -142,7 +143,7 @@ QString Workspace::desktopName( int desk ) return QString::fromUtf8( rootInfo->desktopName( desk ) ); } -extern Time kwin_time; +extern Time qt_x_time; extern void kwin_updateTime(); // used to store the return values of @@ -511,7 +512,7 @@ bool Workspace::workspaceEvent( XEvent * e ) { if ( mouse_emulation && e->type == ButtonPress || e->type == ButtonRelease ) { mouse_emulation = FALSE; - XUngrabKeyboard( qt_xdisplay(), kwin_time ); + XUngrabKeyboard( qt_xdisplay(), qt_x_time ); } if ( e->type == PropertyNotify || e->type == ClientMessage ) { @@ -532,8 +533,8 @@ bool Workspace::workspaceEvent( XEvent * e ) switch (e->type) { case ButtonPress: if ( tab_grab || control_grab ) { - XUngrabKeyboard(qt_xdisplay(), kwin_time); - XUngrabPointer( qt_xdisplay(), kwin_time); + XUngrabKeyboard(qt_xdisplay(), qt_x_time); + XUngrabPointer( qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); tab_grab = control_grab = false; @@ -542,6 +543,18 @@ bool Workspace::workspaceEvent( XEvent * e ) 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 @@ -854,7 +867,7 @@ void Workspace::slotWalkThroughWindows() if ( tab_grab || control_grab ) return; if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable() ) { - //XUngrabKeyboard(qt_xdisplay(), kwin_time); // need that because of accelerator raw mode + //XUngrabKeyboard(qt_xdisplay(), qt_x_time); // need that because of accelerator raw mode // CDE style raise / lower CDEWalkThroughWindows( true ); } else { @@ -956,14 +969,14 @@ bool Workspace::startKDEWalkThroughWindows() ButtonMotionMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask), GrabModeAsync, GrabModeAsync, - None, None, kwin_time ) != GrabSuccess ) { + None, None, qt_x_time ) != GrabSuccess ) { return FALSE; } if ( XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, - kwin_time) != GrabSuccess ) { - XUngrabPointer( qt_xdisplay(), kwin_time); + qt_x_time) != GrabSuccess ) { + XUngrabPointer( qt_xdisplay(), qt_x_time); return FALSE; } tab_grab = TRUE; @@ -980,14 +993,14 @@ bool Workspace::startWalkThroughDesktops( int mode ) ButtonMotionMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask), GrabModeAsync, GrabModeAsync, - None, None, kwin_time ) != GrabSuccess ) { + None, None, qt_x_time ) != GrabSuccess ) { return FALSE; } if ( XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, - kwin_time) != GrabSuccess ) { - XUngrabPointer( qt_xdisplay(), kwin_time); + qt_x_time) != GrabSuccess ) { + XUngrabPointer( qt_xdisplay(), qt_x_time); return FALSE; } control_grab = TRUE; @@ -1113,8 +1126,8 @@ bool Workspace::keyPress(XKeyEvent& ev) if (control_grab || tab_grab){ if ((keyQt & 0xffff) == Qt::Key_Escape){ - XUngrabKeyboard(qt_xdisplay(), kwin_time); - XUngrabPointer( qt_xdisplay(), kwin_time); + XUngrabKeyboard(qt_xdisplay(), qt_x_time); + XUngrabPointer( qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); tab_grab = FALSE; @@ -1167,8 +1180,8 @@ bool Workspace::keyRelease(XKeyEvent& ev) if( !release ) return FALSE; if (tab_grab){ - XUngrabPointer( qt_xdisplay(), kwin_time); - XUngrabKeyboard(qt_xdisplay(), kwin_time); + XUngrabPointer( qt_xdisplay(), qt_x_time); + XUngrabKeyboard(qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); tab_grab = false; @@ -1177,8 +1190,8 @@ bool Workspace::keyRelease(XKeyEvent& ev) } } if (control_grab){ - XUngrabPointer( qt_xdisplay(), kwin_time); - XUngrabKeyboard(qt_xdisplay(), kwin_time); + XUngrabPointer( qt_xdisplay(), qt_x_time); + XUngrabKeyboard(qt_xdisplay(), qt_x_time); tab_box->hide(); keys->setEnabled( true ); control_grab = False; @@ -1394,6 +1407,7 @@ void Workspace::activateClient( Client* c, bool force ) if (!c->isOnDesktop(currentDesktop()) ) { setCurrentDesktop( c->desktop() ); } + c->updateUserTime(); } @@ -2079,7 +2093,6 @@ void Workspace::lowerClient( Client* c ) for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { new_stack[i++] = (*it)->winId(); } -// XRaiseWindow(qt_xdisplay(), new_stack[0]); XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; @@ -2166,7 +2179,6 @@ void Workspace::raiseClient( Client* c ) for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { new_stack[i++] = (*it)->winId(); } -// XRaiseWindow(qt_xdisplay(), new_stack[0]); XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; @@ -2179,6 +2191,29 @@ void Workspace::raiseClient( Client* c ) 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; @@ -2264,7 +2299,7 @@ void Workspace::focusToNull(){ InputOnly, CopyFromParent, mask, &attr); XMapWindow(qt_xdisplay(), null_focus_window); } - XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, kwin_time ); + XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time ); if( !block_focus ) setActiveClient( NULL ); } @@ -2987,7 +3022,7 @@ void Workspace::slotMouseEmulation() { if ( mouse_emulation ) { - XUngrabKeyboard(qt_xdisplay(), kwin_time); + XUngrabKeyboard(qt_xdisplay(), qt_x_time); mouse_emulation = FALSE; return; } @@ -2995,7 +3030,7 @@ void Workspace::slotMouseEmulation() if ( XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, - kwin_time) == GrabSuccess ) { + qt_x_time) == GrabSuccess ) { mouse_emulation = TRUE; mouse_emulation_state = 0; mouse_emulation_window = 0; @@ -3365,7 +3400,7 @@ unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation t e.window = w; e.root = qt_xrootwin(); e.subwindow = w; - e.time = kwin_time; + e.time = qt_x_time; e.x = x; e.y = y; e.x_root = pos.x(); @@ -3379,7 +3414,7 @@ unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation t e.window = w; e.root = qt_xrootwin(); e.subwindow = w; - e.time = kwin_time; + e.time = qt_x_time; e.x = x; e.y = y; e.x_root = pos.x(); @@ -3495,7 +3530,7 @@ bool Workspace::keyPressMouseEmulation( XKeyEvent& ev ) } // fall through case XK_Escape: - XUngrabKeyboard(qt_xdisplay(), kwin_time); + XUngrabKeyboard(qt_xdisplay(), qt_x_time); mouse_emulation = FALSE; return TRUE; default: @@ -3657,7 +3692,7 @@ void Workspace::storeLegacySession( KConfig* config ) 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] = kwin_time; + ev.xclient.data.l[1] = qt_x_time; XSelectInput(newdisplay, w, PropertyChangeMask|StructureNotifyMask); XSendEvent(newdisplay, w, False, 0, &ev); } @@ -4126,7 +4161,7 @@ void Workspace::focusEnsurance() if ( !last_active_client ) last_active_client = topClientOnDesktop(); if ( last_active_client && last_active_client->isVisible() ) { - kwin_time = CurrentTime; + qt_x_time = CurrentTime; requestFocus( last_active_client ); } } diff --git a/workspace.h b/workspace.h index 665d597ce0..f90867d910 100644 --- a/workspace.h +++ b/workspace.h @@ -163,6 +163,7 @@ public: QPoint adjustClientPosition( Client* c, QPoint pos ); void raiseClient( Client* c ); void lowerClient( Client* c ); + void stackClientUnderActive( Client* ); void raiseOrLowerClient( Client * ); void reconfigure();