diff --git a/activation.cpp b/activation.cpp index 4f426f02d6..44e5b004e7 100644 --- a/activation.cpp +++ b/activation.cpp @@ -23,6 +23,8 @@ License. See the file "COPYING" for the exact licensing terms. #include #include #include +#include +#include #include "notifications.h" #include "atoms.h" @@ -658,10 +660,44 @@ void Client::demandAttention( bool set ) { if( isActive()) set = false; - info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); + if( demands_attention == set ) + return; + demands_attention = set; + if( demands_attention ) + { + // Demand attention flag is often set right from manage(), when focus stealing prevention + // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place + // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry + // to be set. + // Delayed call to KNotify also solves the problem of having X server grab in manage(), + // which may deadlock when KNotify (or KLauncher when launching KNotify) need to access X. + Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther; + // Setting the demands attention state needs to be done directly in KWin, because + // KNotify would try to set it, resulting in a call to KNotify again, etc. + if( Notify::makeDemandAttention( e )) + info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); + + if( demandAttentionKNotifyTimer == NULL ) + { + demandAttentionKNotifyTimer = new QTimer( this ); + connect( demandAttentionKNotifyTimer, SIGNAL( timeout()), SLOT( demandAttentionKNotify())); + } + demandAttentionKNotifyTimer->start( 1000, true ); + } + else + info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); workspace()->clientAttentionChanged( this, set ); } +void Client::demandAttentionKNotify() + { + Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther; + Notify::raise( e, i18n( "Window '%1' demands attention." ).arg( KStringHandler::csqueeze(caption())), this ); + demandAttentionKNotifyTimer->stop(); + demandAttentionKNotifyTimer->deleteLater(); + demandAttentionKNotifyTimer = NULL; + } + // TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*, // ignore already existing splashes, toolbars, utilities, menus and topmenus, diff --git a/client.cpp b/client.cpp index f91cdd6fd6..fa82c59aee 100644 --- a/client.cpp +++ b/client.cpp @@ -91,7 +91,8 @@ Client::Client( Workspace *ws ) border_left( 0 ), border_right( 0 ), border_top( 0 ), - border_bottom( 0 ) + border_bottom( 0 ), + demandAttentionKNotifyTimer( NULL ) // SELI do all as initialization { autoRaiseTimer = 0; @@ -128,6 +129,7 @@ Client::Client( Workspace *ws ) not_obscured = false; urgency = false; ignore_focus_stealing = false; + demands_attention = false; check_active_modal = false; Pdeletewindow = 0; diff --git a/client.h b/client.h index d4f15d26a5..a4a3ae0c43 100644 --- a/client.h +++ b/client.h @@ -341,6 +341,7 @@ class Client : public QObject, public KDecorationDefines private slots: void pingTimeout(); void processKillerExited(); + void demandAttentionKNotify(); private: // ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1 @@ -481,6 +482,7 @@ class Client : public QObject, public KDecorationDefines uint not_obscured : 1; uint urgency : 1; // XWMHints, UrgencyHint uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to this client + uint demands_attention : 1; WindowRules client_rules; void getWMHints(); void readIcons(); @@ -534,7 +536,7 @@ class Client : public QObject, public KDecorationDefines uint rule_opacity_inactive; //dto. //int shadeOriginalHeight; bool isBMP_; - + QTimer* demandAttentionKNotifyTimer; }; // NET WM Protocol handler class diff --git a/events.cpp b/events.cpp index b8eb51e35c..b52e8dc580 100644 --- a/events.cpp +++ b/events.cpp @@ -543,6 +543,11 @@ bool Client::windowEvent( XEvent* e ) } if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 ) startupIdChanged(); + if( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry ) + { + if( demandAttentionKNotifyTimer != NULL ) + demandAttentionKNotify(); + } } // TODO move all focus handling stuff to separate file? diff --git a/eventsrc b/eventsrc index f8506e8b56..923bd0a271 100644 --- a/eventsrc +++ b/eventsrc @@ -3530,3 +3530,13 @@ Comment[uz]=Ойнанинг ўлчами ўзгариб бўлди Comment[wa]=On purnea a fini d' candjî s' grandeu Comment[xx]=xxA window has finished resizingxx default_presentation=0 + +[demandsattentioncurrent] +Name=Window On Current Desktop Demands Attention +Comment=A window on the current virtual desktop demands attention +default_presentation=64 + +[demandsattentionother] +Name=Window On Other Desktop Demands Attention +Comment=A window on an inactive virtual desktop demands attention +default_presentation=64 diff --git a/kcmkwin/kwinoptions/windows.cpp b/kcmkwin/kwinoptions/windows.cpp index 8ebd63b7f4..0735c2c58e 100644 --- a/kcmkwin/kwinoptions/windows.cpp +++ b/kcmkwin/kwinoptions/windows.cpp @@ -591,7 +591,7 @@ KAdvancedConfig::KAdvancedConfig (bool _standAlone, KConfig *_config, QWidget *p focusStealingLabel->setBuddy( focusStealing ); focusStealingLayout->addWidget( focusStealingLabel ); focusStealingLayout->addWidget( focusStealing, AlignLeft ); - wtstr = i18n( "This option specifies how much KWin will try to prevent unwanted focus stealing " + wtstr = i18n( "

This option specifies how much KWin will try to prevent unwanted focus stealing " "caused by unexpected activation of new windows. (Note: This feature does not " "work with the Focus Under Mouse or Focus Strictly Under Mouse focus policies.)" "

    " @@ -606,7 +606,10 @@ KAdvancedConfig::KAdvancedConfig (bool _standAlone, KConfig *_config, QWidget *p "or if they belong to the currently active application. This setting is probably " "not really usable when not using mouse focus policy." "
  • Extreme: All windows must be explicitly activated by the user.
  • " - "
" ); + "

" + "

Windows that are prevented from stealing focus are marked as demanding attention, " + "which by default means their taskbar entry will be highlighted. This can be changed " + "in the Notifications control module.

" ); QWhatsThis::add( focusStealing, wtstr ); QWhatsThis::add( focusStealingLabel, wtstr ); connect(focusStealing, SIGNAL(activated(int)), SLOT(changed())); diff --git a/notifications.cpp b/notifications.cpp index ce1ea7e9d2..53a804c3ef 100644 --- a/notifications.cpp +++ b/notifications.cpp @@ -12,15 +12,13 @@ License. See the file "COPYING" for the exact licensing terms. #include "notifications.h" #include +#include "client.h" + namespace KWinInternal { -void Notify::raise( Event e ) +QString Notify::eventToName( Event e ) { - static bool forgetIt = FALSE; - if ( forgetIt ) - return; // no connection was possible, don't try each time - QString event; switch ( e ) { @@ -78,6 +76,12 @@ void Notify::raise( Event e ) case ResizeEnd: event = "resizeend"; break; + case DemandAttentionCurrent: + event = "demandsattentioncurrent"; + break; + case DemandAttentionOther: + event = "demandsattentionother"; + break; default: if ((e > DesktopChange) && (e <= DesktopChange+20)) { @@ -85,11 +89,33 @@ void Notify::raise( Event e ) } break; } + return event; + } +bool Notify::raise( Event e, const QString& message, Client* c ) + { + static bool forgetIt = FALSE; + if ( forgetIt ) + return false; // no connection was possible, don't try each time + + QString event = eventToName( e ); if ( !event ) - return; + return false; - forgetIt= !KNotifyClient::event( 0, event, event ); + forgetIt= !KNotifyClient::event( c ? c->window() : 0, event, message ); + + return !forgetIt; + } + +bool Notify::makeDemandAttention( Event e ) + { + QString event = eventToName( e ); + if( !event ) + return false; + int rep = KNotifyClient::getPresentation( event ); + if( rep == -1 ) + rep = KNotifyClient::getDefaultPresentation( event ); + return rep != -1 && ( rep & KNotifyClient::Taskbar ); } } // namespace diff --git a/notifications.h b/notifications.h index b8fe4c7335..478d15a6a4 100644 --- a/notifications.h +++ b/notifications.h @@ -12,9 +12,14 @@ License. See the file "COPYING" for the exact licensing terms. #ifndef KWIN_NOTIFICATIONS_H #define KWIN_NOTIFICATIONS_H +#include +#include + namespace KWinInternal { +class Client; + class Notify { public: @@ -39,10 +44,15 @@ class Notify MoveEnd, ResizeStart, ResizeEnd, + DemandAttentionCurrent, + DemandAttentionOther, DesktopChange = 100 }; - static void raise( Event ); + static bool raise( Event, const QString& message = QString::null, Client* c = NULL ); + static bool makeDemandAttention( Event ); + private: + static QString eventToName( Event ); }; } // namespace