diff --git a/notifications.cpp b/notifications.cpp index 528668b65d..70a0d307bf 100644 --- a/notifications.cpp +++ b/notifications.cpp @@ -92,9 +92,11 @@ QString Notify::eventToName( Event e ) return event; } +static bool forgetIt = FALSE; +QList< Notify::EventData > Notify::pending_events; + 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 @@ -102,11 +104,35 @@ bool Notify::raise( Event e, const QString& message, Client* c ) if ( event.isNull() ) return false; - forgetIt= !KNotifyClient::event( c ? c->window() : 0, event, message ); +// There may be a deadlock if KNotify event is sent while KWin has X grabbed. +// If KNotify is not running, KLauncher may do X requests (startup notification, whatever) +// that will block it. And KNotifyClient waits for the launch to succeed, which means +// KLauncher waits for X and KWin waits for KLauncher. So postpone events in such case. + if( grabbedXServer()) + { + EventData data; + data.event = event; + data.message = message; + data.window = c ? c->window() : 0; + pending_events.append( data ); + return true; + } + forgetIt= !KNotifyClient::event( c ? c->window() : 0, event, message ); return !forgetIt; } +void Notify::sendPendingEvents() + { + while( !pending_events.isEmpty()) + { + EventData data = pending_events.first(); + pending_events.pop_front(); + if( !forgetIt ) + forgetIt= !KNotifyClient::event( data.window, data.event, data.message ); + } + } + bool Notify::makeDemandAttention( Event e ) { QString event = eventToName( e ); diff --git a/notifications.h b/notifications.h index 478d15a6a4..e8d3cbee4d 100644 --- a/notifications.h +++ b/notifications.h @@ -14,6 +14,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include +#include namespace KWinInternal { @@ -51,8 +52,16 @@ class Notify static bool raise( Event, const QString& message = QString::null, Client* c = NULL ); static bool makeDemandAttention( Event ); + static void sendPendingEvents(); private: static QString eventToName( Event ); + struct EventData + { + QString event; + QString message; + long window; + }; + static QList< EventData > pending_events; }; } // namespace diff --git a/utils.cpp b/utils.cpp index c3fb0732e7..a5143e04ef 100644 --- a/utils.cpp +++ b/utils.cpp @@ -32,6 +32,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include "atoms.h" +#include "notifications.h" #endif @@ -282,8 +283,18 @@ void ungrabXServer() { assert( server_grab_count > 0 ); if( --server_grab_count == 0 ) + { XUngrabServer( QX11Info::display()); + XFlush( QX11Info::display()); + Notify::sendPendingEvents(); + } } + +bool grabbedXServer() + { + return server_grab_count > 0; + } + #endif bool isLocalMachine( const QByteArray& host ) diff --git a/utils.h b/utils.h index 38828f5e09..c90ab3d2bb 100644 --- a/utils.h +++ b/utils.h @@ -188,6 +188,7 @@ QByteArray getStringProperty(WId w, Atom prop, char separator=0); void updateXTime(); void grabXServer(); void ungrabXServer(); +bool grabbedXServer(); // the docs say it's UrgencyHint, but it's often #defined as XUrgencyHint #ifndef UrgencyHint