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
This commit is contained in:
Chani Armitage 2010-09-24 12:03:22 +00:00
parent 19a9fb6e37
commit 3c633a48ab
8 changed files with 64 additions and 7 deletions

View file

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

View file

@ -34,6 +34,7 @@ class Atoms
Atoms();
Atom kwin_running;
Atom activities;
Atom wm_protocols;
Atom wm_delete_window;

View file

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

View file

@ -690,7 +690,9 @@ class Client
ElectricMaximizingMode electricMode;
friend bool performTransiencyCheck();
friend class SWrapper::Client;
friend class SWrapper::Client;
void checkActivities();
};
/**

View file

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

View file

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

View file

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

View file

@ -46,6 +46,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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 );