27 June, 2020

Packaging a big stack like the software from the KDE community – Frameworks, Plasma, and all the applications and libraries and tools from the KDE Release Service – takes a fair bit of time and energy. The KDE-FreeBSD team works on both packaging and porting – making sure that KDE applications behave well on FreeBSD just like on other operating systems.

The majority of the work of compatibility happens in Qt, which is also maintained by the KDE-FreeBSD team. Then the KDE frameworks – 80 or so libraries that are small, lightweight, tiered-so-you-know-about-dependencies and LGPL-licensed – pile a bunch of compatibility on top of that for desktop purposes.

But sometimes, an application needs to dig into the system itself. A text editor edits text regardless of the underlying system, but a memory-usage monitor needs to know how to ask the OS about memory-usage.

So this week I spent a fair bit of time in the KSysGuard codebase, because there’s a FreeBSD bug report that says that the memory-usage monitor shows nothing, and another report that says the graph and the status bar don’t match.

This isn’t the first time: apparently in 2009 I did some work on KSysGuard for OpenSolaris. Different times.

Something that has vaguely bugging me since 2017 is the display of micro-kibibytes in KSysGuard. A micro-kibibyte is one one-hundred-and-twenty-secondth of a single bit – let’s call it a centibit – and that is not useful as a measure of memory usage.

It turns out that in 2016, when porting from KLocale (a kdelibs4-era class) to QLocale, some edge cases were missed. Porting is rarely simple. So today I reintroduced the relevant bits, and in the spirit of modern C++ replaced some magic numbers by static constexpr const int values. As a result, KSysGuard tooltips no longer annoy me (assuming the MRs are accepted over on KDE invent).

And then I looked at the upstream bug list, and fixed a buffer overflow if you have implausibly long disk-device names (I suspect this is only relevant with the old Solaris naming scheme).

Once the bug list is open, it’s natural to click around a bit, and I tracked down another bug to a typo in an entirely different repository, which causes translated rich-text markup to break.

The next bug is about column alignment, and I ended up with 10 commits to illustrate the thinking behind what was a relatively simple refactoring, but a single change would be a lot harder to justify.

.. and then it was 2am. What was I trying to fix? I forget.

After having written about where we get the data for KDE Itinerary’s train station and airport maps from, this post is about how we actually render this data.

Rendering OSM Data

When looking at the geometry, OSM data consists basically just of lines and polygons, so technically there’s only few primitives to render. To make this look like a useful map though, a lot depends on the map styling. That is, the decision with which colors, line strokes, fill patterns, z order, and iconographic or textual labels those geometric primitives should be rendered. Here we are talking about hundreds of rules to make a good map.

Since writing all those rules in code is hard to work with and to maintain, using a declarative way to define the rules is attractive. Several such systems exist in the OSM space already, such as MapCSS or CartoCSS, so we followed that and are using MapCSS for our indoor map renderer.


As the name suggests, there’s quite a few similarities to CSS, which makes it fairly straightforward to work with. Selectors and conditions are tied to querying OSM element types, OSM tags and the current zoom level instead of HTML elements and attributes, and similarly the properties you can declare refer to the styling capabilities of the map renderer instead.

Let’s look at a simple example for rendering railway tracks:

    color: #eff0f1;
    dashes: 2,2;
    width: 1.5m;
    casing-width: 3px;
    casing-color: #31363b;
    linecap: none;

One thing you might notice there is the support for the size unit “m”, meters. Unlike sizes specified in pixel, the size on screen changes depending on the current zoom level. Another powerful mechanism is being able to refer to values from OSM tags here. Let’s expand the above example by the following additional rule:

way[railway=rail][gauge] {
    width: gauge;

This will cause railway tracks being rendered in a width matching their track gauge. The following screenshot of the mixed-gauge Swiss station of Visp shows the result.

Screenshot of a map showing Visp train station, showing both narrow and normal gauge railway tracks.
Visp train station, narrow gauge tracks at the bottom, normal gauge tracks at the top.

Just like CSS, MapCSS also supports importing of rules from other files. This allows a modular design, sharing common rules between a dark and a light style for example, or even to define special-purpose variations of a common base style. This could for example be used to specifically highlight things depending on a users need or the current use-case/workflow.

Another nice side-effect of not having this in (compiled) code is the ability to load and reload MapCSS stylesheets arbitrarily at runtime, which speeds up development considerably: just make a change, reload the stylesheet and check if things look like you want them to look.

One can also twist this into a fancy debug output for the data processing, by creating a diagnostic stylesheet which e.g. just shows geometric primitives as-is.

Screenshot of a diagnostic rendering of a train station platform showing navigation paths and elevator identifiers.
Diagnostic MapCSS stylesheet showing navigation paths and elevator ids on a train station platform.

Such flexibility often has its price though. And indeed, the initial naive implementation didn’t scale up well enough to a realistically sized rule set. However, after a few rounds of optimizations MapCSS evaluation for an entire large building floor now easily happens within a single frame cycle, on a mobile phone. Even loading the data of an entire city into this as a stress test just takes a few seconds. And should we ever hit the limit again, there’s a few more ideas on how to further improve this.

There’s still a number of things from the MapCSS spec we haven’t implemented yet, due to so far not having needed them (e.g. fill textures, extrusion properties, chained selectors or eval expressions). Similarly, there are a few things that seem not easily doable by standard MapCSS features, such as indicating line directions (e.g. on one way streets or escalators), so a few custom extensions might become necessary. This is all fine though, having a system now that we can tailor exactly to our needs.


While there is of course still plenty of work to do all over this, I think we are getting close to an initial integration into KDE Itinerary. While initially not offering more than showing a basic map, it would enable work on deeper integration features, and make all improvements on the map side immediately useful.

If you are interested in contributing, no matter if feedback, ideas or code, check out the workboard on Gitlab.

For playing with this locally, the best starting point is probably the QML example. After building KPublicTransport, add the bin/ sub-folder of the build directory to the QML2_IMPORT_PATH environment variable (or install to a properly setup prefix), and then load tests/indoormap.qml with qmlscene.

There’s no escaping that Qt Quick, sometimes also referred to by its language QML, has become the major focus of the framework. At least until the company decides to drastically change course in Qt 7. There will always be a place for QWidgets-based UI and C++ (everything compiles to C++ anyway, more or less), Qt Quick is really being pushed as the future of building user interfaces, especially for touch, mobile, and embedded.

A few years back, I tried playing around with using Qt Quick for rapid app prototyping by creating some reusable components and I wanted to build upon that by prototyping some non-conventional and even fictional user interfaces from prototype devices and concept videos. I didn’t get far with my limited knowledge of Qt Quick and was only able to implement an extremely crude version of MeeGo’s column-based home screen (a dream from long ago, don’t ask).

Inspired by my success (dripping with sarcasm), I ventured to try out other unconventional UI ideas that I had that I presumed would probably be easier to pull off in QtQuick compared to a traditional widget-based application. Especially since QML is always advertised for in-vehicle HUDs and instrumentation control panels.

Lastly, there was this sort of program I’ve long been thinking of, more as a fun and whimsical nod to some anime of my childhood. Yes, it’ll probably bring back not so fond memories of Clippy, but eh. Like I said, mostly for fun (for now).

I wish I could report that I have become a QML Qonvert (SCNR). I’m still on the fence about the whole Qt Quick matter, regardless of whether it’s the future or not. It’s probably possible to implement all of the fancy animation and touch-based gestures in C++ (since that’s what Qt Quick uses underneath anyway), but it will be like bending over backward just to have a “pure” C++ implementation. And since KDE Plasma, both desktop and mobile, are pretty centered around Qt Quick, the argument is pretty moot for a KDE developer working on those parts.

It’s not that I found the experience terrible. In fact, I’ve probably grown a bit fond of declarative programming, at least for some parts that make sense. I also appreciate how easy it is to play around with non-traditional widgets and controls or even make your own using QML, something I think would have taken me more than a day to pull off in C++ given the same level of proficiency (read: noob).

That freedom and flexibility, however, do come at a price, when the things you’ve taken for granted on the QWidgets side of the fence now become critical knowledge. Positioning, layouting, parenting, and the like have been particular stumbling blocks. They’re easy for the most common use cases but then become exponentially more complicated once you step outside of those. And don’t even get me started on how Qt Quick still lacks a proper tree view after so long.

My main takeaway? Qt Quick is almost like a separate and independent framework of its own, just one that’s built on top of Qt’s core classes. It almost shares very little in common with the widget-based paradigm that it’s probably best to treat it as an entirely different beast rather than just “another way” to develop Qt applications. In other words, it’s definitely something I’ll have to invest in learning deeper, as deep as I tried learning Qt ages ago. And considering the kinds of applications I want to write or am interested in, it is, as one purple dude said, inevitable.

The bugfixing and polish continue this week, but we also managed to squeeze in some welcome UI improvements, particular for our Kate text editor!

New Features

File move and copy operations and other similar I/O related jobs now support nanosecond timestamp precision (Méven Car, Frameworks 5.72)

File copy operations throughout KDE software can now make use of the copy-on-write functionality of the Btrfs filesystem (Méven Car, Frameworks 5.72)

Bugfixes & performance Improvements

.Desktop files whose icons are defined to be SVG files with the full path included now render correctly in Dolphin (Alexander Lohnau, Dolphin 20.04.3)

Hitting Ctrl+Shift+W in Yakuake now closes the session as expected rather than displaying an unpleasant “Ambiguous Shortcut Detected” dialog (Nicolas Fella, Yakuake 20.04.3)

Fixed a case where Discover could hang on launch and then crash (Aleix Pol Gonzales, Plasma 5.12.10 and beyond)

Fixed a bug that could cause Plasma Panels to be incorrectly drawn on top of full-screen game windows (Vlad Zahorodnii, Plasma 5.18.6 and beyond)

When an application exits very soon after inhibiting screen locking, the inhibition is now correctly cleared (Kai Uwe Broulik, Plasma 5.18.6 and beyond)

Fixed a bug that could cause the system to log out instead of restarting or shutting down (David Edmundson, Plasma 5.19.2)

Fixed a crash on Wayland when dragging-and-dropping a URL from Telegram to Firefox (David Edmundson, Plasma 5.19.2)

Switching between KWin rendering backends now lets you return to the original one without having to navigate elsewhere and then return (Benjamin Port, Plasma 5.19.2)

Fix Plasma 5.19 regression: The logout action in the Lock/Logout widget now works again (David Edmundson, Plasma 5.19.3)

Fix Plasma 5.19 regression: window rules using the WM_CLASS property now work again (Ismael Asensio, Plasma 5.19.3)

Fix Plasma 5.19 regression: window rules created from the rule dialog accessible by right-clicking on a window’s titleabar are now saved and applied properly (Ismael Asensio, Plasma 5.19.3)

Fix Plasma 5.19 regression: deleting multiple applications’ shortcuts in the new Global Shortcuts page no longer either fails or causes System Settings to crash (David Redondo, Plasma 5.19.3)

That incredibly annoying bug whereby scrolling with a scroll wheel mouse in a GTK app stops working when a Plasma notification appears has just been fixed!!! (Vlad Zahorodnii, Plasma 5.19.3)

The System Settings Default Applications page now lists Nautilus as a filemanager when it’s installed (Méven Car, Plasma 5.19.3)

Setting detailed locale format settings now works properly (Alexander Lohnau, Plasma 5.19.3)

The Media Player widget now has a saner default size when not in the System Tray (Riccardo Robecchi, Plasma 5.19.3)

Fixed a bug that could cause re-colorable icons to be re-colored incorrectly when changing an application-specific color scheme (David Redondo, Frameworks 5.72)

Plasma no longer crashes when configuring a WPA2-Enterprise encrypted Wi-Fi network with an EAP-TLS with public key only CA certificate file (Albert Astals Cid, QCA 2.3.1)

User Interface Improvements

Yakuake’s window can now be de-maximized with the same keyboard shortcut used to maximize it if you hit it a second time (Anton Karmanov, Yakuake 20.04.3)

Kate’s tab bar is now visually consistent with all the tab bars in other KDE apps (Tomaz Canabrava, Kate 20.08.0):

In case you noticed that the wallpaper is blurry, don’t worry, I’m fixing it

Kate’s tab bar now opens new tabs on the right, like most other tabs bars do (Christoph Cullmann, Kate 20.08.0)

Plasma’s Emoji picker window(which you can open with the Meta+period keyboard shortcut) now closes when you hit the escape key (Alexander Lohnau, Plasma 5.20)

That same Emoji picker window now lets you copy emojis using the standard Ctrl+C shortcut (me: Nate Graham, Plasma 5.20)

When the user has applied updates that require a reboot, the System Tray icon becomes a “Restart” icon and prompts you to restart when you click on it (me: Nate Graham, Plasma 5.20)

The System Tray’s expanded view now displays a button you can click on to configure the System Tray itself (me: Nate Graham):

The file dialog now behaves the same as Dolphin in that when you navigate to the parent folder, the child folder is highlighted (Ahmad Samir, Frameworks 5.72)

When you user trash a file, empty the trash, then undo the deletion, the message shown is now more accurate (Ahmad Samir, Frameworks 5.72)

How You Can Help

Have a look at to discover ways to help be part of a project that really matters. Each contributor makes a huge difference in KDE; you are not a number or a cog in a machine! You don’t have to already be a programmer, either. I wasn’t when I got started. Try it, you’ll like it! We don’t bite!

Finally, consider making a tax-deductible donation to the KDE e.V. foundation.


26 June, 2020

Part 5 -

Hi everyone

It has been two weeks since my last post. In this time period, I took forward my project, adding multiple datasets and completed “share pieces of candies” and “locate the regions” activities.

Our motive behind adding multiple datasets in activities is to make the difficulty range of activities wider. This way the same activity can be easily configured to be played by pupils of different ages or capabilities.

GCompris code has been divided into two parts/folders i.e “activities” and “core” parts.

Activities parts

The Activities folder includes the implementation of each activity, it contains 1 folder per activity and every folder further contains a QML file, javascript file, and resource folder. Here QML is for designing the user interface, javascript (.js) files contain the main logic of the activity and resource folders, as the name suggests it, contains all the resources for activities. Images are good examples for resource folder.

Core Parts

Many elements i.e, help button, bonus which pupils see after successfully completing the level, menu bar and even the base container of each activity are the same in many activities. So, obviously rewriting them every time for each activity is time-consuming and makes the code difficult to maintain. This is why we have a core folder that contains core files which can be easily imported and used by any activity.

Visualization is always easy :).

src/ activities/ sudoku/ Sudoku.qml Sudoku.js resources/ core/ ActivityBase.qml Bonus.qml 

Multiple dataset is implemented in core components of Gcompris. So, whenever I say I have added multiple datasets in an activity, it means I have changed the code of the activity to adapt multiple datasets and added JSON files. We use 1 JSON file to represent 1 dataset and datasets are a resource for an activity so we put them inside the resource folder.

I can further show you the json file and how anyone can easily customize them by discussing my past week’s work on share pieces of candies.

Share Pieces of Candy

Share Activity

As the name suggests, in this activity pupils have to equally distribute candies between their friends.

Share Dataset Activity

These are the datasets

Share JSON Activity

Part of JSON file, which anyone can use to customize levels

We always try to make these keys self-explanatory. One can change values and rerun the application to see the effects.

In Proposal, I planned to only have 2 datasets for this activity but later I found that we have some random levels too, in which we can’t guarantee the extra candies. So after discussing with mentors, we decided to add one flag for each level (you can see the “randomisedInputData” flag in above JSON pic) and the third dataset, which will contain randomized levels. The Share activity contains lots of levels, sublevels and instructions...

Today I want to present a testing technique I now use in Nanonote unit tests.

Nanonote main component is a QTextEdit with several "extensions" to provide custom behaviors such as indenting/unindenting selected lines with tab/shift+tab or moving selected lines up and down with alt+shift+arrow keys (next version feature, #spoileralert!).

Testing these extensions is not particularly difficult but it requires tedious setup to set the text, position the cursor, define the selection. Then you perform the action and have to write more tedious code to check the new text, cursor position and selection match your expectations. Not only is it tedious to write, it is also error-prone and hard to read.

Here is an example:

SECTION("indent whole lines") {
    // GIVEN a TextEdit with 3 lines, the first two being selected
    auto cursor = edit->textCursor();
    cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, 2);

    // WHEN I press Tab
    QTest::keyClick(edit, Qt::Key_Tab);

    // THEN the selected lines are indented
    CHECK(edit->toPlainText() == QString("    1\n"
                                         "    2\n"
    // AND the selected lines are still selected
    CHECK(cursor.selectionStart() == 4);
    CHECK(cursor.selectionEnd() == 12);

(The SECTION and CHECK macros come from the Catch2 test framework I use to write Nanonote tests)

To simplify this I created two helper functions. The first one, setupTextEditContent(), is responsible for setting up a TextEdit in the state I need for my test. It takes a TextEdit instance and a string describing the wanted state in a tiny "markup" language. This language is the text content for the TextEdit with some special characters to define the selection:

  • It must contain a | character to indicate the cursor position.
  • It can contain a * character to indicate the selection start.
  • | may appear before * in the case of an upward selection.

Using this function, we can rewrite our the GIVEN part of our test code like this:

SECTION("indent whole lines") {
    // GIVEN a TextEdit with 3 lines, the first two being selected
    setupTextEditContent(edit, "*1\n"

    // WHEN I press Tab
    QTest::keyClick(edit, Qt::Key_Tab);

    // THEN the selected lines are indented
    CHECK(edit->toPlainText() == QString("    1\n"
                                         "    2\n"
    // AND the selected lines are still selected
    CHECK(cursor.selectionStart() == 4);
    CHECK(cursor.selectionEnd() == 12);

The second function, dumpTextEditContent(), does the opposite: it returns the representation of the TextEdit state using the markup language. It can be used to check the state of the TextEdit instance matches what is expected. Our test code can thus be further simplified into this:

SECTION("indent whole lines") {
    // GIVEN a TextEdit with 3 lines, the first two being selected
    setupTextEditContent(edit, "*1\n"

    // WHEN I press Tab
    QTest::keyClick(edit, Qt::Key_Tab);

    // THEN the selected lines are indented
    // AND the selected lines are still selected
    REQUIRE(dumpTextEditContent(edit) == QString("    *1\n"
                                                 "    2\n"

As you can see the test code is much shorter and, at least for me, easier to read. Furthermore, in case of failures, the error message shows the difference between the actual and the expected state using the markup language instead of telling you that the cursor position is at 3 instead of the expected 2.

I find this technique makes it a lot less painful to write tests and will look into using it in other places when it makes sense. One drawback to keep in mind though is that the implementation of setupTextEditContent() and dumpTextEditContent() must not be buggy itself! It might make sense to write tests for those...

That's it for this article, I hope it was useful for you. I doubt I actually invented this technique, so I'd be interested to hear if you have ever used a similar testing technique in your own projects!


25 June, 2020

"6.0" is a word that brings a lot of excitement but also a lot of aprehension and for good reason. The reason our .0 releases sometimes struggle to match the quality isn't due to changes in the underlying libraries changing but how much we have to port away from the things that we've deprecated internally and have put off porting to.

Too many changes in one release becomes overwhelming and bugs creep in without time to get address them.

We want to be proactive in avoiding that.

At a recent Plasma sprint, we went through some of our bigger targets that we want to finish completely porting away from in time for the 6.0 release that we can actively start doing within the 5.x series where we can do things more gradually and inrecementally.

I've listed two big topics below.



DataEngines were a piece of tech from the KDE4 era. Effectively the idea was to provide an abstract mechanism to expose arbitrary data or plugins to a scriptable format usable by various language bindings. A valid task at the time, but Qt5 provided a much more efficent mechanism to do this capitalising on the metaobject system and models

DataEngines mostly form a now unnecessary layer of indirection that makes QML code hard to parse and suboptimal to run. They also are Plasma specific, which doesn't help other QML consumers of the same data.

We kept with them for Plasma 5 as there's some valid, perfectly working code exposed through them, and have been slowly porting away.


The task isn't to port all existing DataEngines. Some already have replacements under different names, some aren't widely used.

We want to find all existing applets that use DataEngines and make sure there are modern new QML bindings we can port the repsective applets to.

A list of tasks can be found:

System settings modules


Systemsettings is powered by a multitude of KDE Configuration Modules (KCMs) each one is a standalone plugin. There are nearly 100 modules, and with dated code stretching back over 20 years.

KDE, plasma especially is on the turning point between two toolkits. Some things are written in QWidgets and other things use the newer QtQuick. We try and hide this from the user; everything should of course look seamless. Implementation details shouldn't affect the UI.

We've been slowly porting our system settings modules, and whilst some were rocky at first, the later results are looking really nice and really polished, able to make use of emerging new patterns in our Kirigami toolkit.

Mixing two toolkits in the same process leads to a lot of complications that leak into the UI as well as overhead. We want to make it an objective to see if we can port everything to lead to an overall simpler stack.


The overall task is to find KCMs that haven't been ported yet and make the appropriate changes.

However, this isn't just a goal of blindly porting.

We want to take a step back, really polishing the UX, tidying the code to have a good logic vs UI split and revsit some modules that haven't been touched in a long time, putting effort into our underlying frameworks to make sure we have everything ready on the QtQuick side to do so.

A list of KCMs and their porting status can be found:

Getting involved

The best way to get involved is to comment on the relevant phabricator ticket. Or swing by #plasma on IRC and let people know what you want to work on and we'll help you get started.
Some of the systemsettings modules have mockups from the VDG and we want to keep them in the loop.


23 June, 2020

This week I implemented views, drag and drop of storyboard items in the central view and made some small changes. I also ran unit-tests, checked for memory leaks and debugged code, but unfortunately we couldn’t get it tested by users as we got some crashes.

There are three views to customize what part of the storyboard item you see. Namely they are Thumbnail only, Comments only and Both. This was easy to implement as we only had to make changes to delegate and view class to draw the right parts based on the chosen view.

All view
Comments Only View
Thumbnails only View

I also implemented drag and drop of storyboard items. For this I implemented the mimeData and dropMimeData functions and then called the moveRows function in the dropMimeData function to move the rows.

Drag and drop

Also we made the add and delete buttons permanent instead of show on hover as their might be some tablet devices that do not support hover.

I made some changes to unit-tests to account for the changes in the design. All the unit-tests passed. I also got some memory leaks that I investigated and fixed using valgrind. But we got some crashes that couldn’t be fixed this week so we couldn’t get the GUI tested by users.

This week I would focus on debugging the code and getting it tested by users.


22 June, 2020

Here’s a mid-year update on the 2020 roadmap I proposed six months ago:

FUSE mounts to better support accessing remote locations in non-KDE apps: DONE

kio-fuse was released in beta form early this year and is already packaged in many distros. It’s working great! The final release will happen later this year.

Privilege escalation in KIO and Dolphin: AT RISK

It turned out that there was more work remaining here than I had anticipated. Unfortunately nobody seems to have the critical combination of domain knowledge, interest in working on it, and time to do so. Assistance on would be appreciated to help make it happen this year.

Improved Samba share discovery in Dolphin: DONE

This was implemented for Dolphin 20.04. By all accounts, it’s working quite well!

Auto-rotation for tablets, convertibles, and other hardware with rotation sensors: DONE

This was implemented for Plasma 5.18 and works on Wayland (getting it working on X11 is a lost cause, apparently). If it isn’t working for you on Wayland, it’s likely that you don’t have the iio-sensor-proxy package installed, or your hardware isn’t supported by the kernel yet.

Implement more of the proposed visual design changes to the Breeze style and KDE apps: ON TRACK

Work is proceeding at a good pace. The new System Tray design was shipped with Plasma 5.19. We’re targeting 5.20 for the new application appearance and patches are landing. Things are on track.

Better wallpapers in the extra wallpapers repo: AT RISK

This is blocked on implementing a wallpaper cache. I took a stab at it for Plasma 5.18 but it turned out to be more complicated than I had anticipated and I kind of got demoralized and dropped it. Need to resume the work.

Per-screen scale factors on X11: UNLIKELY

Focus has shifted toward Wayland in a big way, and for the past few months, veteran KDE developers have been smashing Wayland problems left and right. They’ve gotten clipboard support working with Klipper and going between Wayland and XWayland windows; made Spectacle work properly; fixed a number of drag-and-drop issues, and are very close to finishing task manager thumbnails, screencasting, and more! Given the progress and momentum, there’s a strong desire to make Wayland finally usable rather than hack things into X11 that it was never designed to support and are unlikely to ever work properly.

Inertial scrolling throughout Plasma and QML apps: UNLIKELY

No work has happened here. A lot of the issue are in Qt itself and are very challenging to resolve, especially on X11. There may be more hope for getting it done on Wayland.

Power/session controls on the lock screen: AT RISK

I started implementing this and got it kinda-sorta working but then lost motivation and forgot about it. Sorry about that. I need to get back into it.

Well there you have it! Of course this is just a tiny fraction of the stuff actually going on, it’s just what’s relevant to the proposed roadmap I outlined earlier.

As always, if you want to see these things happen faster, please feel free to help out! The code is public and the people are friendly. 🙂 What do you have to lose!? Nothing, that’s what!

Just a quick note to let the world know that PhotoTeleport 0.13 has been released.