Skip to content

Friday, 29 September 2023

A few days ago Volker Krause posted this blog about the Nextcloud conference - a very interesting read.

One of the topics is the VFS (Virtual Filesystem-) API for the Linux desktop. Indeed that is a topic for us at ownCloud as well, and I like to share our perspective on it, discussing it in the scope of the free desktop.

The topic is very important, as “syncing” of data from and to cloud storages has changed over time. From having all files mirrored from client to server and vice versa, it has now shifted to keep all files in the cloud, and have them as so called placeholders on the desktop. That means that most files on the client appear with size zero to save space, but the complete filesystem structure is available.

If a user starts to interact with such a dehydrated file, the content is of the file is downloaded transparently utilizing the cloud system client, for example ownClouds desktop client. The same happens when an application accesses such a file. As a result, the placeholders look and behave like the normal filesystem we are used to.

On Windows and on MacOSX, the problem is kind of solved. Both have added APIs to their OS that can be used to implement the access of data on the cloud.

On Linux, we do not have this kind of API yet. That means that it is close to impossible to implement this user experience. Volker already said that desktop environment specific solutions probably do not scale, which I agree with.

At ownCloud we have looked into the implementation of a specific FUSE file system. That should certainly be possible, and is probably a part of the solution, but is considerable effort because of the asynchronous nature of the topic. Given that the market share of Linux desktop systems is pretty small it is not attractive for companies to invest a lot into a Linux only system. Here the power of community could make a difference again.

It would be best if we as open source community would come up with a shared solution as a free desktop standard, that might be oriented on one of the existing APIs, maybe the MacOSX File Provider API: A library and little framework that the linux desktop environments can work with abstracting the VFS.

While collaborating on that, all data clouds could implement the bindings to their storage. With that, the extra implementation efforts for the Linux solution hopefully wouldnt be dramatic any more.

Let’s call this system openVFS as a work title. How can we evolve it? I’d like to invite all interested parties to discuss in this temporary Github repo to collect ideas and opinions. There is also a little experimental code.

Thursday, 28 September 2023

for what seems an eternity i've been running linux as my daily driver and evangilising to anyone that would listen.  however working as a carpenter and having a young family didn't leave much energy, let alone time to contribute.  but over the course of the last year slowly my contributions have been increasing.  as we age our bodies aren't capable of the physical things that you used to be easy and this combined with some other events have allowed me to able fulfil a long held ambition and contribute in a meaningful way to KDE and neon.  anyway enough self-reflection from this antopidean.

thanks to the patience of @jriddell, @sitter and @sgmoore i'm very happy to be helping get neon out the door, so to speak.  after the big plasma 6 push that started in March, i've been improving my ruby skills  trying to get neon's tooling into the best possible shape.  as qt6 started to filter down from unstable, it became apparent that compilation time saving hack of building qt5 and kde frameworks5 once for unstable and copying into the stable and user archives wasn't scaling any longer and couldn't be applied to qt6 and kf6.  after changing unstable to track the latest qt6.6 beta's at the request of the plasma devs, stable and user started building their very own qt5 and kf5.  besides a few late nights and much swearing at versioning problems it all went swimmingly. \0/

lately my energies have been split between trying to get more apps with a decicated kf6 branch into the experimental overlay and trying to keep up with the rapid rate of apps in unstable whose master branch has gone qt6/kf6 only.  as sgmoore pointed out porting pim6 alone has been quite the process.  with a raft of new red/failed job build's in unstable and qt6.6 beta4 just released, it looks like the pace isn't going to slow down for the forseeable future.  anyhow i'll sign off with the obligatory screenshot. cheery bye ;]

kate6_on plasma6 on neon_unstable.png, Sep 2023
kate6_on plasma6 on neon_unstable.
kate6_on plasma6 on neon_unstable.

 

 

Wednesday, 27 September 2023

Recently, I’ve stumbled across some behavior of C++ lambda captures that has, initially, made absolutely no sense to me. Apparently, I wasn’t alone with this, because it has resulted in a memory leak in QtFuture::whenAll() and QtFuture::whenAny() (now fixed; more on that further down).

I find the corner cases of C++ quite interesting, so I wanted to share this. Luckily, we can discuss this without getting knee-deep into the internals of QtFuture. So, without further ado:

Time for an example

Consider this (godbolt):

#include <iostream>
#include <functional>
#include <memory>
#include <cassert>
#include <vector>

struct Job
{
    template<class T>
    Job(T &&func) : func(std::forward<T>(func)) {}

    void run() { func(); hasRun = true; }

    std::function<void()> func;
    bool hasRun = false;
};

std::vector<Job> jobs;

template<class T>
void enqueueJob(T &&func)
{
    jobs.emplace_back([func=std::forward<T>(func)]() mutable {
        std::cout << "Starting job..." << std::endl;
        // Move func to ensure that it is destroyed after running
        auto fn = std::move(func);
        fn();
        std::cout << "Job finished." << std::endl;
    });
}

int main()
{
    struct Data {};
    std::weak_ptr<Data> observer;
    {
        auto context = std::make_shared<Data>();
        observer = context;
        enqueueJob([context] {
            std::cout << "Running..." << std::endl;
        });
    }
    for (auto &job : jobs) {
        job.run();
    }
    assert((observer.use_count() == 0) 
                && "There's still shared data left!");
}

Output:

Starting job...
Running...
Job finished.

The code is fairly straight forward. There’s a list of jobs to which we can be append with enqueueJob(). enqueueJob() wraps the passed callable with some debug output and ensures that it is destroyed after calling it. The Job objects themselves are kept around a little longer; we can imagine doing something with them, even though the jobs have already been run.
In main(), we enqueue a job that captures some shared state Data, run all jobs, and finally assert that the shared Data has been destroyed. So far, so good.

Now you might have some issues with the code. Apart from the structure, which, arguably, is a little forced, you might think “context is never modified, so it should be const!”. And you’re right, that would be better. So let’s change it (godbolt):

--- old
+++ new
@@ -34,7 +34,7 @@
     struct Data {};
     std::weak_ptr<Data> observer;
     {
-        auto context = std::make_shared<Data>();
+        const auto context = std::make_shared<Data>();
         observer = context;
         enqueueJob([context] {
             std::cout << "Running..." << std::endl;

Looks like a trivial change, right? But when we run it, the assertion fails now!

int main(): Assertion `(observer.use_count() == 0) && "There's still shared data left!"' failed.

How can this be? We’ve just declared a variable const that isn’t even used once! This does not seem to make any sense.
But it gets better: we can fix this by adding what looks like a no-op (godbolt):

--- old
+++ new
@@ -34,9 +34,9 @@
     struct Data {};
     std::weak_ptr<Data> observer;
     {
-        auto context = std::make_shared<Data>();
+        const auto context = std::make_shared<Data>();
         observer = context;
-        enqueueJob([context] {
+        enqueueJob([context=context] {
             std::cout << "Running..." << std::endl;
         });
     }

Wait, what? We just have to tell the compiler that we really want to capture context by the name context – and then it will correctly destroy the shared data? Would this be an application for the really keyword? Whatever it is, it works; you can check it on godbolt yourself.

When I first stumbled across this behavior, I just couldn’t wrap my head around it. I was about to think “compiler bug”, as unlikely as that may be. But GCC and Clang both behave like this, so it’s pretty much guaranteed not to be a compiler bug.

So, after combing through the interwebs, I’ve found this StackOverflow answer that gives the right hint: [context] is not the same as [context=context]! The latter drops cv qualifiers while the former does not! Quoting cppreference.com:

Those data members that correspond to captures without initializers are direct-initialized when the lambda-expression is evaluated. Those that correspond to captures with initializers are initialized as the initializer requires (could be copy- or direct-initialization). If an array is captured, array elements are direct-initialized in increasing index order. The order in which the data members are initialized is the order in which they are declared (which is unspecified).

https://en.cppreference.com/w/cpp/language/lambda

So [context] will direct-initialize the corresponding data member, whereas [context=context] (in this case) does copy-initialization! In terms of code this means:

  • [context] is equivalent to decltype(context) captured_context{context};, i.e. const std::shared_ptr<Data> captured_context{context};
  • [context=context] is equivalent to auto capture_context = context;, i.e. std::shared_ptr<Data> captured_context = context;

Good, so writing [context=context] actually drops the const qualifier on the captured variable! Thus, for the lambda, it is equivalent to not having written it in the first place and using direct-initialization.

But why does this even matter? Why do we leak references to the shared_ptr<Data> if the captured variable is const? We only ever std::move() or std::forward() the lambda, right up to the place where we invoke it. After that, it goes out of scope, and all captures should be destroyed as well. Right?

Nearly. Let’s think about the compiler generates for us when we write a lambda. For the direct-initialization capture (i.e. [context]() {}), the compiler roughly generates something like this:

struct lambda
{
    const std::shared_ptr<Data> context;
    // ...
};

This is what we want to to std::move() around. But it contains a const data member, and that cannot be moved from (it’s const after all)! So even with std::move(), there’s still a part of the lambda that lingers, keeping a reference to context. In the example above, the lingering part is in func, the capture of the wrapper lambda created in enqueueJob(). We move from func to ensure that all captures are destroyed when the it goes out of scope. But for the const std::shared_ptr<Data> context, which is hidden inside func, this does not work. It keeps holding the reference. The wrapper lambda itself would have to be destroyed for the reference count to drop to zero.
However, we keep the already-finished jobs around, so this never happens. The assertion fails.

How does this matter for Qt?

QtFuture::whenAll() and whenAny() create a shared_ptr to a Context struct and capture that in two lambdas used as continuations on a QFuture. Upon completion, the Context stores a reference to the QFuture. Similar to what we have seen above, continuations attached to QFuture are also wrapped by another lambda before being stored. When invoked, the “inner” lambda is supposed to be destroyed, while the outer (wrapper) one is kept alive.

In contrast to our example, the QFuture situation had created an actual memory leak, though (QTBUG-116731): The “inner” continuation references the Context, which references the QFuture, which again references the continuation lambda, referencing the Context. The “inner” continuation could not be std::move()d and destroyed after invocation, because the std::shared_ptr data member was const. This had created a reference cycle, leaking memory. I’ve also cooked this more complex case down to a small example (godbolt).

The patch for all of this is very small. As in the example, it simply consists of making the capture [context=context]. It’s included in the upcoming Qt 6.6.0.

Bottom line

I seriously didn’t expect there to be these differences in initialization of by-value lambda captures. Why doesn’t [context] alone also do direct- or copy-initialization, i.e. be exactly the same as [context=context]? That would be the sane thing to do, I think. I guess there is some reasoning for this; but I couldn’t find it (yet). It probably also doesn’t make a difference in the vast majority of cases.

In any case, I liked hunting this one down and getting to know another one of those dark corners of the C++ spec. So it’s not all bad 😉.

I might be busy early next month, so I’m posting this a few days early so I get it out of the way! I managed to do a lot of big stuff this month, and pretty happy with my pace. I still have way too many open MRs though, I really need to get that sorted.

Sorry about the shoddiness of some of the screenshots. We are the midst of our Qt6 transition, and sometimes my Breeze is broken and fell back to a built-in Qt theme. I promise it won’t look that ugly in a couple of months!

Plasma

I redid the Accessibility KCM to make it look a bit nicer, by using the newer sidebar view we use in other KCMs. This still needs some time in the oven, though.

The “new” Accessibility KCM!

The kaccess daemon now reloads it’s config files properly, causing odd behavior like the screen reader never turning off.

Tokodon

The Send button in the composer now changes depending on why you opened it.. This is an easy way to confirm you’re resending, editing and so on.

Screenshot of the new button behavior when editing a post.

I implemented a lot of UX improvements for the profile page. It’s way harder to mess up the timeline state by clicking through tabs too quickly. Oh yeah, and that’s fixed for the notification page filters too.

The settings are overhauled and using the new CategorizedSettings component which will make it easier to add more. This has already made space for granular per-account notification controls!

The new settings page. Better notification controls!

High character count posters rejoice, as the status composer is now usable for you! This will also appear in 23.08, so you don’t have to wait until the next major release.

The status composer now scrolls if it needs to.

The alignment of the top row of buttons in posts is ever so slightly fixed now so it looks prettier, and has better clickable areas.

BeforeAfter
imageimage

I ported the whole application to Qt6 declarative type registration, and other niceties. This doesn’t mean anything for users, but Tokodon should be a bit faster.

If you were ever frustrated with logging into Tokodon, fear not as in the next release the entire process is redone. I rewrote the entire UX to be way more user friendly, less buggy and it supports some cool features like a integrated authorization flow and registration!

The new registration page. You can’t view the server rules yet, but that will be added soon!

Tokodon will now show you a visible warning and explain why your account did not log in, instead of kicking you back to the login page like before:

An example of a login error. It’s even actionable!

Finally, a few media attachment improvements such as media attachments being blacked out if the blurhash is missing (which happens, apparently) and an “Alt” tag that shows up in the top right if the image has alt text. Saves me a click or two, and especially useful for video.

Showcase of the new chips.

NeoChat

I’m attempting to fix the lack of formatting when re-editing messages. It won’t be finished this month, but I’m close!

Two event source dialog changes, including it not showing any data and only showing the option if you have developer tools enabled.

The error message when your device encryption keys are somehow different than what’s currently in your database is now clearer, and tells you to log back in.

PlasmaTube

The sidebar is reorganized so more pages are separated as you might expect. There’s still some work to be done here.

There are more pages in the sidebar now, instead of being packed into one.

Added support for passing a video URL via the commandline.

Made sure PlasmaTube inhbits sleep like other well-behaving video applications when fullscreen.

Kirigami

Finally merged the Navigation Tab Bar page for Kirigami Gallery! It’s a simple example of this component that we use quite often on mobile.

The new section in Kirigami Gallery.

I changed the fullscreen image viewer used in NeoChat, Tokodon and more to stop listening to swipe events with the mouse and stop wrapping key navigation. For application developers, make sure you set the focus properly when opening it so key navigation works.

I fixed the FormArrow bug in Qt6 where it would point the wrong direction and thanks to Ivan Tkachenko for pointing out that we could use the existing enum in Qt. All consumers of this component have already been ported.

The CategorizedSettings component got some fixes as well, including the ability to filter out invisible actions (useful for hiding certain pages on other platforms, e.g. Android.) and fixing the stuck checked state. There’s still a lot of work to do on this component, but it’s a start!

KCoreAddons

I added a QML singleton for grabbing the applications’ KAboutData instead of it being reimplemented for every single QtQuick application. I have no idea why we didn’t do this before!

import QtQuick
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.coreaddons

FormCard.AboutPage {
    aboutData: AboutData
}

Qt

We are trying to adopt qmlformat in KDE. I spend a unreasonable amount of time fixing formatting, so it would be nice to have it automatically happen like we already use clang-format for with C++. I have managed to make some really good headway here, and squash lots of little nagging bugs we have hit. These have not been merged into Qt yet, but I hope we can get them reviewed soon. (If you have approver rights, I would appreciate if you took a look!)

I fixed a bug where qmlformat would indent call expressions twice causing weird indentation like this:

onTestChanged: {
fooBar(test, {
        // Testing
        "foo": "bar"
        });
}

qmlformat shouldn’t insert newlines in empty components and objects which we use in a lot of QML code. Normally qmlformat would format them like this, which wastes space and looks kinda ugly:

QtObject {
}

Oh yeah, and object declarations in arrays should look less insane.

If you use spaces to delineate groups of import statements, qmlformat should now try to preserve that instead of destroying it.

And two more small things: fixing the command line arguments not overriding anything and fixing the QML coding conventions documentation.

Like or comment on this post

Monday, 25 September 2023

I had the hankering for tinkering the KDE application style. The default style by KDE, Breeze, is pretty nice as is, but there are small things I'd like to modify.

There's Klassy which is quite customizable and fun, but I don't really need all of the settings it has.

Then there's Kvantum which uses SVG files to create a theme, but they don't follow KDE colorschemes. And I dislike working with SVG files.

Both are brilliant for their usecases, but I wanted just Breeze with few changes.

Fork time!

Screenshot Zephyr style in action

So, I did what one has to do, forked Breeze and renamed everything Breeze related to Zephyr. I chose Zephyr because it was synonym for Breeze in Thesaurus lol. Also, it makes sure it's last in the list of the application styles, so people don't accidentally confuse it to Breeze.

Here's link to the repository: https://codeberg.org/akselmo/Zephyr

Installation help is also there, but feel free to make issue and/or merge requests for adding stuff like what packages one has to install for their distro.

Unfortunately due to the massive size of the Breeze Gitlab repo, I didn't want to flood Codeberg with the whole history. So, some of the history got lost. I have mentioned it in the readme file though.

After renaming all the things, the whole thing built and installed surprisingly easily.

I then implemented following:

  • Black outline setting, so the default outline has a black one around it.
    • Why? Idk looks cool. Not really other reason.
    • Yes, it can be disabled.
  • Traffic color icons in window deco
    • I am allergic to Apple but the traffic light concept just makes sense to me.
    • Also can be enabled or disabled
  • Customizable style frame and window deco outline colors
    • You can completely change the frame colors.
    • You can also make them invisible! No outlines, no frames! Fun!
  • Slightly rounder windows and buttons
    • At some point I will make a setting for these too, but now they're applied when the thing is built
  • Fitting Plasma style if you use the defaults Zephyr offers (mostly black outlines)
    • The plasma theme buttons do not match the application style in roundness, yet.
    • I am lazy and avoid working with SVG files as long as I can

Why

For fun! For learning! And I wanted to make something that is super close to Breeze (hell, it is Breeze, just few mods), but still has it's own charm and how I like seeing my desktop.

It also can work as a great test bench for others who want to see if they can modify application style.

Just rename anything Zephyr to YourForkNameHere and have fun. But it's probably better to fork the original Breeze project :)

Also, when making my own things for Breeze, it's nice to just implement them in something similar but different name so I can test the changes for longer period of time. And if I like the changes I can maybe show them to upstream.

In future, I will make it work with Plasma 6 (unless i feel lazy). Probably will have to fork Breeze then again and apply my changes. Hopefully it's not too big of a change.

Also, I will be working on the actual Breeze in future too! I hope to implement separator colors for the Plasma colorscheme, so basically you can change the color of all frames and outlines and whatnot. This kinda helped me to figure how that works as well!

All in all, good project, I keep tinkering with it and it helps me understand the Breeze styling and Qt in general more.

Revontuli and Zephyr

My colorscheme Revontuli works really well together with Zephyr. So, feel free to give them a go!

Thanks for reading as usual!

Sunday, 24 September 2023

On Thursday and Friday evenings, I went to the Matrix Community Summit at C-Base in Berlin with Tobias. It was the occasion to meet a few other Matrix developers particularly the Nheko developer, MTRNord and a few other devs whom I only knew by nickname. It was great even though I could only spend a few hours there. Tobias stayed longer and will be able to blog more about the event.

Photo of the C-Base showing a lot of electronical equipements
Photo of the C-Base showing a lot of electronical equipements

During the weekend, instead of going to the Matrix summit, I participated to the KDE Promo sprint with Paul, Aniqa, Niccolo, Volker, Joseph. Aron also joined us via video call on Saturday. This event was also in Berlin at the KDAB officem which we are very thankful for hosting us.

This sprint was the perfect occasion to move forward with many of our pending tasks. I mainly worked on web-related projects as I tried to work on a few items on my large todo list.

We now have an updated donation page, which includes the new donnorbox widget. Donnorboy is now our preferred way to make recurring donations and recurring donations are vital to the success of KDE. Check it out!

Screenshot of the website KDE.org/community/donations
Screenshot of the website KDE.org/community/donations

With Paul, we also looked at the next KDE For-pages. Two of them are now done and we will publish them in the coming weeks. There are plans for a few more and if you want to get involved there, this is the phabricator task to follow.

I also updated the KDE For Kids with the help of Aniqa. It now features the book Ada & Zangemann from Matthias Kirschner and Sandra Brandstätter that sensibilise kids to Free Software. Let me know if you have other books suggestions for kids around Free Software and KDE that we can include on our websites.

This was only a short version of all the things we did during this sprint, I will let the others blog about what they did. More blog posts will certainly pop up on planet.kde.org soon.

The sprint would have been only possible thanks to the generous donation from our users, so consider making a donation today! Your donation also helps to pay for the cost of hosting conferences, server infrastructure, and maintain KDE software.

Tuesday, 19 September 2023

Qt OPC UA – Data Type Code Generation

The type system of OPC UA permits the creation of complex and nested data types. With the merge of the generic struct decoding and encoding feature, the Qt OPC UA module has greatly improved the comfort of handling such types. But for large projects with lots of custom data types, its QVariant based interface might still feel a bit too complicated.

Continue reading Qt OPC UA – Data Type Code Generation at basysKom GmbH.

Saturday, 16 September 2023

Kraft (Github) is a desktop utility making it easy to create offers and invoices quickly and beautifully in small companies.

Today we are releasing Kraft Version 1.1 with significant improvements for users and the Krafts integration with latest software such as cmake and KDE.

It received updated dutch translations in UI and also for the manual. The application icon was fixed, and some cmake related fixes were done that make Kraft working with different versions of Akonadi that are available on different distributions.

Macros

For users, two significant improvements are included: The header- and footer texts of the documents now may contain macros that support automatic computing of values such as dates that depend on the document date. With that, it is for example easy to have for example a payment date printed out on the document, that is ten days later than the document date.

There are even more interesting macros, stay tuned for a separate post about this feature.

Insert Templates Button

The second new feature is a new button that allows to insert templates for the header- or footer text at the cursor position. Before it was only possible to replace the entire text with a template. This will give users way more flexibility how to structure template texts.

In parallel to these improvements, work is also going on in a branch for Kraft 2.0 which will enable more collaborative functions for Kraft.

Thursday, 14 September 2023

Generic Struct Handling is Coming to Qt OPC UA

OPC UA servers often use structured data types, for example when they are implementing a companion specification or exposing custom structured data types from a PLC program. Up to now, Qt OPC UA was just returning a binary blob when reading such a value and the decoding was left entirely to the user. Since OPC UA 1.04, there is a standardized way for a server to expose the data type description for custom data types. We have extended Qt OPC UA to use this information to make it much easier to encode and decode custom data types. The following article introduces the new API.

Continue reading Generic Struct Handling is Coming to Qt OPC UA at basysKom GmbH.

Sunday, 10 September 2023

Man, I wish politics were boring, but that is never going to happen. The only way a party can improve their popularity is by being seen as different and in some way better, so we will always have parties saying exactly that: That the others are wrong and that they are better.

However, in many cases there is actually a right and a wrong way to engage with a problem, so what will happen if one side wants to become popular with the worse solution? Well, lies, propaganda and disinformation of course!

But all of that shouldn't matter. If you have any opinion at all about what policies are good or bad, you can look up for yourself if a specific party worked for or against you in the past. But who wants to figure that out in their free time, really? I certainly don't, but I did it anyway because various elections in Germany are coming up and some parties have been actively working against what the KDE community stands for:

https://wordsmith.social/felixernst/deutscher-wahlkampf-aus-einer-kde-perspektive (German)

Yes, that article is only in German. I don't really want to translate it because I would need to provide a lot of local background knowledge as context. I am also already annoyed enough by Germany's political landscape that I wouldn't want to spend the time figuring out how to explain it to an international readership with varying backgrounds and severity of disinformation.

In any case, I wish all of you will be able to elect the party that is the least corrupt and whose actions (or lack thereof) are the least likely to kill innocent people. Happy voting!