From 3c633a48ab63bfb87b5896f24b103af0611dbe5b Mon Sep 17 00:00:00 2001 From: Chani Armitage Date: Fri, 24 Sep 2010 12:03:22 +0000 Subject: [PATCH] Use an X property for activity associations the new property name is "_KDE_NET_WM_ACTIVITIES", of type XA_STRING, and it's a comma-separated list of activity UUIDs. kwin should respond to other processes changing the activity list for a window, and filter out any bogus IDs. It also caches KActivityController's list of activities to prevent dbus deadlocks. CCMAIL: plasma-devel@kde.org svn path=/trunk/KDE/kdebase/workspace/; revision=1179043 --- atoms.cpp | 3 +++ atoms.h | 1 + client.cpp | 46 ++++++++++++++++++++++++++++++++++++++++------ client.h | 4 +++- events.cpp | 2 ++ manage.cpp | 3 +++ workspace.cpp | 8 ++++++++ workspace.h | 4 ++++ 8 files changed, 64 insertions(+), 7 deletions(-) diff --git a/atoms.cpp b/atoms.cpp index f92b625ce6..0c9b66c5f1 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -38,6 +38,9 @@ Atoms::Atoms() atoms[n] = &kwin_running; names[n++] = (char *) "KWIN_RUNNING"; + atoms[n] = &activities; + names[n++] = (char *) "_KDE_NET_WM_ACTIVITIES"; + atoms[n] = &wm_protocols; names[n++] = (char *) "WM_PROTOCOLS"; diff --git a/atoms.h b/atoms.h index 61189f45c3..4002649e56 100644 --- a/atoms.h +++ b/atoms.h @@ -34,6 +34,7 @@ class Atoms Atoms(); Atom kwin_running; + Atom activities; Atom wm_protocols; Atom wm_delete_window; diff --git a/client.cpp b/client.cpp index b941d07089..a0bb9a91aa 100644 --- a/client.cpp +++ b/client.cpp @@ -1523,13 +1523,12 @@ void Client::setDesktop( int desktop ) */ void Client::setOnActivity( const QString &activity, bool enable ) { - if( activityList.contains(activity) == enable ) //nothing to do + QStringList newActivitiesList = activities(); + if( newActivitiesList.contains(activity) == enable ) //nothing to do return; - //check whether we should set it to all activities - QStringList newActivitiesList = activityList; if (enable) { - QStringList allActivities = KActivityConsumer().availableActivities(); + QStringList allActivities = workspace()->activityList(); if( !allActivities.contains(activity) ) //bogus ID return; newActivitiesList.append(activity); @@ -1544,13 +1543,19 @@ void Client::setOnActivity( const QString &activity, bool enable ) */ void Client::setOnActivities( QStringList newActivitiesList ) { - QStringList allActivities = KActivityConsumer().availableActivities(); + QStringList allActivities = workspace()->activityList(); if( newActivitiesList.size() == allActivities.size() || newActivitiesList.isEmpty() ) { setOnAllActivities(true); return; } + + QByteArray joined = newActivitiesList.join(",").toAscii(); + char *data = joined.data(); activityList = newActivitiesList; + XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8, + PropModeReplace, (unsigned char *)data, joined.size()); + updateActivities( false ); } @@ -1589,7 +1594,6 @@ int Client::desktop() const * Returns the list of activities the client window is on. * if it's on all activities, the list will be empty. * Don't use this, use isOnActivity() and friends (from class Toplevel) - * FIXME do I need to consider if it's not mapped yet? */ QStringList Client::activities() const { @@ -1622,6 +1626,7 @@ void Client::setOnAllActivities( bool on ) if( on ) { activityList.clear(); + XDeleteProperty( display(), window(), atoms->activities ); updateActivities( true ); } else @@ -2216,6 +2221,35 @@ QPixmap* kwin_get_menu_pix_hack() return &p; } +void Client::checkActivities() + { + QStringList newActivitiesList; + QByteArray prop = getStringProperty(window(), atoms->activities); + if ( ! prop.isEmpty() ) + newActivitiesList = QString(prop).split(','); + + if (newActivitiesList == activityList) + return; //expected change, it's ok. + + //otherwise, somebody else changed it. we need to validate before reacting + QStringList allActivities = workspace()->activityList(); + if (allActivities.isEmpty()) + { + kDebug() << "no activities!?!?"; + //don't touch anything, there's probably something bad going on and we don't wanna make it worse + return; + } + for ( int i = 0; i < newActivitiesList.size(); ++i ) + { + if ( ! allActivities.contains( newActivitiesList.at(i) ) ) + { + kDebug() << "invalid:" << newActivitiesList.at(i); + newActivitiesList.removeAt( i-- ); + } + } + setOnActivities( newActivitiesList ); + } + } // namespace #include "client.moc" diff --git a/client.h b/client.h index 83e4e62d7b..fa53e43f10 100644 --- a/client.h +++ b/client.h @@ -690,7 +690,9 @@ class Client ElectricMaximizingMode electricMode; friend bool performTransiencyCheck(); - friend class SWrapper::Client; + friend class SWrapper::Client; + + void checkActivities(); }; /** diff --git a/events.cpp b/events.cpp index 6f367c0671..67556b0d71 100644 --- a/events.cpp +++ b/events.cpp @@ -942,6 +942,8 @@ void Client::propertyNotifyEvent( XPropertyEvent* e ) getMotifHints(); else if( e->atom == atoms->net_wm_sync_request_counter ) getSyncCounter(); + else if( e->atom == atoms->activities ) + checkActivities(); break; } } diff --git a/manage.cpp b/manage.cpp index c0617a34c9..9cceb70c26 100644 --- a/manage.cpp +++ b/manage.cpp @@ -159,6 +159,8 @@ bool Client::manage( Window w, bool isMapped ) init_minimize = rules()->checkMinimize( init_minimize, !isMapped ); noborder = rules()->checkNoBorder( noborder, !isMapped ); + checkActivities(); + // Initial desktop placement if( session ) { @@ -171,6 +173,7 @@ bool Client::manage( Window w, bool isMapped ) // If this window is transient, ensure that it is opened on the // same window as its parent. this is necessary when an application // starts up on a different desktop than is currently displayed + //FIXME do the same for activities too if( isTransient() ) { ClientList mainclients = mainClients(); diff --git a/workspace.cpp b/workspace.cpp index bcb57f0fce..e9048655ab 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -248,6 +248,7 @@ Workspace::Workspace( bool restore ) connect( &activityController_, SIGNAL( currentActivityChanged(QString) ), SLOT( updateCurrentActivity(QString) )); connect( &activityController_, SIGNAL( activityRemoved(QString) ), SLOT( activityRemoved(QString) )); + connect( &activityController_, SIGNAL( activityAdded(QString) ), SLOT( activityAdded(QString) )); } void Workspace::init() @@ -372,6 +373,7 @@ void Workspace::init() } if( !setCurrentDesktop( initial_desktop )) setCurrentDesktop( 1 ); + allActivities_ = activityController_.availableActivities(); updateCurrentActivity( activityController_.currentActivity() ); // Now we know how many desktops we'll have, thus we initialize the positioning object @@ -1674,12 +1676,18 @@ void Workspace::updateCurrentActivity(const QString &new_activity) */ void Workspace::activityRemoved(const QString &activity) { + allActivities_.removeOne(activity); foreach (Client *client, stacking_order) { client->setOnActivity(activity, false); } } +void Workspace::activityAdded(const QString &activity) + { + allActivities_ << activity; + } + /** * Called only from D-Bus */ diff --git a/workspace.h b/workspace.h index f7ee4b2b1c..acd13e6a26 100644 --- a/workspace.h +++ b/workspace.h @@ -46,6 +46,7 @@ along with this program. If not, see . class QMenu; class QActionGroup; +class QStringList; class KConfig; class KActionCollection; class KStartupInfo; @@ -315,6 +316,7 @@ class Workspace : public QObject, public KDecorationDefines int* desktopGrid_; int currentDesktop_; QString activity_; + QStringList allActivities_; bool desktopLayoutDynamicity_; KActivityController activityController_; @@ -337,6 +339,7 @@ class Workspace : public QObject, public KDecorationDefines QRect screenGeometry( int screen ) const; int screenNumber( const QPoint& pos ) const; QString currentActivity() const { return activity_; } + QStringList activityList() const { return allActivities_; } // Tab box Client* currentTabBoxClient() const; @@ -759,6 +762,7 @@ class Workspace : public QObject, public KDecorationDefines void updateCurrentActivity(const QString &new_activity); void activityRemoved(const QString &activity); + void activityAdded(const QString &activity); protected: bool keyPressMouseEmulation( XKeyEvent& ev );