Skip to content

Monday, 15 April 2024

We’re a few weeks after the KDE 6 Megarelease and while many people have it working well there were too many problems in KDE neon’s rollout.

We’ll be hosting two Open Door Chats on KDE meet tomorrow (Tue 16 April) where users can talk to the developers to talk about any problems you had.

https://meet.kde.org/b/jon-0yw-xqi-sk6 The access code will be posted on the KDE neon Telegram group and Matrix room or e-mail jr@jriddell.org for it

Chats at 09:00 UTC (10:00 BST, 11:00 CEST) and 19:00 UTC (20:00 BST, 21:00 CEST) Tue 16 April 2024

Sunday, 14 April 2024

Marknote uses QTextDocument for its WYSIWYG text editor. This is surpringly quite powerful and thanks to some code borrowed from KMail rich text editor, it wasn’t hard to implement huge part of the markdown specification.

But while QTextDocument is great, I hit quickly some limits. This is why I started fixing some of them and I already have some patches up for review in Qt.

Default table style

By default the style of the tables looks straight from the 90s, I submitted a patch to use something a bit nicer: https://codereview.qt-project.org/c/qt/qtbase/+/554132 (merged!)

Old style
Old style

New style
New style

This is easy to change in the app itself and this is already the case in Marknote and NeoChat table rendering, but by default I believe Qt should provides a nice style so that apps don’t need to figure out how to overwrite the default style.

While working on this, I also noticed that the border-collapse property was not supported by the layout engine in QtQuick and only in QtWidget. This resulted in the border of the tables to be twice as thick as they should be. This was fortunately fixed by a simple patch: https://codereview.qt-project.org/c/qt/qtdeclarative/+/554151 (merged!)

Responsive images

A width and an height can be assigned to an image in a QTextDocument by using the respectives html attributes <img height="500" width="500" \>.This works correctly when the window size is known and static but less so then the window can be resized as will be the user. A common technique in web design is to add the following styles to the website:

img {
 max-width: 100%;
}

And while QTextDocument support some CSS properties, max-width wasn’t implemented. Adding the support for more properties in the HTML and CSS parser is quite simple as it was just forwarding the value of the max-width attribute to the QTextImageHandler, and a bit complicated was the handling of the % unit as previously only px and em was supported by Qt.

This resulted in two more patches, one for QtTextDocument and QtWidget and one for QtQuick.

Future

Hopefully these patches will be reviewed and merged soon. Afterward I want to add support for a few more CSS properties like: border-radius for images and some other elements, as well the border-{width, style, color} properties for paragraphs to better support <blockquote /> (important when displaying emails).

Unix like systems with X11 or Wayland

All Unix like systems with either X11 or Wayland are well supported since ever.

Linux with X11 and now Wayland is for a long time the primary system on that Kate work happens.

Over the years it was, like most of the KDE applications, ported to various BSD variants.

Be it some mainstream Linux distribution like Fedora or a niche one like NixOS, Kate is available as binary package. You love BSD? From FreeBSD to OpenBSD, you can get a Kate package via your normal package system.

And in the normal case, you can just build it from source on your own, all needed patches should be in our repositories upstream. If that is not the case for your system, please help to upstream them.

Below the current state of the master branch compiled on NixOS unstable with Wayland.

Unix like systems with X11 or Wayland

How to compile Kate on your own on a Unix like system and start to help to develop it can be found out here.

Windows

Since several years there are activities in the KDE community to provide our libraries and applications for Windows.

Even if that is a non-free platform, we can reach out to new users and developers that might later be then even interested to switch a full open platform.

Progress is slow, but steady. We have Kate and some other applications in the official Windows Store and nightly build for more of them. With reasonable effort you can develop Kate on Windows with Craft.

Below the current state of the master branch running on Windows 11 inside VirtualBox.

Windows

If you like to try that, use the nighly installer linked on the Kate website.

macOS

Beside Windows, the major other non-free platform Kate tries to support is macOS.

We have nighly build available for that and you can, like on Windows, develop Kate with the help of Craft.

Below the current state of the master branch running native on my M2 ARM Mac Mini.

macOS

Same as for Windows, if you like to try that, use the nighly installer for either ARM or Intel Macs linked on the Kate website.

Other Platforms

Naturally there are more than the above mentioned operating systems around.

Beside the mobile ones like Android and iOS that are not that interesting for Kate, many other desktop operating systems exist.

Even if the Kate team itself doesn’t put active work it them, that doesn’t mean Kate can’t run there.

Without any active work on our side, for example a Kate port for Haiku was done. Some one-liner patches for that got even upstreamed.

If you work on some port of our stuff and need to upstream stuff, please contact us. Even if you work on a non-mainstream system, as long as the patches are not too intrusive, we are interested to have them.

Help us!

Naturally the most of our developers are working on the Linux or some BSD.

That means the other systems are always in need of more people to help out, both on the programming and testing side.

For Kate, testing should be easy, grab a nighly build for Windows or macOS on the Kate website. Or even better, get Craft running, that will make it easier to contribute, too.

One recent topic that needs love is the removal of DBus for Windows/macOS/Android and other systems that don’t use it normally.

If you are up to help with that, here that is coordinated. The current state is already sufficient that the nightly builds of Kate no longer hang on e.g. macOS, but still some frameworks like KIO will need more work.

Just don’t get that wrong, DBus is great on the Linux or BSD systems that use it natively, but it is a pain on systems that have no notion of DBus and leads there to hangs or the spawning of unwanted processes. Beside that, the usefulness is low there, as there are no services on the bus to communicate with anyways.

Feedback

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

Saturday, 13 April 2024

Hindi Translation of Cantor and KDE connect - Season of KDE 2024 This is the second and last blog for SoK 2024. During the second half of Season of KDE, I translated Cantor and KDE connect in hindi. Cantor and KDE connect had about 1000 and 500 lines respectively. In order to translate these softwares, I took reference from google translate and AI models to improve on my translation. Translation memory did a great job in finding duplicates and helped me to avoid translating the same words again.

Friday, 12 April 2024

tldr; save the date for our QtCS24 Sep 5-6 in Würzburg.
Stay tuned for more information.

This is my final blog about my experience participating in season of KDE 2024. As part of my final term, I translated Tellico to hindi. Tellico contains 2070 statements with messages containing 1340 and docmessage containing 730 statements. I used Lokalize application for the translation. Here are some screenshots: Lokalize also has a glossary but its very limited for Hindi language. Commission for Scientific and Technical Terminology (https://cstt.education.gov.in/glossary-hindi-engineering-and-technology) and Fuel provides glossaries which can be used to expand Lokalize.

Wednesday, 10 April 2024

So this person, let's call them Jo, was hungry and had no money. Walking the streets, they come across a square where a group of people are working in a communal open kitchen, serving delicious hot meals free of charge.

So, 2023 was a successful year for Qt - and we highlight the Qt 6.5 and Qt 6.6 releases, Qt World Summit, and Qt Contributor Summit.

Our community stays vibrant and engaged with the Qt Project through our forums, mailings lists, asking and answering questions, offering technical advice, reporting bugs, contributing patches, and in many other forms helping Qt flourish.

Tuesday, 9 April 2024

C++11线程特性

std::thread

标准库

C++11引入了std::thread来创建线程,支持对线程join或者detach.

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
#include <iostream>
#include <thread>

using namespace std;

int main() {
auto func = []() {
for (int i = 0; i < 10; ++i) {
cout << i << " ";
}
cout << endl;
};
std::thread t(func);
if (t.joinable()) {
t.detach();
}
auto func1 = [](int k) {
for (int i = 0; i < k; ++i) {
cout << i << " ";
}
cout << endl;
};
std::thread tt(func1, 20);
if (tt.joinable()) { // 检查线程可否被join
tt.join();
}
return 0;
}
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
class ThreadGuard
{
public:
enum class DeAction{join,detach};
ThreadGuard(std::thread&& t,DeAction action): m_t(move(t)) , m_action(action){};
~ThreadGuard()
{
if(m_t.joinable())
{
if(m_action==DeAction::join)
{
m_t.join();
}
else
{
m_t.detach();
}
}
}
ThreadGuard(ThreadGuard&&)=default;
ThreadGuard& operator=(ThreadGuard&&)=default;
private:
std::thread m_t;
DeAction m_action;
};

c++11还提供了获取线程id,或者系统cpu个数,获取thread native_handle,使得线程休眠等功能

1
2
3
4
5
std::thread t(func);
cout << "当前线程ID " << t.get_id() << endl;
cout << "当前cpu个数 " << std::thread::hardware_concurrency() << endl;
auto handle = t.native_handle();// handle可用于pthread相关操作
std::this_thread::sleep_for(std::chrono::seconds(1));

std::mutex

std::mutex是一种线程同步的手段,用于保存多线程同时操作的共享数据。

文库

mutex分为四种:

  • std::mutex:独占的互斥量,不能递归使用,不带超时功能

  • std::recursive_mutex:递归互斥量,可重入,不带超时功能

  • std::timed_mutex:带超时的互斥量,不能递归

  • std::recursive_timed_mutex:带超时的互斥量,可以递归使用

    std::mutex

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
#include <iostream>
#include <mutex>
#include <thread>

using namespace std;
std::mutex mutex_;

int main() {
auto func1 = [](int k) {
mutex_.lock();
for (int i = 0; i < k; ++i) {
cout << i << " ";
}
cout << endl;
mutex_.unlock();
};
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(func1, 200);
}
for (auto& th : threads) {
th.join();
}
return 0;
}

std::timed_mutex

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
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>

using namespace std;
std::timed_mutex timed_mutex_;

int main() {
auto func1 = [](int k) {
timed_mutex_.try_lock_for(std::chrono::milliseconds(200));
for (int i = 0; i < k; ++i) {
cout << i << " ";
}
cout << endl;
timed_mutex_.unlock();
};
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(func1, 200);
}
for (auto& th : threads) {
th.join();
}
return 0;
}

std::lock相关

这里主要介绍两种RAII方式的锁封装,可以动态的释放锁资源,防止线程由于编码失误导致一直持有锁。

c++11主要有std::lock_guard和std::unique_lock两种方式,使用方式都类似,如下:

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
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>

using namespace std;
std::mutex mutex_;

int main() {
auto func1 = [](int k) {
// std::lock_guard<std::mutex> lock(mutex_);
std::unique_lock<std::mutex> lock(mutex_);
for (int i = 0; i < k; ++i) {
cout << i << " ";
}
cout << endl;
};
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(func1, 200);
}
for (auto& th : threads) {
th.join();
}
return 0;
}

std::lock_guard

1
2
3
4
#include <mutex>

template <class Mutex>
class lock_guard;

Mutex 是互斥量的类型。

使用 std::lock_guard 时,只需在需要保护的代码块中创建一个 std::lock_guard 对象,并将需要保护的互斥量传递给它的构造函数。当 std::lock_guard 对象创建时,会自动锁定互斥量,当对象销毁时,会自动解锁互斥量。

std::unique_lock

1
2
3
4
#include <mutex>

template <class Mutex>
class unique_lock;

Mutex 是互斥量的类型。

std::lock_guard 不同,std::unique_lock 对象可以在构造时不锁定互斥量,并且可以在后续的代码中手动锁定或解锁。此外,std::unique_lock 还提供了更多的功能,如可延迟锁定、条件变量的支持等。

std::atomic

c++11提供了原子类型std::atomic,理论上这个T可以是任意类型,但是我平时只存放整形,别的还真的没用过,整形有这种原子变量已经足够方便,就不需要使用std::mutex来保护该变量啦。看一个计数器的代码:

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
struct OriginCounter { // 普通的计数器
int count;
std::mutex mutex_;
void add() {
std::lock_guard<std::mutex> lock(mutex_);
++count;
}

void sub() {
std::lock_guard<std::mutex> lock(mutex_);
--count;
}

int get() {
std::lock_guard<std::mutex> lock(mutex_);
return count;
}
};

struct NewCounter { // 使用原子变量的计数器
std::atomic<int> count;
void add() {
++count;
// count.store(++count);这种方式也可以
}

void sub() {
--count;
// count.store(--count);
}

int get() {
return count.load();
}
};

std::call_once

c++11提供了std::call_once来保证某一函数在多线程环境中只调用一次,它需要配合std::once_flag使用,直接看使用代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
std::once_flag onceflag;

void CallOnce() {
std::call_once(onceflag, []() {
cout << "call once" << endl;
});
}

int main() {
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(CallOnce);
}
for (auto& th : threads) {
th.join();
}
return 0;
}

volatile

volatile通常用来建立内存屏障,volatile修饰的变量,编译器对访问该变量的代码通常不再进行优化,看下面代码:

1
2
3
int *p = xxx;
int a = *p;
int b = *p;

a和b都等于p指向的值,一般编译器会对此做优化,把*p的放入寄存器,之后a,b都等于寄存器的值,但是如果把中间p地址的值改变,内存值改变了,但a,b还是从寄存器中取的值(不一定,看编译器的优化结果),这不符合需求,所以对p加上volatile修饰可以避免此类优化.

注意:volatile不能解决多线程安全问题,针对特种内存才需要使用volatile,它和atomic的特点如下:

  • std::atomic用于多线程访问的数据,且不用互斥量,用于并发编程中
  • volatile用于读写操作不可以被优化掉的内存,用于特种内存中

std::condition_variable

条件变量是c++11引入的一种同步机制,它可以阻塞一个线程或者个线程,直到有线程通知或者超时才会唤醒正在阻塞的线程,条件变量需要和锁配合使用,这里的锁就是上面介绍的std::unique_lock。

成员函数

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
class CountDownLatch {
public:
explicit CountDownLatch(uint32_t count) : count_(count);

void CountDown() {
std::unique_lock<std::mutex> lock(mutex_);
--count_;
if (count_ == 0) {
cv_.notify_all();
}
}

void Await(uint32_t time_ms = 0) {
std::unique_lock<std::mutex> lock(mutex_);
while (count_ > 0) {
if (time_ms > 0) {
cv_.wait_for(lock, std::chrono::milliseconds(time_ms));
} else {
cv_.wait(lock);
}
}
}

uint32_t GetCount() const {
std::unique_lock<std::mutex> lock(mutex_);
return count_;
}

private:
std::condition_variable cv_;
mutable std::mutex mutex_;
uint32_t count_ = 0;
};
  • notify_one

    • 通知一个线程等待
  • notify_all

    • 通知所有线程等待
  • wait

    • 阻塞当前线程,直到条件变量被唤醒
  • wait_for

    • 阻塞当前线程,知道条件变量被唤醒或指定的超时时间后
  • wait_until

    • 阻塞当前线程,直到条件变量被唤醒或到达指定时间点

std::future

c++11关于异步操作提供了future相关的类,主要有std::future、std::promise和std::packaged_task,std::future比std::thread高级些,std::future作为异步结果的传输通道,通过get()可以很方便的获取线程函数的返回值,std::promise用来包装一个值,将数据和future绑定起来,而std::packaged_task则用来包装一个调用对象,将函数和future绑定起来,方便异步调用。而std::future是不可以复制的,如果需要复制放到容器中可以使用std::shared_future。

std::promise与std::future配合使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <functional>
#include <future>
#include <iostream>
#include <thread>

using namespace std;

void func(std::future<int>& fut) {
int x = fut.get();
cout << "value: " << x << endl;
}

int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t(func, std::ref(fut));
prom.set_value(144);
t.join();
return 0;
}

std::packaged_task与std::future配合使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <functional>
#include <future>
#include <iostream>
#include <thread>

using namespace std;

int func(int in) {
return in + 1;
}

int main() {
std::packaged_task<int(int)> task(func);
std::future<int> fut = task.get_future();
std::thread(std::move(task), 5).detach();
cout << "result " << fut.get() << endl;
return 0;
}

std::future用于访问异步操作的结果,而std::promise和std::packaged_task在future高一层,它们内部都有一个future,promise包装的是一个值,packaged_task包装的是一个函数,当需要获取线程中的某个值,可以使用std::promise,当需要获取线程函数返回值,可以使用std::packaged_task。

async

async是比future,packaged_task,promise更高级的东西,它是基于任务的异步操作,通过async可以直接创建异步的任务,返回的结果会保存在future中,不需要像packaged_task和promise那么麻烦,关于线程操作应该优先使用async,看一段使用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <functional>
#include <future>
#include <iostream>
#include <thread>

using namespace std;

int func(int in) { return in + 1; }

int main() {
auto res = std::async(func, 5);
// res.wait();
cout << res.get() << endl; // 阻塞直到函数返回
return 0;
}

async具体语法如下:

1
async(std::launch::async | std::launch::deferred, func, args...);

第一个参数是创建策略:

  • std::launch::async表示任务执行在另一线程
  • std::launch::deferred表示延迟执行任务,调用get或者wait时才会执行,不会创建线程,惰性执行在当前线程。

如果不明确指定创建策略,以上两个都不是async的默认策略,而是未定义,它是一个基于任务的程序设计,内部有一个调度器(线程池),会根据实际情况决定采用哪种策略。

若从 std::async 获得的 std::future 未被移动或绑定到引用,则在完整表达式结尾, std::future的析构函数将阻塞直至异步计算完成,实际上相当于同步操作:

1
2
std::async(std::launch::async, []{ f(); }); // 临时量的析构函数等待 f()
std::async(std::launch::async, []{ g(); }); // f() 完成前不开始

注意:关于async启动策略这里网上和各种书籍介绍的五花八门,这里会以cppreference为主。

有时候我们如果想真正执行异步操作可以对async进行封装,强制使用std::launch::async策略来调用async。

1
2
3
4
template <typename F, typename... Args>
inline auto ReallyAsync(F&& f, Args&&... params) {
return std::async(std::launch::async, std::forward<F>(f), std::forward<Args>(params)...);
}

原文章

补充

Sunday, 7 April 2024