Commit b15eaf38 authored by Eike Hein's avatar Eike Hein

Handle clients which change window metadata during early startup

Summary:
Some apps initially show their window with bogus/useless window
metadata and then update to useful metadata during early startup.
For example, LibreOffice sets WM_CLASS to soffice/Soffice and
then updates to libreoffice-writer/libreoffice. This leads to
a poor user experience on particular the Icons-only Task Manager,
but also the regular Task Manager depending on settings.

Depending on its configuration (and Icons-only Task Manager is
a particular set of configuration options, as far as the model
is concerned), TasksModel will try to sort a new window task
adjacent to its launcher task. The appearance of a new window
task also causes matching (in terms of identification) launcher
or startup tasks to be filtered out. To the user, this forms a
lifecycle of the launcher being replaced by the window in-place
(and a startup state inbetween, optionally but by default).

Prior to this patch, this sorting decision was only done once,
when a new window enters the source model stack. This meant the
LibreOffice window would initially be sorted into the "wrong"
spot (the bogus metadata doesn't allow us to relate it to its
launcher) and then, following the metadata change, stick to the
wrong position.

Simply changing the code to sort things again on any metadata
change would not have been good enough: Metadata changes can
occur at any time, and things should not just move around on
the user - this sort mode is called "Manual" for a reason. Also,
the visual result would still be poor: The window would initially
appear at the wrong position, then move to the right one a bit
later.

This patch takes the following approach:
* It adds a new config key to taskmanagerrulesrc for listing
  ids of matching tasks to completely hide, and of course the
  code needed to implement this.
* It adds LibreOffice' bogus initial metadata to this key, so
  the tasks is initially hidden.
* The sort code skips over hidden window tasks in the sort
  insert queue instead of moving them. The queue is marked as
  stale then, and cleared on unrelated windowing system changes.
* It resorts when tasks are unhidden (i.e. once the metadata
  update has occured and the task no longer matches the above
  config key).

The visual result is that the startup notification on the
launcher spins a little bit longer than before, even though the
window has already appeared (although LO lags in filling in its
contents anyway), and then morphs into the window task
representation once the client has completed the window metadata
change. This happens in such a short order as to be more or less
imperceptible.

If startup notifications are turned off it's broadly the same,
minus the spinning.

BUG:396871

Reviewers: davidedmundson, broulik, ngraham

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D15410
parent 4b4439fe
......@@ -11,3 +11,4 @@ VirtualBox Machine=virtualbox
[Settings]
MatchCommandLineFirst=perl
TryIgnoreRuntimes=perl
SkipTaskbar=Soffice
This diff is collapsed.
......@@ -65,6 +65,11 @@ AppData appDataFromUrl(const QUrl &url, const QIcon &fallbackIcon)
pixmap.loadFromData(bytes);
data.icon.addPixmap(pixmap);
}
if (uQuery.hasQueryItem(QLatin1String("skipTaskbar"))) {
QString skipTaskbar(uQuery.queryItemValue(QLatin1String("skipTaskbar")));
data.skipTaskbar = (skipTaskbar == QStringLiteral("true"));
}
}
// applications: URLs are used to refer to applications by their KService::menuId
......@@ -405,6 +410,23 @@ QUrl windowUrlFromMetadata(const QString &appId, quint32 pid,
services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ Name) and (not exist NoDisplay or not NoDisplay)").arg(appId));
sortServicesByMenuId(services, appId);
}
// Check rules configuration for whether we want to hide this task.
// Some window tasks update from bogus to useful metadata early during startup.
// This config key allows listing the bogus metadata, and the matching window
// tasks are hidden until they perform a metadate update that stops them from
// matching.
QStringList skipTaskbar = set.readEntry("SkipTaskbar", QStringList());
if (skipTaskbar.contains(appId)) {
QUrlQuery query(url);
query.addQueryItem(QStringLiteral("skipTaskbar"), QStringLiteral("true"));
url.setQuery(query);
} else if (skipTaskbar.contains(mapped)) {
QUrlQuery query(url);
query.addQueryItem(QStringLiteral("skipTaskbar"), QStringLiteral("true"));
url.setQuery(query);
}
}
// Ok, absolute *last* chance, try matching via pid (but only if we have not already tried this!) ...
......@@ -451,7 +473,8 @@ QUrl windowUrlFromMetadata(const QString &appId, quint32 pid,
// applications: URLs are used to refer to applications by their KService::menuId
// (i.e. .desktop file name) rather than the absolute path to a .desktop file.
if (!menuId.isEmpty()) {
return QUrl(QStringLiteral("applications:") + menuId);
url.setUrl(QStringLiteral("applications:") + menuId);
return url;
}
QString path = services.at(0)->entryPath();
......@@ -461,7 +484,10 @@ QUrl windowUrlFromMetadata(const QString &appId, quint32 pid,
}
if (!path.isEmpty()) {
QString query = url.query();
url = QUrl::fromLocalFile(path);
url.setQuery(query);
return url;
}
}
......
......@@ -40,6 +40,7 @@ struct AppData
QString genericName; // Generic application name.
QIcon icon;
QUrl url;
bool skipTaskbar = false;
};
enum UrlComparisonMode {
......
......@@ -86,7 +86,8 @@ void WaylandTasksModel::Private::init()
QVector<int>{Qt::DecorationRole, AbstractTasksModel::AppId,
AbstractTasksModel::AppName, AbstractTasksModel::GenericName,
AbstractTasksModel::LauncherUrl,
AbstractTasksModel::LauncherUrlWithoutIcon});
AbstractTasksModel::LauncherUrlWithoutIcon,
AbstractTasksModel::SkipTaskbar});
};
rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc"));
......@@ -203,7 +204,7 @@ void WaylandTasksModel::Private::addWindow(KWayland::Client::PlasmaWindow *windo
// Refresh roles satisfied from the app data cache.
this->dataChanged(window, QVector<int>{AppId, AppName, GenericName,
LauncherUrl, LauncherUrlWithoutIcon});
LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar});
}
);
......@@ -405,7 +406,7 @@ QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const
} else if (role == IsDemandingAttention) {
return window->isDemandingAttention();
} else if (role == SkipTaskbar) {
return window->skipTaskbar();
return window->skipTaskbar() || d->appData(window).skipTaskbar;
} else if (role == SkipPager) {
// FIXME Implement.
} else if (role == AppPid) {
......
......@@ -116,7 +116,8 @@ void XWindowTasksModel::Private::init()
QVector<int>{Qt::DecorationRole, AbstractTasksModel::AppId,
AbstractTasksModel::AppName, AbstractTasksModel::GenericName,
AbstractTasksModel::LauncherUrl,
AbstractTasksModel::LauncherUrlWithoutIcon});
AbstractTasksModel::LauncherUrlWithoutIcon,
AbstractTasksModel::SkipTaskbar});
};
sycocaChangeTimer.setSingleShot(true);
......@@ -330,7 +331,7 @@ void XWindowTasksModel::Private::windowChanged(WId window, NET::Properties prope
|| properties2 & (NET::WM2DesktopFileName | NET::WM2WindowClass)) {
wipeInfoCache = true;
wipeAppDataCache = true;
changedRoles << Qt::DecorationRole << AppId << AppName << GenericName << LauncherUrl << AppPid;
changedRoles << Qt::DecorationRole << AppId << AppName << GenericName << LauncherUrl << AppPid << SkipTaskbar;
}
if (properties & (NET::WMName | NET::WMVisibleName)) {
......@@ -645,7 +646,9 @@ QVariant XWindowTasksModel::data(const QModelIndex &index, int role) const
const KWindowInfo *info = d->windowInfo(window);
// _NET_WM_WINDOW_TYPE_UTILITY type windows should not be on task bars,
// but they should be shown on pagers.
return (info->hasState(NET::SkipTaskbar) || info->windowType(NET::UtilityMask) == NET::Utility);
return (info->hasState(NET::SkipTaskbar)
|| info->windowType(NET::UtilityMask) == NET::Utility
|| d->appData(window).skipTaskbar);
} else if (role == SkipPager) {
return d->windowInfo(window)->hasState(NET::SkipPager);
} else if (role == AppPid) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment