Skip to content

Monday, 20 May 2024

A few weeks ago (Time flies!) I attended the KDE Goals Sprint in Berlin. I didn't have concrete plans, but I intended to look into accessibility. Quite some time ago I had improved the accessibility of Kleopatra and at Akademy 2023 in Thessaloniki I gave a talk about it. Back then I had taken the easy route fixing everything directly in Kleopatra and working around several issues in Qt instead of fixing the issues in Qt itself so that all apps could profit. Time to do something about it.

(In-)Accessible icon-only buttons

A common problem for accessibility is icon-only buttons. If a button doesn't have text then screen readers can only tell their user that there's a button. That's not very helpful. Sometimes the developers have at least assigned a tool tip to the button. This can be read out by the screen readers (Qt provides the tool tip as accessible description of the button.), but it's often too verbose. To make a button without text accessible the developer has to set the accessible name property of the widget or, in case of a Qt Quick app, the name property of the Accessible QML Type. Unfortunately, that's often forgotten if the UI isn't designed with accessibility in mind.

At the sprint I discussed several ideas with other participants to help developers remember to set the accessible name:

  • A helper class to instantiate in your app which inspects the app's windows and prints a report at the end with all inaccessible icon-only buttons it has found. A bit like Qt's QAbstractItemModelTester or the different compiler sanitizers. I implemented a prototype of such a class, but didn't pursue this further. The downside of this approach is that the developer needs to open each window of the app to find all inaccessible buttons. If they are already aware of the problem then it's probably easier to search the code.
  • Instead of using a helper class to inspect the widget tree from the inside one could inspect the accessibility tree of the app from the outside. This could be built into our Appium-driven UI test framework so that developers don't have to do anything special. Except that they need to write UI tests that open each and every window of their app. I think it's still worth to look into this.
  • Last but not least, we pondered writing a clazy test. Thinking about the many different ways a text can be set on a button (e.g. with KGuiItem::assign) we doubted that it would be feasible to write such a test.

In the end the easiest approach could be education. If the developers are aware of the problem then there's a good chance that they remember to set an accessible name the next time they add an icon-only button to their app.

Accessible date/time inputs

Volker, Carl, David, Harald and me discussed and explored some ideas to make the date and time inputs in Qt Quick apps like Itinerary accessible. In Kleopatra I resorted to allow the user to enter the date in a simple text input instead of trying to make the complex UI of KDE's date picker accessible. Read Volker's blog and David's blog to find out which solutions they found for Qt Quick apps.

A small automation interlude

One advantage of sitting with other people in the same room is that you may overhear them talking about a mistake (e.g. a faulty commit) and you know exactly how to prevent this kind of mistake in the future. In this case the problem was a missing quote character in some YAML file. And the preventive measure was adding a YAML linter CI job. While I was at it I removed some unnecessary code from the CI job and added the job to a second repository.

List views with underlying multi-column model

In several widgets that show or use a simple list of items Qt allows using a model with multiple columns, e.g. QListView, QComboBox, QCompleter. In general this works well except that Qt has a long-standing bug: When navigating through the list screen readers read the entries of the underlying model column by column instead of reading only the entries in the selected model column (QTBUG-33786). In Kleopatra I worked around this bug with a proxy model which pretended that the model only had one column.

During the sprint I finally sat down and prepared a fix for Qt. For better readability I split my changes in five separate commits which resulted in five separate patches for Qt: 556857, 556858, 556859, 556860, 556861. Being used to multi-commit MRs in GitLab I wondered if I had done something wrong when I submitted my changes, but apparently that's Gerrit's way of handling patch reviews. The first two commits are code clean-ups, the third commit changes one aspect of the accessibility test for list views, the fourth commit is the actual fix, and the fifth commit adds a few more unit tests I find useful. The first three commits have been merged, but the actual fix is still waiting for a review.

After that I looked into the problem that QListView emitted an accessibility focus event when the current item changed even if the list view didn't have focus. I found out that this had been fixed recently by a fellow Qt contributor who ran into the same problem independently of me. This meant that I could remove the workaround in Kleopatra for new enough Qt.

Thanks to MBition for hosting us and to Aleix for making sure we don't starve. And many thanks to those donating to KDE which makes these sprints possible.

Friday, 17 May 2024

MarkNote 1.2 🔗

Carl Schwan CarlSchwan 16:15 +00:00
RSS

The MarkNote team is happy to announce the 1.2 release of MarkNote, KDE’s WYSIWYG note-taking application. Marknote lets you create rich text notes and easily organise them into notebooks. You can personalise your notebooks by choosing an icon and accent color for each one, making it easy to distinguish between them and keep your notes at your fingertips. Your notes are saved as Markdown files in your Documents folder, making it easy to use your notes outside of Marknote as well as inside the app.

Notes management

This releases brings highly wanted features like the ability to choose a custom folder where to store your notes. Mathis Brüchert also added the ability to change the sorting of notes from alphabetically to by date.

Mathis made the sidebar collapsable and added a focus mode where everything but the editing page is displayed.

Finally if you prefer to just use Marknote as a Markdown editor, we made it possible to just open any markdown files directly from the file browser or the console. Additionally Marknote supports markdown files with a so called front matter, which is a common way to inject metadata to markdown in static website generators like Hugo and Jekyll.

Editing

In term of edition support, the subset of markdown supported increased again. Now it is possible to add and edit tables.

 

Additionally we started transforming inline markdown directly to rich text as you type. Support is limited to a few markdown constructs but is likely to grow over time.

You can now customize the font used by editor.

 

Aside from being able to edit text, it’s now possible to also create sketches directly from MarkNote.

 

Mobile Support

Mathis took care of ensuring MarkNote was fully usable when used with Plasma Mobile.

Windows and macOS support

Marknote now provides nightly builds for Windows and macOS. While the Windows builds should be fully usable, the macOS build still has an issue where most icons are not displayed. This should be fixed as soon as we can make use of the new KIconTheme version.

As part of the work to improve the macOS support, Marknote also gained global menu support for Linux.

Others

  • The command bar will show translated shortcuts. (Laurent Montel)
  • Unify spelling of MarkNote and fix typos in the README.md (Jonah Brüchert)

Get Involved

Mathis created a Matrix channel for MarkNote: #marknote:kde.org. There is also still a lot of small improvements that can be done everywhere and which don’t require a lot of programming experience. Take a look at these two tasks !31 and !27 for some inspiration on what you could work on.

强制类型转换

C++提供了四个强制类型转换的关键字:

  • static_cast
  • const_cast
  • reinterpret_cast
  • ``dynamic_cast`

static_cast

1
static_cast<目标类型>(表达式)
1
2
int num = 2;
double result =static_cast<double>(num);

该运算符将表达式转换为目标类型。但没有进行运行时类型检查来保证转换的安全性

主要用法

  1. 用于类层次结构中父类和子类之间指针或引用的转换.进行上行转换是安全的(即将子类的指针或引用转换成父类是正确的);进行下行转换的时候,由于没有动态类型检查,所以是不安全的。继承必须为public
  2. 用于基本类型之间的转换,如intchar安全性也需要程序员来保证
  3. 把空指针转换为目标类型的空指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Person
{
public:
void print()
{
cout << "Person " << endl;
}
};

class Son : public Person
{
public:
void print()
{
cout << "Son " << endl;
}
};

void print1(Person *p)
{
p->print();
}

int main()
{
Son s;
print1(static_cast<Person *>(&s));
return 0;
}

const_cast

const_cast是c++中专用于处理与const相关的强制类型转换的关键字

其功能为:为一个变量重新设定其const描述.

即:const_cast可以为一个变量强行增加或删除其const限定.

需要明确的是,即使用户通过const_cast强行去除了const属性,也不代表当前变量从不可变变为了可变。const_cast只是使得用户接管了编译器对于const限定的管理权,故用户必须遵守“不修改变量”的承诺。如果违反此承诺,编译器也不会因此而引发编译时错误,但可能引发运行时错误。

  1. const_cast可用于更改const成员函数内的非const类成员。
  2. const_cast可用于将const数据传递给不接收const的函数。
  3. const_cast<>里边的内容必须是引用或者指针。
  4. const_cast也可以用来抛弃volatile__unaligned属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Student
{
private:
int roll;
public:
Student(int r) :roll(r) {}
void fun() const
{
(const_cast<Student*> (this))->roll = 5;
}
int getRoll() { return roll; }
};

int main() {
Student student(3);
std::cout << "Old roll number: " << student.getRoll() << std::endl;
student.fun();
std::cout << "New roll number: " << student.getRoll() << std::endl;

// const_cast只能调节类型限定符,不能更改基础类型
int a1 = 40;
//const int* b1 = &a1;
//char* c1 = const_cast <char*> (b1); // 编译程序时出错

const volatile int* d1 = &a1;
std::cout << "typeid of d1 " << typeid(d1).name() << '\n'; // int const volatile *
int* e1 = const_cast <int*> (d1);
std::cout << "typeid of e1 " << typeid(e1).name() << '\n'; // int *

return 0;
}

在const成员函数fun()中,编译器将“this”视为“ const student const this”,即“this”是指向常量对象的常量指针,因此编译器不允许通过以下方式更改数据成员“这个”指针。const_cast将“this”指针的类型更改为“student const this”

const_cast比简单类型转换更安全。从某种意义上讲,如果强制类型与原始对象不相同,则强制转换不会发生,这是比较安全的。

1
2
3
int a=20;
const int *p=&a;
char *c1=const_cast<char*> (p);//编译时程序出错

reinterpret_cast

reinterpret,即重新解释.

该强制类型转换的作用是提供某个变量在底层数据上的重新解释.

当我们对一个变量使用reinterpret_cast后,编译器将无视任何不合理行为,强行将被转换变量的内存数据重解释为某个新的类型。用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。 它不检查指针类型和指针所指向的数据是否相同。

需要注意的是,reinterpret_cast要求转换的两个数据所占用的内存大小一致,否则会引发编译时错误.

1
data_type *var_name = reinterpret_cast <data_type *>(pointer_variable);

使用 reinterpret_cast 的目的:

  1. reinterpret_cast是一种非常特殊且危险的类型转换操作符。并且建议使用适当的数据类型使用它,即(指针数据类型应与原始数据类型相同)。
  2. 它可以将任何指针类型转换为任何其他数据类型。
  3. 当我们要使用位时使用它。
  4. 它仅用于将任何指针转换为原始类型。
  5. 布尔值将转换为整数值,即0表示false,1表示true。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class A {
public:
int a;
A(int i) :a(i) {}
void fun_a()
{
std::cout << "In class A\n";
}
};

class B {
public:
int b;
B(int i) :b(i) {}
void fun_b()
{
std::cout << "In class B\n";
}
};

void testReinterpretCast() {
B *x = new B(5);
A* y = reinterpret_cast<A*>(x);
y->fun_a(); // In class A
std::cout << y->a << std::endl; // 5
}

dynamic_cast

dynamic用于在运行时实现向下类型转换。

1
dynamic_cast <type-id> (expression)

expression转换为type-id类型,type-id必须是类的指针,类的引用或者是void,

如果type-id是一个指针,那么expression也是一个指针,是引用的话同为引用

特点如下:

  1. 它是在运行是进行处理的,其余三个都是在编译时完成. 运行时进行类型检查
  2. 不能用于内置的基本数据类型之间的强制转换
  3. dynamic_cast 要求 <> 内所描述的目标类型必须为指针或引用。dynamic_cast 转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回 nullptr
  4. 在类的转换时,在类层次上进行向上转换(子类指针指向父类指针),与static_cast的效果是一样的。在进行父类指针向子类指针的转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
  5. 向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。在C++中,编译期的类型转换有可能会在运行时出现错误,特别是涉及到类对象的指针或引用操作时,更容易产生错误。dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。
  6. 使用 dynamic_cast 进行转换的,基类中一定要有虚函数,否则编译不通过(类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义)。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class AA {
public:
virtual void print() {
cout << "in class AA" << endl;
};
};

class BB :public AA {
public:
void print() {
cout << "in class BB" << endl;
};
};

void testDynamicCast() {
AA* a1 = new BB; // a1是A类型的指针指向一个B类型的对象
AA* a2 = new AA; // a2是A类型的指针指向一个A类型的对象

BB* b1, * b2, * b3, * b4;

b1 = dynamic_cast<BB*>(a1);// not null,向下转换成功,a1 之前指向的就是 B 类型的对象,所以可以转换成 B 类型的指针。
if (b1 == nullptr)
cout << "b1 is null" << endl;
else
cout << "b1 is not null" << endl;

b2 = dynamic_cast<BB*>(a2);// null,向下转换失败
if (b2 == nullptr)
cout << "b2 is null" << endl;
else
cout << "b2 is not null" << endl;

// 用 static_cast,Resharper C++ 会提示修改为 dynamic_cast
b3 = static_cast<BB*>(a1);// not null
if (b3 == nullptr)
cout << "b3 is null" << endl;
else
cout << "b3 is not null" << endl;

b4 = static_cast<BB*>(a2);// not null
if (b4 == nullptr)
cout << "b4 is null" << endl;
else
cout << "b4 is not null" << endl;

a1->print();// in class BB
a2->print();// in class AA

b1->print();// in class BB
//b2->print(); // null 引发异常
b3->print();// in class BB
b4->print();// in class AA
}

详细

Wednesday, 15 May 2024

So, recently, I shifted some of my workload to kde. And I started my KDE journey with KDE Neon. Even with a 2 year old base and packages, snaps and ppas saved me. Now, one of the important part of my work was to clone the repo using ssh. And here comes the problem.

In gnome, the gnome-keyring and the seahorse would automatically manage my ssh and gpg key passphrases. But, in KDE, that didn’t happen. On the starting I was getting frustrated with the ssh always asking me the passphrase. Then after a lot of reading manuals and internet searching, I found out about ksshaskpass. This can be used to write my passphrase and there I can set it to remember my passphrase, which it would essentially save in kwallet. So, How did I set this up?

Monday, 13 May 2024

A few weeks ago I attended the KDE Goals April 2024 sprint

I was there as part of the Automation & Systematization sprint given my involvement in the release process, the "not very automatized" weekly emails about the status of CI about KDE Gear and KDE Frameworks, etc. but I think that maybe I was there more as "person that has been around a long time, ask me if you have questions about things that are documented through oral tradition"

I didn't end up doing lots of work on sprint topics themselves (though I participated in various discussions, did a bit of pair-programming with Aleix on QML accessibility issues, inspired DavidR to do the QML-text-missing-i18n check that he describes in his blog); instead I cheated a bit and used the sprint to focus on some of the KDE stuff I had a bit on my backlog, creating the KDE Gear release/24.05 branches and lots of MR reviewing and more!

Group photo

Thanks KDE e.V. for sponsoring the trip, if you would like such events to continue please we need your continued donations

And remember Akademy talk submission period ends in 10 days, send your talk now!

A new revision of digiKam Recipes is available for your reading pleasure. The new version covers the auto tagging feature introduced in digiKam 8.3 and explains how to run digiKam in a container.

If you bought the book through Gumroad, you’ll find the new revision in the Library section. The book purchased through Google Play should be updated automatically to the latest version. If you have problems getting the latest revision of the book, contact the author at dmpop@cameracode.coffee

Saturday, 11 May 2024

In this rather lengthy post I talk a bit about the current issues with icons for the KDE applications I work on or use.

Let’s start with looking at what I mean with KDE applications and what the current state is, up to KDE Frameworks 6.2 and current KDE Gear 24.02. Then let’s see what will be improved in future releases.

What do I mean with ‘KDE Applications’

If I speak about ‘KDE Applications’ here I talk about applications like Kate, Dolphin, Okular and others like that.

This means applications developed with Qt and KDE Frameworks that integrate well with the KDE Plasma desktop but are not restricted to it.

Many of this applications not just aim to work well on Linux & BSD or other open source operating systems but are ported and working well on the rather different Windows and macOS desktop. Some even are successful since years in the official Windows Store.

The above applications are part of the KDE Gear releases, but the described issues and solutions naturally are not restricted to stuff released with that.

What most of these applications have in common is that they rely on rather large parts of our Frameworks. With that they depend at least indirectly on an icon set that covers large parts of what our default icon set Breeze provides. Even if you use no icons from that icon set yourself in your application, just using the standard actions or many widgets/dialogs from Frameworks will rely on some subset of Breeze.

Current State of Icons per Desktop or Platform

When talking about the current situation of icons that depends largely on the desktop or platform you are running the KDE application on.

Let’s take a look at some (I for sure miss some that are common or loved, that doesn’t mean I disregard them, I just want to limit the scope).

KDE Plasma on Linux/BSD with Wayland/X11

If you just aim to run on the KDE Plasma desktop with your Qt and KDE Frameworks based application, all is fine with icons, there is no problem.

The KDE project did their job, at least for Kate I never did have any issues with icons on Plasma.

Below a screenshot of Kate 24.02 running on Plasma 6. All icons are there, they are properly re-colored for the dark theme, too, including not just the used Breeze icons but for example the small Git icons in the left sidebar that Kate has bundled.

KDE Plasma on Linux/BSD with Wayland/X11

This is the vanilla state each user will get if Kate is installed on Plasma (and the dark theme is used). There are no patches done during building to achieve that nor is there any extra user configuration necessary.

Microsoft Windows

If you run Kate on Windows, the icon situation is good, too, if you use our Windows Store variant or get at build done via Craft.

See below what the current nightly of Kate looks like in some Windows 11 VM (I just started it from the unpacked ZIP, no setup needed).

Microsoft Windows

In the Craft build descriptions we do some patches to ensure the Breeze icons are bundled as library and the application links with that. In addition we ensure with some more patching that our own icon engine is used to allow for the proper recoloring.

If you don’t do that patching you will end up with close to no icons or for dark theme black on black icons.

Apple’s macOS

The situation on macOS is the same as on Windows.

If you go with a Craft build of Kate, you will end up with something like below.

Apple's macOS

All icons are there and even application provided icons like our Git one are properly recolored.

Without the Craft patches Kate has more or less no icons like on Windows.

Haiku

After covering Plasma and the two large closed-source desktop operating systems, as a small excursion, look how Kate (the KF5 based version) looks if installed on Haiku with the package they provide.

Haiku

Kate looks ok, system icons intermixed with Breeze as fallback icons.

GNOME

For testing this, I installed the latest Fedora Workstation in a VM. I have done no user configuration beside what the installer and initial setup wizard asked and then just installed the Kate package. The shell was even helpful to ask to do that after you just tried to start the not installed Kate.

Kate on GNOME

Most icons not there, not that nice. For details about that read this post, we don’t need to re-iterate this again.

If you think: that is just Kate, let us just try Okular.

Okular on GNOME

One thing that can be at least solved easily is that the icons are gone, we just install the Breeze icon set as package.

Kate and Okular with Breeze on GNOME

Looks ok, system icons intermixed with Breeze as fallback icons just like on Haiku. Not stylish but usable.

I was unable to trigger Kate or Okular to adjust to the dark mode GNOME provides, therefore I can not test if we end up with black on black icons there, but it is likely, as the fallback is just Breeze.

MATE

Kate and Dolphin 24.02 on MATE with dark mode on NixOS, normal system packages, Breeze icons is installed.

MATE

System icons intermixed with Breeze as fallback icons, looks not that nice. Breeze icons not readable, as recoloring is not working.

Xfce

Kate and Dolphin 24.02 on Xfce with dark mode on NixOS, normal system packages, Breeze icons is installed.

Xfce

Same mix and unreadable state as on MATE.

Enlightenment

Kate 24.02 on Enlightenment with dark mode on NixOS, normal system packages, Breeze icons is installed.

Enlightenment

Just unreadable icons, beside out own Git icon and the few colored ones.

Summary: What’s up with Icons today

The icons in KDE applications do look perfect on KDE Plasma. That should be no real surprise as many people working on these applications will test them there and KDE Frameworks and Qt are well tested on Plasma, too.

The icons look fine on Windows and macOS, too, at least for applications that got properly ported, but only thanks to patches we do in Craft. If you just grab e.g. Kate’s and the needed frameworks sources from our normal repositories, you don’t get that.

If the maintainers of the port for some OS do care, like the Haiku people, KDE applications can look fine there.

On other desktop environments it doesn’t look that great out of the box.

Unlike for the other operating systems, there the same packages without extra patches are running.

Whereas that works perfect on Plasma, we rely too much that the desktop environment running provides an icon set that has a similar coverage and naming as Breeze. As we don’t hard depend on the Breeze icons for our applications, it can even happen that just no fitting icons are there per default.

Even if that can be solved with some better package dependencies, you still end up with a patchwork look and without a Qt platform theme plugin that handles the needed recoloring to make dark mode feasible.

Getting it fixed

Fortunately, just because the status quo is not that nice, it must not stay that way.

We have more or less all needed parts to fix the situation, we did already fix it during the porting to Windows and macOS.

We just never pushed to get this stuff done on Linux and Co.

How did we solve it there?

  • We have the Breeze icon set as Qt resource inside a library and link with that. That makes them a hard build and runtime dependency and easy to deploy.
  • We ensure the icon engine we have in our KIconThemes framework is there and used.
  • We enforce the Breeze Qt style. (this is not really icon related, but ensures an usable look’n’feel, too)

The first and the last thing are easy to do on Linux and Co., too, even with still allowing the user to override the icon set and style, but still defaulting to Breeze.

The second point is harder, as that requires at the moment a few hacks and is not 100% as good as going the Qt platform theme plugin route we use inside Plasma.

For KDE Frameworks 6.3 we worked to get that done.

See our meta issue on our GitLab instance covering that topic.

All is not perfect, we will need to get some Qt API to fully do that, but the current state is already usable.

Here a comparison with the state as we have it now in our released software compared to with the state in the current master branch on an Cinnamon desktop.

Cinnamon

The left side is the current Kate 24.02, the right side the current master build of Kate with master Frameworks.

The hard dependency to the Breeze icon library is done in KIconThemes, if you link to that, you are guaranteed that you have Breeze icons. You can naturally just link to only the Breeze icon library on your own.

The ensuring that the proper icon engine is done with some new API in KIconThemes that application developers must opt-in for. The same for the Qt style setup, there we have API in KConfigWidgets.

For Kate the concrete changes can be found here. They are minimal and even remove some platform specific code for the style setup.

Including fallback code for pre 6.3 Frameworks compatibility of the style setting, the basic idea is:

#include <KIconTheme>

#define HAVE_STYLE_MANAGER __has_include(<KStyleManager>)
#if HAVE_STYLE_MANAGER
#include <KStyleManager>
#endif

int main(...)
{
    /**
     * trigger initialisation of proper icon theme
     */
#if KICONTHEMES_VERSION >= QT_VERSION_CHECK(6, 3, 0)
    KIconTheme::initTheme();
#endif

    QApplication yourAppInstance(...);

#if HAVE_STYLE_MANAGER
    /**
     * trigger initialisation of proper application style
     */
    KStyleManager::initStyle();
#else
    /**
     * For Windows and macOS: use Breeze if available
     * Of all tested styles that works the best for us
     */
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
    QApplication::setStyle(QStringLiteral("breeze"));
#endif
#endif

    ...
}

In the long run, once 6.3 is the minimal version the application depends on, this is just:

#include <KIconTheme>
#include <KStyleManager>

int main(...)
{
    /**
     * trigger initialisation of proper icon theme
     */
    KIconTheme::initTheme();

    QApplication yourAppInstance(...);

    /**
     * trigger initialisation of proper application style
     */
    KStyleManager::initStyle();

    ...
}

At the moment KIconTheme::initTheme() is still a bit hacky until we have proper Qt API, but that is not visible for the API user.

If we get this properly done in our applications, that will not just solve the current issue for running them in other desktop environments.

With that API in use and the now already upstreamed patches, one can build vanilla Frameworks and Kate on Windows and macOS and the icons will just work in the resulting application bundles and you get an usable style out of the box if Breeze is there.

Help Wanted!

We have now some API to help our applications to be more usable on non-Plasma installations and Windows and macOS.

We still need to make use of it and we need to improve the implementation and upstream to Qt the needed extra API to make it a real 100% replacement to what we do with the Plasma integration plugin.

If you have time to help us, show up on our meta issue.

Not just coding is needed, we for example have still a few icons that don’t recolor well, help to fix that is wanted, too.

Feedback

You can provide feedback on the matching KDE Social or reddit post.

Friday, 10 May 2024

Since the last two posts about this topic (part one, part two) there has been some more progress, so let’s take a look.

Brightness Control

In Plasma 6.0, when HDR is enabled, you get to choose the brightness of SDR content in the display settings:

HDR settings

This is however not convenient to change quickly and doesn’t apply to HDR content, which so far is always presented at 100% brightness. In Plasma 6.1, you’ll be able to use the same ways to control brightness as on laptops or with displays that support DDC/CI in SDR mode: Just drag the brightness slider, press keyboard shortcuts or scroll on the brightness icon in the system tray, and it’ll dim the whole screen like you’d expect.

brightness slider and the system tray

Powerdevil unfortunately only supports controlling the brightness of a single screen right now, and that limitation also applies here. The API it uses to communicate with KWin is per display though, so once powerdevil supports multiple screens, it’ll work for HDR displays too.

More accurate colors without a Colorimeter

Many new displays have a color gamut much wider than sRGB, and assume that their input signal is fitting for their color gamut - which means that, unless you use an ICC profile, colors will be much more saturated than they should be. There’s a few ways to correct this:

  • find an sRGB option in your monitor’s OSD. This is usually pretty accurate if it’s available, but also limits all apps to sRGB
  • buy a Colorimeter and profile your display. That costs money though, and the display profiling situation on Linux isn’t in great shape at the moment (DisplayCAL on Flathub is 4 years old, my last attempt at building it on Fedora didn’t work, and it only works correctly on Xorg atm)
  • find an ICC profile for your display on the Internet and use that, hoping the display doesn’t deviate too much from the one that was profiled

There is a fourth option though: Use the color information from the display’s EDID. In Plasma 6.1, you can simply select this in the display settings.

color profile settings

Note that it comes with some caveats too:

  • the EDID only describes colors with the default display settings, so if you change the “picture mode” or similar things in the display settings, the values may not be correct anymore
  • the manufacturer may not measure every panel and just put generic values for the display model into the EDID
  • the manufacturer may put completely wrong values in there (which is why this is disabled by default)
  • even when correct values are provided, ICC profiles have much more detailed information on the display’s behavior than the EDID can contain

So if you care about color accuracy, this is not a way out of getting a Colorimeter and profiling your display… but if you just have two screens and you’re annoyed that one of them has much more intense colors than the other, this option is an easy and fast way to fix it.

Gamescope

Joshua Ashton implemented a new backend in gamescope, that uses Wayland subsurfaces to forward content to the host compositor instead of compositing it all into one image, and includes direct support for the frog_color_management_v1 protocol. The result of this is that with a new enough gamescope you don’t have to use any Vulkan layers to have gamescope pass HDR content to KWin, and you don’t have to use the --hdr-debug-force-output option anymore. If you want to play a game in HDR, you can now just put

gamescope -W 5120 -H 1440 --hdr-enabled --fullscreen -- %command%

into its launch options in Steam (with width and height adjusted to your screen ofc) and you’re done.

Doom Eternal launch options

The backend also reduces the image copies made in comparison to the previously default SDL backend; the game’s buffers are directly passed to the host compositor, in most cases even while overlays are visible.

GPU Drivers

The driver situation is something I’ve been kind of ignoring in previous posts, but it’s obviously pretty important. First, let’s talk about the KMS API for setting a display into HDR mode. It consists of two parts:

  • the HDR_OUTPUT_METADATA property, which compositors use to set mostly brightness related metadata about the image, like which transfer function is used and which brightness and color values the content roughly contains
  • the Colorspace property, which compositors use to set the colorspace of the image, so that the display interprets the colors correctly

When you enable HDR in the system settings in Plasma, KWin will set HDR_OUTPUT_METADATA to the Perceptual Quantizer transfer function, and the brightness and mastering display properties to almost exactly what the display’s EDID says is optimal. This is done independently of the actual image content or what apps on the screen tell the compositor, to prevent the display from doing dumb things like dimming down just because an HDR video you opened claims it wants to display a supernova in your living room.

The one exception to that is the max_cll value - the maximum brightness of any pixel on the screen. It’s set to the maximum average brightness the display can show, because my Samsung C49RG94SSR monitor reduces the backlight brightness below SDR levels if you set the max_cll value it claims is ideal… With that one exception, this strategy has worked without issues so far.

On the Colorspace front, KWin sets the property to Default in SDR mode, and to BT2020_RGB when HDR is enabled. Sounds simple enough, but it’s of course actually more complicated.

Like any API that didn’t actually get used in practice for a very long time (if ever), both the API and the implementations were and are quite broken. The biggest issues I’ve seen so far are:

  • AMD’s implementation of the Colorspace property for DisplayPort was broken, which caused colors to be washed out in HDR mode (fixed in Linux 6.8)
  • the NVidia driver doesn’t force a modeset when HDR_OUTPUT_METADATA changes the transfer function or Colorspace changes its value, which causes temporary glitches when enabling HDR on some displays
  • the Intel driver claims to support both properties for HDR laptop displays, but the implementation is missing entirely (this is being worked on)
  • the Colorspace property implementations from Intel and NVidia cause washed out colors on many displays, because the API requires the compositor to change the property value depending on whether or not communication with the display uses RGB or YUV encoding… which the compositor doesn’t actually know anything about. The AMD implementation works around this by translating the property to the correct value in the kernel
  • multiple laptop- or display specific issues that you can look up in the bug trackers if you want to

To summarize, if you want to use HDR, it’s best to use an AMD GPU with either kernel 6.8, or if you really must use an older kernel, HDMI. Even then, you might still see issues in some cases though - if you do, please make bug reports about it! Neither driver nor compositor developers can fix what they don’t know is broken.

What’s next?

As always, for every bit of progress made or feature implemented, there’s ten more upcoming exciting things that could be talked about or worked on… but the big next topic is offloading of color management tasks to the GPU’s scanout hardware, to save power and improve performance. Next week I’ll be attending the 2024 Linux Display Next hackfest, which will focus on exactly that, so stay tuned!

In a previous post I talked about using the QML Language Server for KDE development. Since writing that post a few things happened, so it’s time for an update.

I mentioned that when using Kate qmlls should work out of the box when opening a QML file. That’s mostly true, there is one problem though. Depending on your distribution the binary for qmlls has a different name. Sometimes it’s qmlls, sometimes qmlls6 or qmlls-qt6. You may need to adjust the LSP Server settings in Kate to match the name on your system.

In order for qmlls to find types that are defined in your application’s C++ code those must not only be declaratively registered, qmlls also needs to be told where to find the type information. Fortunately Qt 6.7 comes with a handy way to do that. By passing -DQT_QML_GENERATE_QMLLS_INI=ON to CMake you get an appropriate config file generated. This will be placed into the project’s source directory but is specific to your setup, so add that to your gitignore file (PS: You can set up a global gitignore file for your system, so you don’t need to add this to all your projects). Unfortunately the initial implementation produced wrong configurations for some modules, but this is fixed in Qt 6.7.2.

A problem I mentioned is that qmlls doesn’t find modules that are not installed into the same path as Qt. With Qt 6.8 there will be two new options. The -I parameter allows to add custom import paths to qmlls’ search paths. The -E parameter makes qmlls consider the value of the QML_IMPORT_PATH environment variable for its search paths.

In order for qmlls to work properly modules need to be created using the CMake API and use declarative type registration. Since writing the last post some KDE modules have been converted to those, but there’s still more to do.

Thanks to the QML team for those swift improvements!

Lambda表达式

匿名函数是很多高级语言都支持的概念,如lisp语言在1958年首先采用匿名函数。匿名函数有函数体,但没有函数名。C++11中引入了lambda表达式。利用lambda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使代码更可读。但是从本质上来讲,lambda表达式只是一种语法糖,因为所有其能完成的工作都可以用其它稍微复杂的代码来实现。但是它简便的语法却给C++带来了深远的影响。如果从广义上说,lamdba表达式产生的是函数对象。

相同类似功能我们也可以使用函数对象或者函数指针实现:函数对象能维护状态,但语法开销大,而函数指针语法开销小,却没法保存范围内的状态。lambda表达式正是结合了两者的优点。

声明Lambda表达式

1
2
3
4
5
6
7
// 完整语法
[capture list] (params list) mutable(optional) constexpr(optional)(c++17) exception attribute -> return type { function body };

// 可选的简化语法
[capture list] (params list) -> return type {function body}; //1
[capture list] (params list) {function body};//2
[capture list] {function body};//3
  • capture list:捕获外部变量列表,不能省略;
  • params list:形参列表,可以省略(但是后面必须紧跟函数体);
  • mutable指示符: 可选,将lambda表达式标记为mutable后,函数体就可以修改传值方式捕获的变量;
  • constexpr:可选,C++17,可以指定lambda表达式是一个常量函数;
  • exception:异常设定, 可选,指定lambda表达式可以抛出的异常;
  • attribute:可选,指定lambda表达式的特性;
  • return type:返回类型
  • function body:函数体

标号1. 函数声明了一个const类型的表达式,此声明不可改变capture list中的捕获的值。

标号2. 函数省略了返回值,此时如果function body内含有return语句,则按return语句返回类型决定返回值类型,若无则返回值为void类型。

标号3. 函数无参数列表,意味无参函数。

1
2
3
vector<int> vec{1,0,9,5,3,3,7,8,2};

sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; });

捕获外部变量

lambda表达式最前面的方括号的意义何在?其实这是lambda表达式一个很Hong要的功能,就是闭包。这里我们先讲一下lambda表达式的大致原理:每当你定义一个lambda表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符),我们称为闭包类型(closure type)。那么在运行时,这个lambda表达式就会返回一个匿名的闭包实例,其实一个右值。所以,我们上面的lambda表达式的结果就是一个个闭包。闭包的一个强大之处是其可以通过传值或者引用的方式捕捉其封装作用域内的变量,前面的方括号就是用来定义捕捉模式以及变量,我们又将其称为lambda捕捉块。

Lambda表达式可以捕获外面变量,但需要我们提供一个谓词函数([capture list]在声明表达式最前)。类似参数传递方式:值传递、引入传递、指针传递。在Lambda表达式中,外部变量捕获方式也类似:值捕获、引用捕获、隐式捕获

值捕获

1
2
3
4
5
int a = 123;
auto f = [a] { cout << a << endl; };
f(); // 输出:123
a = 321;
f(); // 输出:123

值捕获和参数传递中的值传递类似,被捕获的值在Lambda表达式创建时通过值拷贝的方式传入,类中会相应添加对应类型的非静态数据成员。在运行时,会用复制的值初始化这些成员变量,从而生成闭包。因此Lambda表达式函数体中不能修改该外部变量的值; 因为函数调用运算符的重载方法是const属性的。同样,函数体外对于值的修改也不会改变被捕获的值。 想改动传值方式捕获的值,那么就要使用mutable

1
2
3
auto add_x = [x](int a) mutable { x *= 2; return a + x; };  // 复制捕捉x

cout << add_x(10) << endl; // 输出:30

因为一旦将lambda表达式标记为mutable,那么实现的函数调用运算符是非const属性的。

引用捕获

1
2
3
4
int a = 123;
auto f = [&a] { cout << a << endl; };
a = 321;
f(); // 输出:321

引用捕获的变量使用的实际上就是该引用所绑定的对象,因此引用对象的改变会改变函数体内对该对象的引用的值。 对于引用捕获方式,无论是否标记mutable,都可以在lambda表达式中修改捕获的值。

隐式捕获

隐式捕获有两种方式,分别是 [=]:以值补获的方式捕获外部所有变量 [&]:表示以引用捕获的方式捕获外部所有变量

1
2
3
int a = 123, b=321;
auto df = [=] { cout << a << b << endl; }; // 值捕获
auto rf = [&] { cout << a << b << endl; }; // 引用捕获

其他

捕获外部变量形式
[ ]不捕获任何变量(无参函数)
[变量1,&变量2, …]值(引用)形式捕获指定的多个外部变量
[this]值捕获this指针
[=, &x]变量x以引用形式捕获,其余变量以传值形式捕获
[*this]通过传值方式捕获当前对象
[&, x]默认以引用捕获所有变量,但是x是例外,通过值捕获

既然只使用一次,那直接写全代码不就行了,为啥要函数呢?——因为lambda可以捕获局部变量

在上面的捕获方式中,注意最好不要使用[=][&]默认捕获所有变量。首先说默认引用捕获所有变量,你有很大可能会出现悬挂引用(Dangling references),因为引用捕获不会延长引用的变量的声明周期:

1
2
3
4
std::function<int(int)> add_x(int x)
{
return [&](int a) {return x+a;};
}

因为参数x仅是一个临时变量,函数调用后就被销毁,但是返回的lambda表达式却引用了该变量,但调用这个表达式时,引用的是一个垃圾值,所以会产生没有意义的结果。如果通过传值的方式来解决上面的问题:

1
2
3
4
std::function<int(int)> add_x(int x)
{
return [=](int a) { return x + a; };
}

使用默认传值方式可以便面悬挂引用问题.但是采用默认值捕获所有变量仍然有风险。例如当在类中捕获私有变量,当返回值为lambda表达式时,无法捕获到私有变量,但当指定为[=]时,会捕获到this指针的副本,当类已经调用析构函数,使用该指针仍然不安全.

参数

  • 参数列表中不能有默认参数
  • 不支持可变参数
  • 所有参数必须要参数名(相当于不可以有占位参数)

返回类型

单一的return语句可以推断返回类型;多语句则默认返回void,否则报错,应指定返回类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 正确,单一return语句
transform(vi.begin(),vi.end(),vi.begin(),
[] (int i) {
return i<0? -i; i;
}
);
// 错误。不能推断返回类型
transform(vi.begin(),vi.end(),vi.begin(),
[] (int i) {
if (I<0) return -i;
else return i;
}
);
// 正确,尾置返回类型
transform(vi.begin(),vi.end(),vi.begin(),
[] (int i) ->int{
if (I<0)
return -i;
else
return i;
}
);

赋值

auto和function可接受lambda表达式的返回:

1
2
3
int x =8,y=9;
auto add = [](int a,int b){return a+b;};
std::function<int(int,int)> Add = [=] (int a,int b){return a+b};

lambda表达式产生的类不含有默认构造函数、赋值运算符、默认析构函数。至于闭包类中是否有对应成员,C++标准中给出的答案是:不清楚的,看来与具体实现有关。还有一点要注意:lambda表达式是不能被赋值的:

1
2
3
4
5
auto a = [] { cout << "A" << endl; };
auto b = [] { cout << "B" << endl; };

a = b; // 非法,lambda无法赋值
auto c = a; // 合法,生成一个副本

因为禁用了赋值操作符:

1
ClosureType& operator=(const ClosureType&) = delete;

但是没有禁用复制构造函数,所以可以用一个lambda表达式去初始化另外一个lambda表达式而产生副本。并且lambda表达式也可以赋值给相对应的函数指针,这也使得你完全可以把lambda表达式看成对应函数类型的指针

新特性

C++14中,lambda又得到了增强,一个是泛型lambda表达式,一个是lambda可以捕捉表达式。

lambda捕捉表达式

lambda表达式可以按复制或者引用捕获在其作用域范围内的变量。而有时候,我们希望捕捉不在其作用域范围内的变量,而且最重要的是我们希望捕捉右值。所以C++14中引入了表达式捕捉,其允许用任何类型的表达式初始化捕捉的变量:

1
2
3
4
5
6
7
8
// 利用表达式捕获,可以更灵活地处理作用域内的变量
int x = 4;
auto y = [&r = x, x = x + 1] { r += 2; return x * x; }();
// 此时 x 更新为6,y 为25

// 直接用字面值初始化变量
auto z = [str = "string"]{ return str; }();
// 此时z是const char* 类型,存储字符串 string

可以看到捕捉表达式扩大了lambda表达式的捕捉能力,有时候你可以用std::move初始化变量。这对不能复制只能移动的对象很重要,比如std::unique_ptr,因为其不支持复制操作,你无法以值方式捕捉到它。但是利用lambda捕捉表达式,可以通过移动来捕捉它:

1
2
3
4
auto myPi = std::make_unique<double>(3.1415);

auto circle_area = [pi = std::move(myPi)](double r) { return *pi * r * r; };
cout << circle_area(1.0) << endl; // 3.1415

原文章