Fix captions with non-BMP characters

KWin replaces any non-printable character with a space. This check does not
handle surrogate pairs correctly. Additionally, translators sometimes insert
non-printable soft-hyphens into titles, which also cause KWin to display a
space instead.

This code adds the missing surrogate handling, and (to fix both issues), also
removes non-printable characters instead of replacing them with a space.

Also moved the changed test after these changes, so that changes in non-
printable characters do not cause unneeded redraws.

Unit tests adapted by Vlad Zagorodniy.

Test Plan:

kwrite /tmp/Test😣.txt shows correct title. I also tested actual non-printable
characters, such as 0x1A, and these are correctly omitted.

BUG: 376813
FIXED-IN: 5.15.5

Reviewers: #kwin, zzag

Reviewed By: #kwin, zzag

Subscribers: zzag, grasslin, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D19052
This commit is contained in:
Christoph Feck 2019-04-26 10:52:57 +02:00
parent 97c3cb73ff
commit 57440d1d6b
2 changed files with 39 additions and 14 deletions

View file

@ -49,7 +49,8 @@ private Q_SLOTS:
void init(); void init();
void cleanup(); void cleanup();
void testCaptionSimplified(); void testTrimCaption_data();
void testTrimCaption();
void testFullscreenLayerWithActiveWaylandWindow(); void testFullscreenLayerWithActiveWaylandWindow();
void testFocusInWithWaylandLastActiveWindow(); void testFocusInWithWaylandLastActiveWindow();
void testX11WindowId(); void testX11WindowId();
@ -94,13 +95,23 @@ struct XcbConnectionDeleter
} }
}; };
void X11ClientTest::testTrimCaption_data()
{
QTest::addColumn<QByteArray>("originalTitle");
QTest::addColumn<QByteArray>("expectedTitle");
void X11ClientTest::testCaptionSimplified() QTest::newRow("simplified")
<< QByteArrayLiteral("Was tun, wenn Schüler Autismus haben?\342\200\250\342\200\250\342\200\250 Marlies Hübner - Mozilla Firefox")
<< QByteArrayLiteral("Was tun, wenn Schüler Autismus haben? Marlies Hübner - Mozilla Firefox");
QTest::newRow("with emojis")
<< QByteArrayLiteral("\bTesting non\302\255printable:\177, emoij:\360\237\230\203, non-characters:\357\277\276")
<< QByteArrayLiteral("Testing nonprintable:, emoij:\360\237\230\203, non-characters:");
}
void X11ClientTest::testTrimCaption()
{ {
// this test verifies that caption is properly trimmed // this test verifies that caption is properly trimmed
// see BUG 323798 comment #12
QSignalSpy windowAddedSpy(effects, &EffectsHandler::windowAdded);
QVERIFY(windowAddedSpy.isValid());
// create an xcb window // create an xcb window
QScopedPointer<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr)); QScopedPointer<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
@ -119,8 +130,8 @@ void X11ClientTest::testCaptionSimplified()
xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
xcb_icccm_set_wm_normal_hints(c.data(), w, &hints); xcb_icccm_set_wm_normal_hints(c.data(), w, &hints);
NETWinInfo winInfo(c.data(), w, rootWindow(), NET::Properties(), NET::Properties2()); NETWinInfo winInfo(c.data(), w, rootWindow(), NET::Properties(), NET::Properties2());
const QByteArray origTitle = QByteArrayLiteral("Was tun, wenn Schüler Autismus haben?\342\200\250\342\200\250\342\200\250 Marlies Hübner - Mozilla Firefox"); QFETCH(QByteArray, originalTitle);
winInfo.setName(origTitle.constData()); winInfo.setName(originalTitle);
xcb_map_window(c.data(), w); xcb_map_window(c.data(), w);
xcb_flush(c.data()); xcb_flush(c.data());
@ -131,8 +142,8 @@ void X11ClientTest::testCaptionSimplified()
Client *client = windowCreatedSpy.first().first().value<Client*>(); Client *client = windowCreatedSpy.first().first().value<Client*>();
QVERIFY(client); QVERIFY(client);
QCOMPARE(client->window(), w); QCOMPARE(client->window(), w);
QVERIFY(client->caption() != QString::fromUtf8(origTitle)); QFETCH(QByteArray, expectedTitle);
QCOMPARE(client->caption(), QString::fromUtf8(origTitle).simplified()); QCOMPARE(client->caption(), QString::fromUtf8(expectedTitle));
// and destroy the window again // and destroy the window again
xcb_unmap_window(c.data(), w); xcb_unmap_window(c.data(), w);

View file

@ -1463,13 +1463,27 @@ static const QChar LRM(0x200E);
void Client::setCaption(const QString& _s, bool force) void Client::setCaption(const QString& _s, bool force)
{ {
if (!force && _s == cap_normal)
return;
QString s(_s); QString s(_s);
for (int i = 0; i < s.length(); ++i) for (int i = 0; i < s.length(); ) {
if (!s[i].isPrint()) if (!s[i].isPrint()) {
s[i] = QChar(u' '); if (QChar(s[i]).isHighSurrogate() && i + 1 < s.length() && QChar(s[i + 1]).isLowSurrogate()) {
const uint uc = QChar::surrogateToUcs4(s[i], s[i + 1]);
if (!QChar::isPrint(uc)) {
s.remove(i, 2);
} else {
i += 2;
}
continue;
}
s.remove(i, 1);
continue;
}
++i;
}
const bool changed = (s != cap_normal); const bool changed = (s != cap_normal);
if (!force && !changed) {
return;
}
cap_normal = s; cap_normal = s;
if (!force && !changed) { if (!force && !changed) {
emit captionChanged(); emit captionChanged();