I had joined MBRDI in June 2021, after almost 4 years, I decided to part ways with MBRDI this January 2025 and decided to pursue new adventure, more on that when time comes, but for now I am keeping it under wraps. I hope you can respect that! 🙂
However this is not the only professional update I want to talk about, I want to talk about another exciting opportunity,
NGI0 Core grant
I am very happy to announce that I will be working on improving Plasma Mobile (and in general, Linux Mobile) power management through funding support from the NGI0 Core Fund, a fund established by NLnet with financial support from the European Commission’s Next Generation Internet program. You can find very thin details on this page. Though this is not the only project that is being funded through NGI0 Core fund, There are total 56 projects being sponsored as part of October call.
Kaidan 0.12.0 looks and behaves better than ever before!
Chats can now quickly be pinned and moved.
In addition, the list of group chat participants to mention them is placed above the cursor if enough space is available.
With this release, OMEMO can be used right after migrating an account and migrated contacts are correctly verified.
The developer teams continued to improve the Qt6 port of Krita. Dmitry fixed the HDR support on Windows (Dmitry Kazakov, link). Freya fixed an OpenGL crash on macOS (Freya Lupen, link).
Carl Schwan reduced the memory usage of various Akonadi resources by around 75% each. The optimized resources, which take advantage of this new API, are the following: Birthday, VCard files and directories, Ical, Mbox, Open-Xchange, cardDAV and calDAV. There is already significant progress done in that direction also for the IMAP and POP3 resources. The technical background behind this is that these resources running as independent processes are now using non-visual QCoreApplication instead of the more powerful QApplication, which is more appropriate resource wise for background services. This is part of the Don't depend on QtWidgets in lower parts of the stack milestones. (Carl Schwan, 25.08.0. Link 1, link 2, link 3, link 4, link 5, link 6, link 7, link 8, link 9, link 10, link 11, ...)
Daniel made a change to ensure that operations in Akonadi that operate on a large number of items are processed as multiple smaller batches which the SQL engine can then handle (Daniel Vratil, 25.08.0. Link).
Merkuro Calendar Manage your tasks and events with speed and ease
Tobias ported Merkuro Calendar to the new QML declaration which slightly improves the performance but more importantly enables us to take advantage of the QML tooling (Tobias Fella, 25.04.0. Link).
Carl made the region used to display holidays configurable. You can also select more than one region now (Carl Schwan, 25.04.0. Link)
Kleopatra Certificate manager and cryptography app
Tobias moved the notepad feature to a separate window, which means it's now possible to have multiple notepads open at the same time (Tobias Fella, 25.08.0. Link).
Tobias also ensured the GPG password prompt (pinentry) in Kleopatra is properly parented to the correct parent window on Wayland (Tobias Fella, 25.04.0. Link). Other apps using GPG were also fixed.
KOrganizer KOrganizer is a calendar and scheduling application
Allen made a series of small improvements and bugfixes to Korganizer. He improved the configure view menu action description (link), added more information to the delete folder dialog (link), and added a search option to consider the current view filters. (Link).
The digiKam team released version 8.6.0. of the powerful photo classifying and editing tool. Among many other things, digiKam now comes with a smarter face management tool, an improved auto-tagging system that identifies elements in your images, fully automatic red-eye removal, and a new image quality feature that classifies images according to their aesthetic quality. The digiKam developers also fixed 140 bugs.
Volker improved the history of past searches in KTrip by reusing some code from Itinerary. The biggest improvements are that the list is now de-duplicated, and the model supports more features not yet exposed to the UI. (Volker Kruase, 25.08.0. Link)
For a complete overview of what's going on, visit KDE's Planet, where you can find all KDE news unfiltered directly from our contributors.
Get Involved
The KDE organization has become important in the world, and your time and
contributions have helped us get there. As we grow, we're going to need
your support for KDE to become sustainable.
You can help KDE by becoming an active community member and getting involved.
Each contributor makes a huge difference in KDE — you are not a number or a cog
in a machine! You don’t have to be a programmer either. There are many things
you can do: you can help hunt and confirm bugs, even maybe solve them;
contribute designs for wallpapers, web pages, icons and app interfaces;
translate messages and menu items into your own language; promote KDE in your
local community; and a ton more things.
You can also help us by donating. Any monetary
contribution, however small, will help us cover operational costs, salaries,
travel expenses for contributors and in general just keep KDE bringing Free
Software to the world.
To get your application mentioned here, please ping us in invent or in Matrix.
I just got myself a brand new car: an ID.Buzz with seven seats so that I can fit the whole family at once. I’m very happy with the car this far, but since it has connectivity, I want to see if I can integrate it into HomeAssistant.
Having played with the software for a bit (and reported a bug that Till fixed asap – I’m impressed!) I decided to setup the whole thing on my little RaspberryPi that runs a few little services I use around the house.
Preparing this, I setup a new user and installed the software in a Python virtual environment:
sudo adduser carconnectivity
sudo su carconnectivity
cd
mkdir carconnectivity
cd carconnectivity/
python -m venv venv
source venv/bin/activate
pip install carconnectivity-connector-volkswagen==0.5a1 carconnectivity-plugin-mqtt carconnectivity-plugin-mqtt_homeassistant
vim carconnectivity.json
Using the vim command, I created the CarConnectivity configuration file. Update usernames, passwords and IPs to your needs. I will experiment with the interval parameter, as I don’t want to discharge the 12v battery by querying the car too much.
Having configured the service (and having run it manually to fix my mistakes) I created the carconnectivity.service systemd service shown below (in /etc/systemd/system):
Friendly country, Friendly people, Reunions, New Experience, a bit of hustle and that’s LIFE!
So, this was my 4th conference and 3rd international trip! A lot of new experiences and new friends, but let’s talk about FOSSASIA Summit 2025 first.
FOSSASIA Summit 2025
This is the first conference where I was having a lot of different tasks, lightning talk, representing Ubuntu booth and KDE booth. So, a lot of different perspectives will be here. Sometimes I’ll be writing from KDE’s pov, sometimes Ubuntu’s and sometimes my own.
After four months of active maintenance and many weeks triaging bugs, the digiKam team is proud to present version 8.6.0 of its open source digital photo manager.
The digiKam team has continued to work on a better Artificial Intelligence integration in digiKam, and many parts have been improved with the 8.6.0 release.
One of the biggest behind-the-scenes changes in the upcoming Plasma 6.4 release is the split of kwin_x11 and kwin_wayland codebases. With this blog post, I would like to delve in what led us to making such a decision and what it means for the future of kwin_x11.
Background
KWin started as an X11 window manager almost two and a half decades ago. Over the course of the years, it transformed drastically. It gained support for compositing on X, and it became a Wayland compositor.
Sharing the same codebase was critical in the early days of kwin_wayland. We already had working window management abstractions, which had been tested for many years, so we could reuse them on Wayland instead of writing new from scratch. Also, if kwin_x11 gained a new feature, then kwin_wayland would likely gain it for free too.
As time went by, kwin_wayland outgrew kwin_x11. They still shared code but they became quite distinct projects with different mental models how things operate, e.g. how pixels get on the screen or how input works. It also didn’t help that many Plasma developers jumped the X11 ship and turned to the Wayland side as part of the “eating your own dog food” practice, which eventually led to the feature freeze in KWin/X11 back in 2018 due to the lack of sufficient testing and various breakages.
Some time around 2020, we started taking a more bold and aggressive approach to Wayland session development because we saw that Plasma Wayland was trailing behind other desktop environments and something had to be changed in order to catch up. Such a policy produced great results, and Plasma is now one of the leading Wayland desktop environments. Unfortunately, it also greatly contributed to the number of regressions in the X11 session.
Another issue was that there were some features that we couldn’t make work as expected on Wayland so we had to drop them for everyone, which understandably made X11 users unhappy.
Goals
A few years ago, we started contemplating the idea of splitting the X11 and Wayland codebases because of the growing list of regressions affecting the X11 session, and architecture restrictions imposed on KWin/Wayland by the way KWin/X11 works.
That would allows us to keep KWin/X11 working as is without it breaking too often and freely change KWin/Wayland in ways that we think are best suited to make the Plasma Wayland session even better. Of course, it is not a silver bullet solution: we replace one problem with another problem (mainly related to maintenance and ensuring interface compatibility between two projects).
Details
After various discussions online and at Akademy and also seeing (impressive) Plasma Wayland usage statistics, we decided that it’s the right time to do such a split. The main kwin repository is going to host KWin/Wayland, while the kwin-x11 repository is going to host KWin/X11.
KWin/X11 and KWin/Wayland are co-installable so users can freely switch between the X11 and Wayland sessions back and forth and also make sure that updating to 6.4 is not a big hassle for distributions. You’ll be able to have only KWin/X11 or only KWin/Wayland on your computer, or both.
The codebase split doesn’t affect Xwayland support in KWin/Wayland. In other words, X11 applications will continue running on Plasma Wayland.
Extensions
Like any other Plasma component, KWin’s functionality can be extended using plugins. There’s good and bad news. The good news is that extensions written in JavaScript and QML (for example, fancy effects that are available at the KDE Store) will continue working both with kwin_x11 and kwin_wayland as expected, so extension developers don’t need to do anything about it. The bad news is that C++ extensions should be specifically targeted for kwin_x11 and kwin_wayland because neither provides API and ABI compatibility guarantees for its C++ API.
As Wayland progress moves forward, it is likely that the scripting API of KWin/Wayland will be further extended.
Future of KWin/X11
KWin/X11 will be still maintained for the foreseeable future. But that maintenance work will boil down to fixing build errors, adapting to new KDE Frameworks and Plasma APIs, and backporting window-related fixes from KWin/Wayland. There are no plans to drop KWin/X11 in the Plasma 6 lifecycle, although it’s highly possible that it will happen in Plasma 7.
KWin/X11 won’t receive new features anymore; until recently, it received new features that had been developed against KWin/Wayland passively (because both lived in the same repository). However, it might be actually a good thing because the X11 session doesn’t receive that much testing nowadays.
Last week I decided to clean up a bit of digital cruft. That is, I moved a few of my websites onto a single VPS, saving quite a bit of monthly server hosting costs.
What I did was that I moved VPSes from Linode (Akamai) to DigitalOcean, but also migrated a full web hotel from One to DigitalOcean (converting email accounts to email forwards).
As this is something that I do very rarely, I decided to document the process here so that I don’t have to look everything up again next time around.
The grunt work was about migrating a number of L*MP services to a LEMP server. There are a couple of tasks involved here, mainly migration of databases and getting WordPress running in a subdirectory using Nginx. The rest of the exercise had to do with the moving of nameservers and waiting for DNS propagation to get certbot to provide certificates for the new location.
Migration of MySQL databases
The migration of a database between machines can be broken down into three stages:
Dumping the old database
Creating a new database and user
Sourcing the database contents into the new database
I choose to do it in these three stages, as I’d like to keep the old database dump as an additional backup. The other option would be to transfer the database contents in a single step, merging steps 1 and 3 into one
Nevertheless, I use mysqldump to dump the database contents, and then bzip2 to reduce the size of the dump. This is efficient since and SQL dump is quite verbose.
This is derived from the answer by Anuboiz over at stack overflow. The resulting file is then transferred to the new server using scp together with the actual website.
The next step is to create a new database and a new database user. Here, I assume MariaDB (using the mysql commands), as my main target is WordPress. For other database engines, e.g. Postgresql, please check the docs for exact grammar, but the SQL commands should be very similar.
sudo mysql mysql> CREATE DATABASE databasename; mysql> USE databasename; mysql> CREATE USER 'username'@'localhost' identified by 'password'; mysql> GRANT CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, SELECT, REFERENCES, RELOAD on databasename.* TO 'username'@'localhost' WITH GRANT OPTION; mysql> EXIT
The next step is to read the database contents into the new database. For this, we need to unzip the sql dump, e.g. bunzip2 dumpname.sql.bz2, which will result in a file called dumpname.sql. Please notice that bunzip2 unzips the file and removes the original, zipped, file. If you want to keep the original, use the -k option.
Once you have the dumpname.sql file available, you can read it into the database with the newly created user using the source command as shown below.
mysql -u username -p
enter the password here
mysql> USE databasename;
mysql> SOURCE dumpname.sql;
mysql> EXIT
Now you should have a new database with the old database contents on the new server, with an associated database user. For WordPress sites, make sure that you reflect any changes in the associated wp-config.php file.
WordPress in a subdirectory using Nginx
The other piece of the puzzle that was new to me was to run WordPress from a subdirectory, e.g. example.com/blog/, rather than from the root level, e.g. example.com/.
Removing most of the nginx server configuration, the following parts does the magic:
server { root /var/www/thelins.se; index index.php index.html;
The trick was to ensure that the subdirectory try_files statement refer to the correct index.php. Notice that this has to be done for each WordPress instance, if you happen to have multiple WordPress installations in various subdirectories on the same domain.
Conclusions
Its a bit of hassle to migrate a lot of web sites at once, but the monetary saving from moving the low traffic sites onto a single VPS, and the simplification of the management and monitoring by moving all VPSes to a single provider makes it worth it.
In the previous blog, you learned all about moving items within a single view, to reorder them.
In part 2, we are still talking about moving items, and still about inserting them between existing items (never overwriting items) but this time the user can move items from one view to another. A typical use case is a list of available items on the left, and a list of selected items on the right (one concrete example would be to let the user customize which buttons should appear in a toolbar). This also often includes reordering items in the right-side list, the good news being that this comes for free (no extra code needed).
To allow dragging items out of the view, make sure to do the following:
☑ Call view->setDragDropMode(QAbstractItemView::DragOnly) (or DragDrop if it should support both).
☑ Call view->setDragDropOverwriteMode(false) so that QTableView calls removeRows when moving rows, rather than just clearing their cells
☑ Call view->setDefaultDropAction(Qt::MoveAction) so it's a move and not a copy
Setting up the model on the drag side
To implement dragging items out of a model, you need to implement the following:
class CountryModel : public QAbstractTableModel
{
~~~
Qt::ItemFlags flags(const QModelIndex &index) const override
{
if (!index.isValid())
return {}; // depending on whether you want drops as well (next section)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
// the default is "return supportedDropActions()", let's be explicit
Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }
QMimeData *mimeData(const QModelIndexList &indexes) const override; // see below
bool removeRows(int position, int rows, const QModelIndex &parent) override; // see below
};
More precisely, the check-list is the following:
☑ Reimplement flags() to add Qt::ItemIsDragEnabled in the case of a valid index
☑ Reimplement supportedDragActions() to return Qt::MoveAction
☑ Reimplement mimeData() to serialize the complete data for the dragged items. If the views are always in the same process, you can get away with serializing only node pointers (if you have that, e.g. for tree models) and application PID (to refuse dropping onto another process). Otherwise you can encode the actual data, like this:
QMimeData *CountryModel::mimeData(const QModelIndexList &indexes) const
{
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
for (const QModelIndex &index : indexes) {
// This calls operator<<(QDataStream &stream, const CountryData &countryData), which you must implement
stream << m_data.at(index.row());
}
QMimeData *mimeData = new QMimeData;
mimeData->setData(s_mimeType, encodedData);
return mimeData;
}
s_mimeType is the name of the type of data (make up a name, it usually starts with application/x-)
☑ Reimplement removeRows(), it will be called after a successful drop. For instance, if your data is in a vector called m_data, the implementation would look like this:
bool CountryModel::removeRows(int position, int rows, const QModelIndex &parent)
{
beginRemoveRows(parent, position, position + rows - 1);
for (int row = 0; row < rows; ++row)
m_data.removeAt(position);
endRemoveRows();
return true;
}
Setting up the view on the drop side
☑ Call view->setDragDropMode(QAbstractItemView::DragDrop) (already done if both views should support dragging and dropping)
Setting up the model on the drop side
To implement dropping items into a model (between existing items), you need to implement the following:
class DropModel : public QAbstractTableModel
{
~~~
Qt::ItemFlags flags(const QModelIndex &index) const override
{
if (!index.isValid())
return Qt::ItemIsDropEnabled;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // and optionally Qt::ItemIsDragEnabled (previous section)
}
// the default is "copy only", change it
Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
QStringList mimeTypes() const override { return {QString::fromLatin1(s_mimeType)}; }
bool dropMimeData(const QMimeData *mimeData, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override; // see below
};
☑ Reimplement supportedDropActions() to return Qt::MoveAction
☑ Reimplement flags() For a valid index, make sure Qt::ItemIsDropEnabled is NOT set (except for tree models where we need to drop onto items in order to insert a first child). For the invalid index, add Qt::ItemIsDropEnabled, to allow dropping between items.
☑ Reimplement mimeTypes() and return the name of the MIME type used by the mimeData() function on the drag side.
☑ Reimplement dropMimeData() to deserialize the data and insert new rows. In the special case of in-process tree models, clone the dragged nodes. In both cases, once you're done, return true, so that the drag side then deletes the dragged rows by calling removeRows() on its model.
bool DropModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
~~~ // safety checks, see full example code
if (row == -1) // drop into empty area = append
row = rowCount(parent);
// decode data
const QByteArray encodedData = mimeData->data(s_mimeType);
QDataStream stream(encodedData);
QVector<CountryData> newCountries;
while (!stream.atEnd()) {
CountryData countryData;
stream >> countryData;
newCountries.append(countryData);
}
// insert new countries
beginInsertRows(parent, row, row + newCountries.count() - 1);
for (const CountryData &countryData : newCountries)
m_data.insert(row++, countryData);
endInsertRows();
return true; // let the view handle deletion on the source side by calling removeRows there
}
☑ Call widget->setDragDropMode(QAbstractItemView::DragOnly) or DragDrop if it should support both
☑ Call widget->setDefaultDropAction(Qt::MoveAction) so the drag starts as a move right away
On the "drop" side:
☑ Call widget->setDragDropMode(QAbstractItemView::DropOnly) or DragDrop if it should support both
☑ Reimplement supportedDropActions() to return only Qt::MoveAction
Additional requirements for QTableWidget
When using QTableWidget, in addition to the common steps above you need to:
On the "drag" side:
☑ Call item->setFlags(item->flags() & ~Qt::ItemIsDropEnabled); for each item, to disable dropping onto items.
☑ Call widget->setDragDropOverwriteMode(false) so that after a move the rows are removed rather than cleared
On the "drop" side:
☑ Call widget->setDragDropOverwriteMode(false) so that it inserts rows instead of replacing cells (the default is false for the other views anyway)
☑ Another problem is that the items created by a drop will automatically get the Qt::ItemIsDropEnabled flag, which you don't want. To solve this, use widget->setItemPrototype() with an item that has the right flags (see the example).
Additional requirements for QTreeWidget
When using QTreeWidget, you cannot disable dropping onto items (which creates a child of the item).
You could call item->setFlags(item->flags() & ~Qt::ItemIsDropEnabled); on your own items, but when QTreeWidget creates new items upon a drop, you cannot prevent them from having the flag Qt::ItemIsDropEnabled set. The prototype solution used above for QTableWidget doesn't exist for QTreeWidget.
This means, if you want to let the user build and reorganize an actual tree, you can use QTreeWidget. But if you just want a flat multi-column list, then you should use QTreeView (see previous section on model/view separation).
Addendum: Move/copy items between views
If the user should be able to choose between copying and moving items, follow the previous section and make the following changes.
With Model/View separation
On the "drag" side:
☑ Call view->setDefaultDropAction(...) to choose whether the default should be move or copy. The user can press Shift to force a move, and Ctrl to force a copy.
☑ Reimplement supportedDragActions() in the model to return Qt::MoveAction | Qt::CopyAction
On the "drop" side:
☑ Reimplement supportedDropActions() in the model to return Qt::MoveAction | Qt::CopyAction
The good news is that there's nothing else to do.
Using item widgets
On the "drag" side:
☑ Call widget->setDefaultDropAction(...) to choose whether the default should be move or copy. The user can press Shift to force a move, and Ctrl to force a copy.
Until Qt 6.10 there was no setSupportedDragActions() method in the item widget classes (that was QTBUG-87465, I implemented it for 6.10). Fortunately the default behavior is to use what supportedDropActions() returns so if you just want move and copy in both, reimplementing supportedDropActions() is enough.
On the "drop" side:
☑ Reimplement supportedDropActions() in the item widget class to return Qt::MoveAction | Qt::CopyAction
The good news is that there's nothing else to do.
Improvements to Qt
While writing and testing these code examples, I improved the following things in Qt:
QTBUG-1387 "Drag and drop multiple columns with item views. Dragging a row and dropping it in a column > 0 creates multiple rows.", fixed in 6.8.1
QTBUG-36831 "Drop indicator painted as single pixel when not shown" fixed in 6.8.1
QTBUG-87465 ItemWidgets: add supportedDragActions()/setSupportedDragActions(), implemented in 6.10
Conclusion
In the next blog post of this series, you will learn how to move (or copy) onto existing items, rather than between them.