From 96e1fdab6c4e1e3405aa64dbb3c941dc90dbdb0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?=
Date: Mon, 30 May 2005 13:31:33 +0000
Subject: [PATCH] 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
---
activation.cpp | 38 ++++++++++++++++++++++++++++++-
client.cpp | 4 +++-
client.h | 4 +++-
events.cpp | 5 +++++
eventsrc | 10 +++++++++
kcmkwin/kwinoptions/windows.cpp | 7 ++++--
notifications.cpp | 40 +++++++++++++++++++++++++++------
notifications.h | 12 +++++++++-
8 files changed, 107 insertions(+), 13 deletions(-)
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