diff --git a/activation.cpp b/activation.cpp index c946c1b1c8..26a52df81d 100644 --- a/activation.cpp +++ b/activation.cpp @@ -297,45 +297,65 @@ void Workspace::activateClient( Client* c, bool force ) \sa Workspace::activateClient() */ void Workspace::requestFocus( Client* c, bool force ) - { // the 'if( c == active_client ) return;' optimization mustn't be done here + { + takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false); + } + +void Workspace::takeActivity( Client* c, int flags, bool handled ) + { + // the 'if( c == active_client ) return;' optimization mustn't be done here if (!focusChangeEnabled() && ( c != active_client) ) - return; + flags &= ~ActivityFocus; - //TODO will be different for non-root clients. (subclassing?) if ( !c ) { focusToNull(); return; } - if( !c->isOnCurrentDesktop()) // shouldn't happen, call activateClient() if needed + if( flags & ActivityFocus ) { - kdWarning( 1212 ) << "requestFocus: not on current desktop" << endl; - return; + Client* modal = c->findModal(); + if( modal != NULL && modal != c ) + { + if( !modal->isOnDesktop( c->desktop())) + modal->setDesktop( c->desktop()); + // if the click was inside the window (i.e. handled is set), + // but it has a modal, there's no need to use handled mode, because + // the modal doesn't get the click anyway + // raising of the original window needs to be still done + if( flags & ActivityRaise ) + raiseClient( c ); + flags &= ~ActivityRaise; + c = modal; + handled = false; + } } - - Client* modal = c->findModal(); - if( modal != NULL && modal != c ) - { - if( !modal->isOnDesktop( c->desktop())) // move the modal to client's desktop - modal->setDesktop( c->isOnAllDesktops() ? currentDesktop() : c->desktop()); - requestFocus( modal, force ); - return; - } - if ( c->isShown( false ) ) - { - c->takeFocus( force, Allowed ); - should_get_focus.append( c ); - focus_chain.remove( c ); - if ( c->wantsTabFocus() ) - focus_chain.append( c ); - } - else if ( c->isShade() && c->wantsInput()) + if ( !( flags & ActivityFocusForce ) && ( c->isTopMenu() || c->isDock() || c->isSplash()) ) + flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced + if( c->isShade()) { + if( c->wantsInput() && ( flags & ActivityFocus )) + { // client cannot accept focus, but at least the window should be active (window menu, et. al. ) - c->setActive( true ); - focusToNull(); + c->setActive( true ); + focusToNull(); + } + flags &= ~ActivityFocus; + handled = false; // no point, can't get clicks } + if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed + { + kdWarning( 1212 ) << "takeActivity: not shown" << endl; + return; + } + c->takeActivity( flags, handled, Allowed ); + } + +void Workspace::handleActivityRaise( Client* c, Time timestamp ) + { + if( last_restack == CurrentTime || timestampCompare( timestamp, last_restack ) >= 0 ) + raiseClient( c ); } /*! @@ -415,6 +435,10 @@ void Workspace::gotFocusIn( const Client* c ) } } +void Workspace::setShouldGetFocus( Client* c ) + { + should_get_focus.append( c ); + } // focus_in -> the window got FocusIn event // session_active -> the window was active when saving session diff --git a/atoms.cpp b/atoms.cpp index 16c2d19986..89abe0679f 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -18,7 +18,7 @@ namespace KWinInternal Atoms::Atoms() { - const int max = 20; + const int max = 50; Atom* atoms[max]; char* names[max]; Atom atoms_return[max]; @@ -61,6 +61,9 @@ Atoms::Atoms() atoms[n] = &kde_system_tray_embedding; names[n++] = (char*) "_KDE_SYSTEM_TRAY_EMBEDDING"; + + atoms[n] = &net_wm_take_activity; + names[n++] = (char*) "_NET_WM_TAKE_ACTIVITY"; Atom fake; atoms[n] = &fake; diff --git a/atoms.h b/atoms.h index ab06a51532..a2571f79ff 100644 --- a/atoms.h +++ b/atoms.h @@ -36,6 +36,7 @@ class Atoms Atom net_wm_user_time; Atom kde_net_wm_user_creation_time; Atom kde_system_tray_embedding; + Atom net_wm_take_activity; }; diff --git a/client.cpp b/client.cpp index ecc73c5b8a..8b0b477dfc 100644 --- a/client.cpp +++ b/client.cpp @@ -128,6 +128,7 @@ Client::Client( Workspace *ws ) Pdeletewindow = 0; Ptakefocus = 0; + Ptakeactivity = 0; Pcontexthelp = 0; Pping = 0; input = FALSE; @@ -871,7 +872,7 @@ void Client::rawHide() workspace()->clientHidden( this ); } -static void sendClientMessage(Window w, Atom a, long x) +void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3) { XEvent ev; long mask; @@ -881,8 +882,11 @@ static void sendClientMessage(Window w, Atom a, long x) ev.xclient.window = w; ev.xclient.message_type = a; ev.xclient.format = 32; - ev.xclient.data.l[0] = x; + ev.xclient.data.l[0] = protocol; ev.xclient.data.l[1] = qt_x_time; + ev.xclient.data.l[2] = data1; + ev.xclient.data.l[3] = data2; + ev.xclient.data.l[4] = data3; mask = 0L; if (w == qt_xrootwin()) mask = SubstructureRedirectMask; /* magic! */ @@ -1101,15 +1105,43 @@ bool Client::isOnCurrentDesktop() const return isOnDesktop( workspace()->currentDesktop()); } -/*! - Puts the focus on this window. Clients should never calls this - themselves, instead they should use Workspace::requestFocus(). - */ -void Client::takeFocus( bool force, allowed_t ) +// performs activation and/or raising of the window +void Client::takeActivity( int flags, bool handled, allowed_t ) { - if ( !force && ( isTopMenu() || isDock() || isSplash()) ) - return; // toplevel menus and dock windows don't take focus if not forced + if( !handled || !Ptakeactivity ) + { + if( flags & ActivityFocus ) + takeFocus( Allowed ); + if( flags & ActivityRaise ) + workspace()->raiseClient( this ); + return; + } +#ifndef NDEBUG + static Time previous_activity_timestamp; + static Client* previous_client; + if( previous_activity_timestamp == qt_x_time && previous_client != this ) + { + kdWarning( 1212 ) << "Repeated use of the same X timestamp for activity" << endl; + kdDebug( 1212 ) << kdBacktrace() << endl; + } + previous_activity_timestamp = qt_x_time; + previous_client = this; +#endif + int flg = 0; + if( flags & ActivityFocus ) + { + flg |= 1 << 0; + workspace()->setShouldGetFocus( this ); + } + if( flags & ActivityRaise ) + flg |= 1 << 1; + sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_take_activity, flg ); + } + +// performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS +void Client::takeFocus( allowed_t ) + { #ifndef NDEBUG static Time previous_focus_timestamp; static Client* previous_client; @@ -1127,6 +1159,7 @@ void Client::takeFocus( bool force, allowed_t ) } if ( Ptakefocus ) sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus); + workspace()->setShouldGetFocus( this ); } /*! @@ -1299,6 +1332,7 @@ void Client::getWindowProtocols() Pdeletewindow = 0; Ptakefocus = 0; + Ptakeactivity = 0; Pcontexthelp = 0; Pping = 0; @@ -1309,6 +1343,8 @@ void Client::getWindowProtocols() Pdeletewindow = 1; else if (p[i] == atoms->wm_take_focus) Ptakefocus = 1; + else if (p[i] == atoms->net_wm_take_activity) + Ptakeactivity = 1; else if (p[i] == atoms->net_wm_context_help) Pcontexthelp = 1; else if (p[i] == atoms->net_wm_ping) @@ -1462,7 +1498,7 @@ bool Client::wantsTabFocus() const bool Client::wantsInput() const { - return input; + return input || Ptakefocus; } /*! @@ -1647,10 +1683,9 @@ void Client::updateAllowedActions( bool force ) void Client::autoRaise() { workspace()->raiseClient( this ); - delete autoRaiseTimer; - autoRaiseTimer = 0; + cancelAutoRaise(); } - + void Client::cancelAutoRaise() { delete autoRaiseTimer; diff --git a/client.h b/client.h index 5d1dc5c88f..da1235d313 100644 --- a/client.h +++ b/client.h @@ -186,7 +186,8 @@ class Client : public QObject, public KDecorationDefines bool isResizable() const; bool isCloseable() const; // may be closed by the user (may have a close button) - void takeFocus( bool force, allowed_t ); + void takeActivity( int flags, bool handled, allowed_t ); // takes ActivityFlags as arg (in utils.h) + void takeFocus( allowed_t ); void demandAttention( bool set = true ); void setMask( const QRegion& r, int mode = X::Unsorted ); @@ -215,7 +216,7 @@ class Client : public QObject, public KDecorationDefines bool providesContextHelp() const; - bool performMouseCommand( Options::MouseCommand, QPoint globalPos ); + bool performMouseCommand( Options::MouseCommand, QPoint globalPos, bool handled = false ); QCString windowRole() const; QCString sessionId(); @@ -366,6 +367,8 @@ private slots: void pingWindow(); void killProcess( bool ask, Time timestamp = CurrentTime ); void updateUrgency(); + static void sendClientMessage( Window w, Atom a, Atom protocol, + long data1 = 0, long data2 = 0, long data3 = 0 ); void embedClient( Window w, const XWindowAttributes &attr ); void detectNoBorder(); @@ -430,6 +433,7 @@ private slots: uint original_skip_taskbar :1; // unaffected by KWin uint Pdeletewindow :1; // does the window understand the DeleteWindow protocol? uint Ptakefocus :1;// does the window understand the TakeFocus protocol? + uint Ptakeactivity : 1; // does it support _NET_WM_TAKE_ACTIVITY uint Pcontexthelp : 1; // does the window understand the ContextHelp protocol? uint Pping : 1; // does it support _NET_WM_PING? uint input :1; // does the window want input in its wm_hints diff --git a/events.cpp b/events.cpp index c5386115b4..7b72892003 100644 --- a/events.cpp +++ b/events.cpp @@ -110,8 +110,8 @@ void RootInfo::changeActiveWindow( Window w, NET::RequestSource src, Time timest { if( timestamp == CurrentTime ) timestamp = c->userTime(); - if( src == NET::FromUnknown ) - src = NET::FromTool; // KWIN_FOCUS, use qt_x_time as timestamp? + if( src != NET::FromApplication && src != NET::FromActivity && src != FromTool ) + src = NET::FromTool; if( src == NET::FromTool ) workspace->activateClient( c ); else // NET::FromApplication @@ -131,6 +131,21 @@ void RootInfo::changeActiveWindow( Window w, NET::RequestSource src, Time timest } } +void RootInfo::restackWindow( Window w, RequestSource src, Window above, int detail, Time timestamp ) + { + if( Client* c = workspace->findClient( WindowMatchPredicate( w ))) + { + if( timestamp == CurrentTime ) + timestamp = c->userTime(); + if( src != NET::FromApplication && src != NET::FromActivity && src != FromTool ) + src = NET::FromTool; + if( src == NET::FromActivity ) + workspace->handleActivityRaise( c, timestamp ); + else + c->restackWindow( above, detail, src, timestamp, true ); + } + } + void RootInfo::closeWindow(Window w) { Client* c = workspace->findClient( WindowMatchPredicate( w )); @@ -161,12 +176,6 @@ void RootInfo::gotPing( Window w, Time timestamp ) c->gotPing( timestamp ); } -void RootInfo::restackWindow( Window w, RequestSource source, Window above, int detail, Time timestamp ) - { - if( Client* c = workspace->findClient( WindowMatchPredicate( w ))) - c->restackWindow( above, detail, source, timestamp, true ); - } - // **************************************** // Workspace // **************************************** @@ -1089,15 +1098,9 @@ bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, in return true; } - if ( isActive() && w == wrapperId() - && ( options->clickRaise && !bModKeyHeld ) ) - { - if ( button < 4 ) // exclude wheel - autoRaise(); - } - Options::MouseCommand com = Options::MouseNothing; bool was_action = false; + bool perform_handled = false; if ( bModKeyHeld ) { was_action = true; @@ -1119,6 +1122,7 @@ bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, in if( !isActive() && w == wrapperId()) { was_action = true; + perform_handled = true; switch (button) { case Button1: @@ -1134,10 +1138,18 @@ bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, in com = Options::MouseActivateAndPassClick; } } + // active inner window + if( isActive() && w == wrapperId() + && options->clickRaise && button < 4 ) // exclude wheel + { + com = Options::MouseActivateAndPassClick; + was_action = true; + perform_handled = true; + } } if( was_action ) { - bool replay = performMouseCommand( com, QPoint( x_root, y_root) ); + bool replay = performMouseCommand( com, QPoint( x_root, y_root), perform_handled ); if ( isSpecialWindow() && !isOverride()) replay = TRUE; diff --git a/layers.cpp b/layers.cpp index c63f9d9f42..6666aad349 100644 --- a/layers.cpp +++ b/layers.cpp @@ -72,6 +72,8 @@ License. See the file "COPYING" for the exact licensing terms. #include "group.h" #include +extern Time qt_x_time; + namespace KWinInternal { @@ -337,7 +339,10 @@ void Workspace::raiseClient( Client* c ) unconstrained_stacking_order.append( c ); if( !c->isSpecialWindow()) + { most_recently_raised = c; + last_restack = qt_x_time; + } } void Workspace::raiseClientWithinApplication( Client* c ) diff --git a/useractions.cpp b/useractions.cpp index 759ef01655..43235daed7 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -292,7 +292,7 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) /*! Performs a mouse command on this client (see options.h) */ -bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPos) +bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPos, bool handled ) { bool replay = FALSE; switch (command) @@ -316,8 +316,7 @@ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPo break; case Options::MouseActivateAndRaise: replay = isActive(); // for clickraise mode - workspace()->requestFocus( this ); - workspace()->raiseClient( this ); + workspace()->takeActivity( this, ActivityFocus | ActivityRaise, handled && replay ); break; case Options::MouseActivateAndLower: workspace()->requestFocus( this ); @@ -325,15 +324,14 @@ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPo break; case Options::MouseActivate: replay = isActive(); // for clickraise mode - workspace()->requestFocus( this ); + workspace()->takeActivity( this, ActivityFocus, handled && replay ); break; case Options::MouseActivateRaiseAndPassClick: - workspace()->requestFocus( this ); - workspace()->raiseClient( this ); + workspace()->takeActivity( this, ActivityFocus | ActivityRaise, handled ); replay = TRUE; break; case Options::MouseActivateAndPassClick: - workspace()->requestFocus( this ); + workspace()->takeActivity( this, ActivityFocus, handled ); replay = TRUE; break; case Options::MouseActivateRaiseAndMove: diff --git a/utils.h b/utils.h index 7f8d62b331..5bdf7cdbec 100644 --- a/utils.h +++ b/utils.h @@ -66,6 +66,14 @@ inline void operator++( Layer& lay ) lay = static_cast< Layer >( lay + 1 ); } +// for Client::takeActivity() +enum ActivityFlags + { + ActivityFocus = 1 << 0, // focus the window + ActivityFocusForce = 1 << 1, // focus even if Dock etc. + ActivityRaise = 1 << 2 // raise the window + }; + // Some KWin classes, mainly Client and Workspace, are very tighly coupled, // and some of the methods of one class may be called only from speficic places. // Those methods have additional allowed_t argument. If you pass Allowed diff --git a/workspace.cpp b/workspace.cpp index 8b72032b76..9ad6a894f2 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -69,6 +69,7 @@ Workspace::Workspace( bool restore ) last_active_client (0), most_recently_raised (0), movingClient(0), + last_restack (CurrentTime), was_user_interaction (false), session_saving (false), control_grab (false), diff --git a/workspace.h b/workspace.h index 83d925191f..c1b390d460 100644 --- a/workspace.h +++ b/workspace.h @@ -80,11 +80,6 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine template< typename T1, typename T2 > void forEachClient( T1 procedure, T2 predicate ); template< typename T > void forEachClient( T procedure ); - Group* findGroup( Window leader ) const; - void addGroup( Group* group, allowed_t ); - void removeGroup( Group* group, allowed_t ); - Group* findClientLeaderGroup( const Client* c ) const; - QRect clientArea( clientAreaOption, const QPoint& p, int desktop ) const; QRect clientArea( clientAreaOption, const Client* c ) const; @@ -109,13 +104,15 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine // stealing prevention code. Client* mostRecentlyActivatedClient() const; - void setActiveClient( Client*, allowed_t ); void activateClient( Client*, bool force = FALSE ); void requestFocus( Client* c, bool force = FALSE ); + void takeActivity( Client* c, int flags, bool handled ); // flags are ActivityFlags + void handleActivityRaise( Client* c, Time timestamp ); bool allowClientActivation( const Client* c, Time time = -1U, bool focus_in = false, bool session_active = false ); void restoreFocus(); void gotFocusIn( const Client* ); + void setShouldGetFocus( Client* ); bool fakeRequestedActivity( Client* c ); void unfakeActivity( Client* c ); void activateNextClient( Client* c ); @@ -218,6 +215,11 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine // only called from Client::destroyClient() or Client::releaseWindow() void removeClient( Client*, allowed_t ); + void setActiveClient( Client*, allowed_t ); + Group* findGroup( Window leader ) const; + void addGroup( Group* group, allowed_t ); + void removeGroup( Group* group, allowed_t ); + Group* findClientLeaderGroup( const Client* c ) const; bool checkStartupNotification( Window w, KStartupInfoData& data ); @@ -424,6 +426,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine Client* last_active_client; Client* most_recently_raised; // used _only_ by raiseOrLowerClient() Client* movingClient; + Time last_restack; ClientList clients; ClientList desktops;