Use KNotify for notifications about windows demanding attention.

People who use hidden Kicker, have their taskbar configured to show
only windows on the current desktop or similar now may configure
it so that windows that didn't get focus because of focus stealing
prevention now can not only have their taskbar entry marked
but there can be e.g. also a passive popup.


svn path=/trunk/KDE/kdebase/kwin/; revision=419727
This commit is contained in:
Luboš Luňák 2005-05-30 13:31:33 +00:00
parent 2e71a5d3cd
commit 96e1fdab6c
8 changed files with 107 additions and 13 deletions

View file

@ -23,6 +23,8 @@ License. See the file "COPYING" for the exact licensing terms.
#include <qpopupmenu.h>
#include <kxerrorhandler.h>
#include <kstartupinfo.h>
#include <kstringhandler.h>
#include <klocale.h>
#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,

View file

@ -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;

View file

@ -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

View file

@ -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?

View file

@ -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

View file

@ -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( "<p>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.)"
"<ul>"
@ -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.</li>"
"<li><em>Extreme:</em> All windows must be explicitly activated by the user.</li>"
"</ul>" );
"</ul></p>"
"<p>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.</p>" );
QWhatsThis::add( focusStealing, wtstr );
QWhatsThis::add( focusStealingLabel, wtstr );
connect(focusStealing, SIGNAL(activated(int)), SLOT(changed()));

View file

@ -12,15 +12,13 @@ License. See the file "COPYING" for the exact licensing terms.
#include "notifications.h"
#include <knotifyclient.h>
#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

View file

@ -12,9 +12,14 @@ License. See the file "COPYING" for the exact licensing terms.
#ifndef KWIN_NOTIFICATIONS_H
#define KWIN_NOTIFICATIONS_H
#include <stdlib.h>
#include <qstring.h>
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