GNOME.ORG

24 hours a day, 7 days a week, 365 days per year...

August 30, 2018

Challenges in Maintaining A Big Tent for Software Freedom

[ A similar version of this blog post was cross-posted on Software Freedom Conservancy's blog

In recent weeks, I've been involved with a complex internal discussion by a major software freedom project about a desire to take a stance on social justice issues other than software freedom. In the discussion, many different people came forward with various issues that matter to them, including vegetarianism, diversity, and speech censorship, wondering how that software freedom project should handle other social justices causes that are not software freedom. This week, (separate and fully unrelated) another project, called Lerna, publicly had a similar debate. The issues involved are challenging, and it deserves careful consideration regardless of how the issue is raised.

One of the first licensing discussions that I was ever involved in the mid 1990s was with a developer, who was a lifelong global peace activist, objecting to the GPL because it allowed the USA Department of Defense and the wider military industrial complex to incorporate software into their destructive killing machines. As a lifelong pacifist myself, I sympathized with his objection, and since then, I have regularly considered the question of “do those who perpetrate other social injustices deserve software freedom?”

I ultimately drew much of my conclusion about this from activists for free speech, who have a longer history and have therefore had longer time to consider the philosophical question. I remember in the late 1980s when I first learned of the ACLU, and hearing that they assisted the Klu-Klux Klan in their right to march. I was flabbergasted; the Klan is historically well-documented as an organization that was party to horrific murder. Why would the ACLU defend their free speech rights? Recently, many people had a similar reaction when, in defense of the freedom of association and free speech of the National Rifle Association (NRA), the ACLU filed an amicus brief in a case involving the NRA, an organization that I and many others oppose politically. Again, we're left wondering: why should we act to defend the free speech and association rights of political causes we oppose — particularly for those like the NRA and big software companies who have adequate resources to defend themselves?

A few weeks ago, I heard a good explanation of this in an interview with ACLU's Executive Director, whom I'll directly quote, as he stated succinctly the reason why ACLU has a long history of defending everyone's free speech and free association rights:

[Our decision] to give legal representation to Nazis [was controversial].… It is not for the government's role to decide who gets a permit to march based on the content of their speech. We got lots of criticism, both internally and externally. … We believe these rights are for everyone, and we truly mean it — even for people we hate and whose ideology is loathsome, disgusting, and hurtful. [The ACLU can't be] just a liberal/left advocacy group; no liberal/left advocacy group would take on these kinds of cases. … It is important for us to forge a path that talks about this being about the rights of everyone.

Ultimately, fighting for software freedom is a social justice cause similar to that of fighting for free speech and other causes that require equal rights for all. We will always find groups exploiting those freedoms for ill rather than good. We, as software freedom activists, will have to sometimes grit our teeth and defend the rights to modify and improve software for those we otherwise oppose. Indeed, they may even utilize that software for those objectionable activities. It's particularly annoying to do that for companies that otherwise produce proprietary software: after all, in another realm, they are actively working against our cause. Nevertheless, either we believe the Four Software Freedoms are universal, or we don't. If we do, even our active political opponents deserve them, too.

I think we can take a good example from the ACLU on this matter. The ACLU, by standing firm on its core principles, now has, after two generations of work, developed the power to make impact on related causes. The ACLU is the primary organization defending immigrants who have been forcibly separated from their children by the USA government. I'd posit that only an organization with a long history of principled activity can have both the gravitas and adequate resources to take on that issue.

Fortunately, software freedom is already successful enough that we can do at least a little bit of that now. For example, Conservancy (where I work) already took a public position, early, in opposition of Trump's immigration policy because of its negative impact on software freedom, whose advancement depends on the free flow of movement by technologists around the world. Speaking out from our microphone built from our principled stand on software freedom, we can make an impact that denying software freedom to others never could. Specifically, rather than proprietarizing the license of projects to fight USA's Immigration and Customs Enforcement (ICE) and its software providers, I'd encourage us to figure out a specific FOSS package that we can prove is deployed for use at ICE, and use that fact as a rhetorical lever to criticize their bad behavior. For example, has anyone investigated if ICE uses Linux-based servers to host their otherwise proprietary software systems? If so, the Linux community is already large and powerful enough that if a group of Linux contributors made a public statement in political opposition to the use of Linux in ICE's activities, it would get national news attention here in the USA. We could even ally with the ACLU to assure the message is heard. No license change is needed to do that, and it will surely be more effective.

Again, this is how software freedom is so much like free speech. We give software freedom to all, which allows them to freely use and deploy the software for any purpose, just like hate groups can use the free speech microphone to share their ideas. However, like the ACLU, software freedom activists, who simultaneously defend all users equal rights in copying, sharing and modifying the software, can use their platform — already standing on the moral high ground that was generated by that long time principled support of equal rights — to speak out against those who bring harm to society in other ways.

Finally, note that the Four Software Freedoms obviously should never be the only laws and/or rules of conduct of our society. Just like you should be prevented from (proverbially) yelling Fire! in a crowded movie theater, you still should be stopped when you deploy Free Software in a manner that violates some other law, or commits human rights violations. However, taking away software freedom from bad actors, while it seems like a panacea to other societal ills, will simply backfire. The simplicity and beauty of copyleft is that it takes away someone's software freedom only at the moment when they take away someone else's software freedom; copyleft ensures that is the only reason your software freedom should be lost. Simple tools work best when your social justice cause is an underdog, and we risk obscurity of our software if we seek to change the fundamental simple design of copyleft licensing to include licensing penalties for other social justice grievances (— even if we could agree on which other non-FOSS causes warrant “copyleft protection”). It means we have a big tent for software freedom, and we sometimes stand under it with people whose behavior we despise. The value we have is our ability to stand with them under the tent, and tell them: “while I respect your right to share and improve that software, I find the task you're doing with the software deplorable.”. That's the message I deliver to any ICE agent who used Free Software while forcibly separating parents from their children.

August 29, 2018

Debugging an Rc<T> reference leak in Rust

The bug that caused two brown-paper-bag released in librsvg — because it was leaking all the SVG nodes — has been interesting.

Memory leaks in Rust? Isn't it supposed to prevent that?

Well, yeah, but the leaks were caused by the C side of things, and by unsafe code in Rust, which does not prevent leaks.

The first part of the bug was easy: C code started calling a function implemented in Rust, which returns a newly-acquired reference to an SVG node. The old code simply got a pointer to the node, without acquiring a reference. The new code was forgetting to rsvg_node_unref(). No biggie.

The second part of the bug was trickier to find. The C code was apparently calling all the functions to unref nodes as appropriate, and even calling the rsvg_tree_free() function in the end; this is the "free the whole SVG tree" function.

There are these types:

// We take a pointer to this and expose it as an opaque pointer to C
pub enum RsvgTree {}

// This is the real structure we care about
pub struct Tree {
    // This is the Rc that was getting leaked
    pub root: Rc<Node>,
    ...
}

Tree is the real struct that holds the root of the SVG tree and some other data. Each node is an Rc<Node>; the root node was getting leaked (... and all the children, recursively) because its reference count never went down from 1.

RsvgTree is just an empty type. The code does an unsafe cast of *const Tree as *const RsvgTree in order to expose a raw pointer to the C code.

The rsvg_tree_free() function, callable from C, looked like this:

#[no_mangle]
pub extern "C" fn rsvg_tree_free(tree: *mut RsvgTree) {
    if !tree.is_null() {
        let _ = unsafe { Box::from_raw(tree) };
                         // ^ this returns a Box<RsvgTree> which is an empty type!
    }
}

When we call Box::from_raw() on a *mut RsvgTree, it gives us back a Box<RsvgTree>... which is a box of a zero-sized type. So, the program frees zero memory when the box gets dropped.

The code was missing this cast:

    let tree = unsafe { &mut *(tree as *mut Tree) };
                                 // ^ this cast to the actual type inside the Box
    let _ = unsafe { Box::from_raw(tree) };

So, tree as *mut Tree gives us a value which will cause Box::from_raw() to return a Box<Tree>, which is what we intended. Dropping the box will drop the Tree, reduce the last reference count on the root node, and free all the nodes recursively.

Monitoring an Rc<T>'s reference count in gdb

So, how does one set a gdb watchpoint on the reference count?

First I set a breakpoint on a function which I knew would get passed the Rc<Node> I care about:

(gdb) b <rsvg_internals::structure::NodeSvg as rsvg_internals::node::NodeTrait>::set_atts
Breakpoint 3 at 0x7ffff71f3aaa: file rsvg_internals/src/structure.rs, line 131.

(gdb) c
Continuing.

Thread 1 "rsvg-convert" hit Breakpoint 3, <rsvg_internals::structure::NodeSvg as rsvg_internals::node::NodeTrait>::set_atts (self=0x646c60, node=0x64c890, pbag=0x64c820) at rsvg_internals/src/structure.rs:131

(gdb) p node
$5 = (alloc::rc::Rc<rsvg_internals::node::Node> *) 0x64c890

Okay, node is a reference to an Rc<Node>. What's inside?

(gdb) p *node
$6 = {ptr = {pointer = {__0 = 0x625800}}, phantom = {<No data fields>}}

Why, a pointer to the actual contents of the Rc. Look inside again:

(gdb) p *node.ptr.pointer.__0
$9 = {strong = {value = {value = 3}}, weak = {value = {value = 1}},  ... and lots of extra crap ...

Aha! There are the strong and weak reference counts. So, set a watchpoint on the strong reference count:

(gdb) set $ptr = &node.ptr.pointer.__0.strong.value.value
(gdb) watch *$ptr
Hardware watchpoint 4: *$ptr

Continue running the program until the reference count changes:

(gdb) continue
Thread 1 "rsvg-convert" hit Hardware watchpoint 4: *$ptr

Old value = 3
New value = 2

At this point I can print a stack trace and see if it makes sense, check that the refs/unrefs are matched, etc.

TL;DR: dig into the Rc<T> until you find the reference count, and watch it. It's wrapped in several layers of Rust-y types; NonNull pointers, an RcBox for the actual container of the refcount plus the object it's wrapping, and Cells for the refcount values. Just dig until you reach the refcount values and they are there.

So, how did I find the missing cast?

Using that gdb recipe, I watched the reference count of the toplevel SVG node change until the program exited. When the program terminated, the reference count was 1 — it should have dropped to 0 if there was no memory leak.

The last place where the toplevel node loses a reference is in rsvg_tree_free(). I ran the program again and checked if that function was being called; it was being called correctly. So I knew that the problem must lie in that function. After a little head-scratching, I found the missing cast. Other functions of the form rsvg_tree_whatever() had that cast, but rsvg_tree_free() was missing it.

I think Rust now has better facilities to tag structs that are exposed as raw pointers to extern code, to avoid this kind of perilous casting. We'll see.

In the meantime, apologies for the buggy releases!

rlife : a cellular automata library written in Rust

In the previous article, I’ve told you I was going to talk more about my personal project, rlife.

So rlife is a life library written in Rust. It aims at allowing to do manipulations on cellular automata, like computing the next generation of a CA, loading/saving a CA from/to a file, do various analysis on it (like locating the coordinates of a pattern, counting the number of living cells) and other manipulations. The main object of this library is the Gridthat represents the grid of the CA and it also stores all its properties (the file format used, the rulesets, the current size of the grid, etc…). This library could allow some developers to use CAs with a high level of abstraction and have the possibility to do many (in the future…) operations on it.

For now, this library is at a very early stage of development so it doesn’t support a lot of things. It supports only 2D life-like CAs and allows only to do basic operations on them (loading/saving in files (with a simple file format used by rlife), computing the next generation, getting/setting the state of a cell in the grid, creating a randomly filled grid of a given size and some more). But of course, it will support other CA types and file formats along with many more operations in the future.

rlife has also two kind of grids. On one hand, you have toroidal grids whose size are fixed but they deal with the edge effect with their toroidal property (i.e. it is as if the left and right borders were connected, as well as the top and bottom ones). On the other hand, you have “resizable” grids whose size will vary accordingly with the size of the pattern contained inside of it: if the pattern grows, the grid will grow and if it shrinks, the grid will shrink too.

I will soon make an evolution that will distinguish rlife with other similar libraries. Instead of doing computations with the CPU, rlife will rely on the GPU for the operations that can profit of its parallelism (and fortunately, most of the essential operations you can do on CAs can benefit from this). And it will use Vulkan via vulkano for this. Maybe Vulkan could also be used to directly render the grid on the screen when needed (if I understand correctly, GTK+ 4 could allow this). The first version of this improvement could land quite soon as I’ve almost finished writing a prototype for this and I will just have to integrate this in rlife.

Finally, I am developing this library in order to use it in a new application. I want to write a cellular automata application for GNOME. I should be able to start its development when rlife will have a minimal Vulkan support and I hope that this application could become something useful.

What ails GHashTable?

I promised a closer look at GHashTable and ways to improve it; here’s that look and another batch of benchmarks to boot.

This time around I’ve dropped most of the other tables from the plots, keeping only khash and adding results from my GLib branch and Rust’s HashMap, the latter thanks to a pull request from Josh Stone. These tables have closely comparable performance and therefore provide a good reference. Besides, every table tested previously is either generally slower or more memory-hungry (or both), and including them would compress the interesting parts of the plot.

I’ll try to be brief this time¹. For more background, check out my previous post.

Bad distribution

First and foremost, the distribution was terrible with densely populated integer keyspaces. That’s taken care of with a small prime multiplier post-hash.

Peak memory waste

Previously, we’d resize by allocating new arrays and reinserting the entries, then freeing the old arrays. We now realloc() and rearrange the entries in place, lowering peak memory use by about a third. This can prevent going into swap or even crashing out on a memory-constrained system.

Overall memory waste

If you’ve got a sharp eye, you’ll notice that overall memory consumption is lower now too. Whereas the old implementation always made space for 64-bit keys and values, the new one will allocate 32 bits when possible and switch to bigger entries on demand. In the above test, the keys are integers in the range [0 .. 2³²-1], reducing memory consumption by 20% overall. If values had been in [0 .. 2³²-1] too, the reduction would’ve amounted to 40%. A caveat though — negative integers (e.g. from GINT_TO_POINTER()) still require 64 bits due to two’s complement/sign extension.

Load factor mistargeting

When GHashTable is subjected to churn/aging, it will accumulate tombstones, and eventually the sum of entries and tombstones will eclipse the maximum load, resulting in a cleanup. Since a cleanup is just a reinsertion in place, it’s handled similarly to a resize, and we take the opportunity to pick a better size when this happens. Unfortunately, the grow threshold was set at .5 if the table got filled up the rest of the way by tombstones, resulting in post-grow load factors as low as .25. That’s equal to the shrink threshold, so with a little (bad) luck it’d shrink back immediately afterwards.

I changed the threshold to .75, so the load factor intervals (not counting tombstones) now look like this:

  • <.25 → shrink immediately → .5
  • [.25 .. .75] → no change
  • [.75 .. .9375] → grow on cleanup → [.375 .. .46875]
  • >.9375 → grow immediately → .46875

This seems like a more reasonable stable range with less opportunity for fluctuation and waste, and there’s still lots of headroom for tombstones, so cleanups aren’t too frequent.

But it’s slower now?

In some cases, yes — can’t be helped. It’s well worth it, though. And sometimes it’s faster:

This particular run uses less memory than before, which is puzzling at first, since keys and values are both pointers. A look at the test’s smaps reveals the cause:

01199000-08fb8000 rw-p 00000000 00:00 0 [heap]

The heap happened to be mapped in the lower 4GiB range, and GHashTable can now store 32-bit entries efficiently. That means pointers too.

I caught khash doing something interesting in this benchmark. Some time after the aging cycle has started (1), it initiates a tombstone cleanup. In this case it decides to grow the table simultaneously, starting at (2). This could be an example of the kind of load factor mistargeting I mentioned above — certainly it would have a very low load factor for the remainder of the test.

Robin Hood to the rescue?

Short answer: No. Long answer:

Rust uses Robin Hood probing, and the linear shifts required for insertion and deletion start to get expensive as the table fills up. It also came in last in a lookup-heavy load I ran. On the other hand it avoids tombstones, so there’s no need for periodic cleanups, and deletions will make it progressively faster instead of slower. GHashTable’s quadratic probing seems to hold a slight edge, albeit workload-dependent and, well, slight. In any case, I couldn’t find a compelling reason to switch.

What about attack resistance?

The improved GHashTable is much more resistant to accidental misbehavior. However, it wouldn’t be too hard to mount a deliberate attack resulting in critically poor performance². That’s what makes Rust’s HashMap so interesting; it gets its attack resistance from SipHash, and if these benchmarks are anything to go by, it still performs really well overall. It’s only slightly slower and adds a reasonable 4 bytes of overhead per item relative to GHashTable, presumably because it’s storing 64-bit SipHash hashes vs. GHashTable’s 32-bit spit and glue.

I think we’d do well to adopt SipHash, but unfortunately GHashTable can’t support keyed hash functions without either backwards-incompatible API changes or a hacky scheme where we detect applications using the stock g_str_hash() etc. hashers and silently replace them with calls to corresponding keyed functions. For new code we could have something like g_hash_table_new_keyed() accepting e.g. GKeyedHasher.

A better option might be to add a brand new implementation and call it say, GHashMap — but we’d be duplicating functionality, and existing applications would need source code changes to see any benefit.


¹ Hey, at least I tried.

² If you can demo this on my GLib branch, I’ll send you a beer dogecoin nice postcard as thanks. Bonus points if you do it with g_str_hash().

August 28, 2018

Solaris 11.4 released

Congrats to my colleagues in the Solaris team who released Solaris 11.4 today. Despite the 11.x moniker, this is actually a major Solaris release; Oracle has just decided to go down the perpetual macOS X / Windows 10 version numbering route from now on. (This development is unlikely to faze Solaris veterans, who have been using SunOS 5.x since 1992.)

Being the first major Solaris release in 7 years, it’s also the first to ship with a GNOME 3 desktop. So thanks, as always, to everyone in the GNOME and related FOSS communities who made that possible.

On a personal note, this version also ships the last significant Solaris project I worked on, which during development was known as the Analytics WebUI, but now goes by the marketing-approved moniker of the Observability Tools System Web Interface. It’s always nice when something you’ve worked on sees the light of day, and it’s even nicer when you know somebody else will have to deal with any complaints 😊

GTK+ and the application id

tl;dr: If you want to be sure your application will be displayed with the correct icon under different Wayland compositors make sure that your GApplication (or GtkApplication) uses

g_set_prgname(your_g_application_id);

on GTK+3. On GTK+4 this is handled for you.

Details: While working on touch based window switching for the Librem5 I noticed that lots of the GNOME application did not end up with a proper icon when using g_desktop_app_info_new (desktop_id). The desktop_id is determined from the Wayland xdg surface's app_id as specified by in Wayland's xdg-shell protocol.

The protocol says:

The compositor shell will try to group application surfaces together
by their app ID. As a best practice, it is suggested to select app
ID's that match the basename of the application's .desktop file.
For example, "org.freedesktop.FooViewer" where the .desktop file is
"org.freedesktop.FooViewer.desktop".

It's even more explicit about the relation of the app_id to the D-Bus service name:

For D-Bus activatable applications, the app ID is used as the D-Bus
service name.

So why does this currently fail? It's because GTK+3 historically uses g_get_prgname() to set the app_id and this defaults to application's basename. But what we rather want is

g_application_id == D-Bus service name == $(basename desktop_file_path .desktop) == xdg app_id

There were patches by Jonas Ådahl to fix this but those were partially reverted since it broke existing applications. Now with GTK+4 around the corner we can fix this. See the migration docs.

This will also allow us to get rid of all the rename-desktop-file in the flatpak manifests too.

(The reason why this currently works in gnome-shell is because there's a private protocoll between GTK+ and GNOME Shell that (among other things) works around this).

Update: to check what app_id Wayland is seeing use:

WAYLAND_DEBUG=1 your_program |& grep 'xdg_toplevel@[0-9]\+\.set_app_id'

August 27, 2018

What this blog will become after GSoC

Hello everyone, I am back after some weeks of vacation!

So GSoC 2018 officially ended last week but I’ve decided to keep using this blog for posting news of the work I will be doing for some time (i.e. until I find a better place for this).

What will I talk about?

  • Fractal: I will continue to document my contribution for Fractal as I did during GSoC
  • I will blog about the work I will do on a project of mine: rlife
  • I am planning on starting a new project that will be a GNOME application, built on the top of rlife, so I will blog about it when I will start to work on it

I will soon write another blog post in order to talk more about rlife.

Realtek on the LVFS!

For the last week I’ve been working with Realtek engineers adding USB3 hub firmware support to fwupd. We’re still fleshing out all the details, as we also want to also update any devices attached to the hub using i2c – which is more important than it first seems. A lot of “multifunction” dongles or USB3 hubs are actually USB3 hubs with other hardware connected internally. We’re going to be working on updating the HDMI converter firmware next, probably just by dropping a quirk file and adding some standard keys to fwupd. This will let us use the same plugin for any hardware that uses the rts54xx chipset as the base.

Realtek have been really helpful and open about the hardware, which is a refreshing difference to a lot of other hardware companies. I’m hopeful we can get the new plugin in fwupd 1.1.2 although supported hardware won’t be available for a few months yet, which also means there’s no panic getting public firmware on the LVFS. It will mean we get a “works out of the box” experience when the new OEM branded dock/dongle hardware starts showing up.

August 26, 2018

About Flatpak installations

If you have tried Flatpak, you probably know that it can install apps per user or system-wide.  Installing an app system-wide has the advantage that all users on the system can use it. Installing per-user has the advantage that it doesn’t require privileges.

Most of the time, that’s more than enough choice. But there is more.

Standard locations

Before moving on, it is useful to briefly look at where Flatpak installs apps by default.

flatpak install --system flathub org.gnome.Todo

When you install an app like this. it ends up in in /var/lib/flatpak. If you instead use the –user option, it ends up in ~/.local/share/flatpak. To be 100% correct, I should say $XDG_DATA_HOME/flatpak, since Flatpak does respect the XDG basedir spec.

Anatomy of an installation

Flatpak calls the places where it installs apps installations. Every installation has a few subdirectories that are useful to know:

  • repo – this is the OSTree repository where the files for the installed apps and runtimes reside. It is a single repository, so all the apps and runtimes that are part of the same installation get the benefit of deduplication via content-addressing.
  • exports – when an app is installed, Flatpak extracts some files that need to be visible to the outside world, such a desktop files, icons, D-Bus services files, and this is where they end up.
  • appstream – a flatpak repository contains the appstream data for the apps it contains as a separate branch, and Flatpak extracts it on the client-side for consumers like KDE’s Discover or GNOME Software.
  • app, runtime – the deployed versions of apps and runtimes get checked out here. Diving deeper, you see the files of an app in app/org.gimp.GIMP/current/active/files. This directory is what gets mounted in the sandbox as /app if you run the GIMP.

Custom installations

So far, so good. But maybe you have a setup with dozens of machines, and have an existing setup where /opt is shared. Wouldn’t it be nice to have a flatpak installation there, instead of duplicating it in /var on every machine ? This is where custom installations come in.

You can tell Flatpak about another place to  install apps by dropping a file in /etc/flatpak/installations.d/. It can be as simple as the following:

[Installation "bigleaf"]
Path=/opt/flatpak
DisplayName=bigleaf

See the flatpak-installation man page for all the details about this file.

GNOME Software currently doesn’t know such custom installations, and you will have to adjust the shell glue in /etc/profile.d/flatpak.sh for GNOME shell to see apps from there, but that is easy enough.

A patch to make flatpak.sh pick up custom installations automatically would be a welcome contribution!

Apps on a stick

Flatpak has a few more tricks up its sleeve when it comes to sharing apps between machines. A pretty cool one is the recently added create-usb command. It lets you copy one (or more) apps on a usb stick, and install it from there on another machine. While trying this out, I hit a few hurdles, that I’ll briefly point out here.

To make this work, Flatpak relies on an extra piece of information about the remote, the collection ID. The collection ID is a property of the actual remote repository. Flathub has one, org.flathub.Stable.

To make use of it, we need to add it to the configuration for the remote, like this:

$ flatpak remote-modify --collection-id=org.flathub.Stable flathub
$ flatpak update

If you don’t add the collection ID to your remote configuration, you will be greeted by an error saying “Remote ‘flathub’ does not have a collection ID set”. If you omit the flatpak update, the error will say “No such branch (org.flathub.Stable, ostree-metadata) in repository”.

Another error you may hit is “fsetxattr: Operation not supported”.  I think the create-usb command is meant to work with FAT-formatted usb sticks, so this will hopefully be fixed soon. For now, just format your usb stick as EXT4.

After these preparations, we are ready for:

$ flatpak --verbose create-usb /run/media/mclasen/Flatpak org.gimp.GIMP

which will take some time to copy things to the usb stick (which happes to be mounted at /run/media/mclasen/Flatpak) . When this command is done, we can inspect the contents of the OSTree repository like this:

$ flatpak remote-ls file:///run/media/mclasen/Flatpak/.ostree/repo
Ref 
org.freedesktop.Platform.Icontheme.Adwaita
org.freedesktop.Platform.VAAPI.Intel 
org.freedesktop.Platform.ffmpeg 
org.gimp.GIMP 
org.gnome.Platform

Flatpak copied not just the GIMP itself, but also runtimes and extensions that it uses. This ensures that we can install the app from the usb stick even if some of these related refs are missing on the target system.

But of course, we still need to see it work! So I uninstalled the GIMP, disabled my network, plugged the usb stick back in, and:

$ flatpak install --user flathub org.gimp.GIMP
0 metadata, 0 content objects imported; 569 B transferred in 0 seconds

flatpak install: Error updating remote metadata for 'flathub': [6] Couldn't resolve host name
Installing in user:
org.gimp.GIMP/x86_64/stable flathub 1eb97e2d4cde
permissions: ipc, network, wayland, x11
file access: /tmp, host, xdg-config/GIMP, xdg-config/gtk-3.0
dbus access: org.gtk.vfs, org.gtk.vfs.*
Is this ok [y/n]: y
Installing for user: org.gimp.GIMP/x86_64/stable from flathub
[####################] 495 metadata, 4195 content objects imported; 569 B transferred in 1 seconds
Now at 1eb97e2d4cde.

Voilà, an offline installation of a Flatpak. I left the error message in there as proof that I was actually offline :) A nice detail of the collection ID approach is that Flatpak knows that it can still update the GIMP from flathub when I’m online.

Coming soon, peer-to-peer

This post is already too long, so I’ll leave peer-to-peer and advertising Flatpak repositories on the local network via avahi for another time.

Until then, happy Flatpaking! 💓📦💓📦

 

Using host Nvidia driver with Flatpak

This post is going to be a bit of a deep dive into how GL driver extensions work on Flatpak, why they work the way they do, and how we can best use them moving forward. Some of this information is useful for distro packagers and of course just anybody interested in Flatpak details.

Just as a quick recap Flatpak does not use host libraries. This is required to be portable and reproducable on nearly any Linux distro. This even applies to very important libraries like libgl but this is rarely a problem as Mesa supports a wide range of hardware. The exception to this is properietary drivers which for desktop users means Nvidia. This isn’t necessarily a problem since we can just package more drivers but Nvidia’s userspace drivers must be the same version as the kernel driver which becomes a bit of a packaging nightmare because then we have to package every version that is in use.

The logical solution to this is for the Flatpak GL driver to come from the same place that your host GL driver did. Flatpak supports “unmanaged extensions” allowing loading extensions installed externally into /var/lib/flatpak/extension and $XDG_DATA_HOME/flatpak/extension. This does make my previous paragraph a lie but it places trust in the host to be responsable and understand how ABI works to keep things portable which we will get into later.

Before we get into packaging the Nvidia driver on the host for this lets look at how the GL driver extension point specifically works. This extension point is defined as such:

'Extension org.freedesktop.Platform.GL':
    versions: "18.08;1.4"
    version: "1.4"
    directory: "%{lib}/GL"
    subdirectories: "true"
    no-autodownload: "true"
    autodelete: "false"
    add-ld-path: "lib"
    merge-dirs: "vulkan/icd.d;glvnd/egl_vendor.d"
    download-if: "active-gl-driver"
    enable-if: "active-gl-driver"

I will point out the important parts of this later but I will just cover what active-gl-driver means really quick. The -if properties run custom checks within flatpak to know if an extension should be downloaded or enabled. This check specifically looks up your nvidia version from the kernel and maps that to a string e.g. nvidia-396-54. What your current version is can be checked with flatpak --gl-drivers where you will notice host and default are always inserted. This value can also be overriden with the env var FLATPAK_GL_DRIVERS.

So here is a real world example of me packaging this on Fedora: negativo17/nvidia-driver. The path it ends up installed into is: …/org.freedesktop.Platform.GL.host/x86_64/1.4. As you can see host is used here because it is always in the default driver list and thus will always get used first. The 1.4 version might also be confusing since the 1.4 runtime is ancient and nobody uses it. The reason this is used is because the Nvidia driver has no ABI requirements outside of libc basically and will run on every freedesktop runtime that exists so far. That guarentee does not exist for all drivers and must be used cautiously. The layout of files ends up being this because of merge-dirs and add-ld-path shown above:

├── glvnd
│   └── egl_vendor.d
│       └── 10_nvidia.json
├── lib
│   ├── libGLX_nvidia.so.396.54
│   └── All libs omitted for brevity…
├── OpenCL
│   └── vendors
│       └── nvidia.icd
└── vulkan
    └── icd.d
        └── nvidia_icd.json

That should cover all the details for host extensions but this extension point is also still very useful for normal flatpaked extensions. There is a MR for mesa-git that would end up being used by setting FLATPAK_GL_DRIVERS=mesa-git to try out newer versions.

If you are a distro packager I do believe it makes sense to start making a nvidia package for the Flatpak extension especially for beta versions. This should probably not be used to package host versions of Mesa though because they link to host libraries outside of libc. There is some work done to make this possible but most distro packages are not acceptable for this usage and there is rarely an upside as fdo 18.08 might even have newer Mesa versions than some distros and can get updates.

August 25, 2018

GUADEC 2018

Its has been more than a month since Guadec wrapped up, though I’ve been busy and did not manage to write my report till now.

Traveling

I was a bit anxious about the travel, It was my first time flying and not only that but I had to spent the night in the Airport due to departure being at 6am. The flights went smoothly and I arrived at Málaga in the evening. Afterwards I took a bus to get to Almeria, it was a pleasant surprise to find out that other gnomies were also on board.

View of Málaga from a plain, right before landing.

Talks

There were many exciting talks. In particular I really enjoyed the Auditorium track, on Saturday, about Flatpak and Buildstream. At Sunday I attended Carlos Soriano’s talk Devops for GNOME and Tobias Bernard’s Designing GNOME Mobile. I was really looking forward to Federico’s talk too, sadly couldn’t make it in time and watched it online instead.

BoF days

On Monday morning I attended the Librem 5 BoF along with Julian Sparber in order to talk about Fractal and the Messages Application with the folks from Purism. We discussed Fractal’s upcoming features and plans going forward. Afterwards I head over to the Gitlab Workshop to help with whatever I could. During that time Jean Felder and I debugged an issue and we got Music Development build to run along side Music stable/system install succesfully!

The highlight of Tuesday was the Theming BoF. It was really interesting so many different groups attending and discussing the issues the Design Team brought forward. App developers, Platform developers, Downstream, Designers and even Elementary folks were there to give their opinion and talk about their experience and how they deal with such issues in Elementary. Tobias Bernard and Elementary Summarized them both really well in their posts here, and here. Cassidy James also took great notes and added them to the wiki page of the BoF here.

Jakub presenting the new Icon designs.

 

Wednesday was also packed. In the morning we had the What is a GNOME App BoF. More on that another time though, Carlos and Tobias are working on a proper Proposal. After that Tobias and I took some time to work on the blocker issues, before making the first “public” release of the new Podcasts app for GNOME. We’ve been working on the app for the past year, and its now available on Flathub!

Social Events

By far the thing I enjoyed the most from GUADEC was the social events. Talking with people about all sorts of thing and seeing perspectives of others from all around the world was a magical experience and though-provoking. I don’t really like going to the beach, but I loved both the beach party and the Sandcastle BoFs. The visit to the Alcazaba Castle and the Flamenco show afterwards was absolutely delightful too.

 

View of the Alcazaba Castle Walls.

 

 

A big thanks to the Foundation

My trip was sponsored by the GNOME Foundation. Thanks a lot to the volunteers at the Travel committee for all their hard work and making it possible for me to attend GUADEC!

August 24, 2018

A GNOME dev enters an Akademy and…

Felipe Borges and me recently went to Akademy after a conversation between the two of us three days before the KDE conference that went like:

  • “Ugh, seems there is no GNOMEr going to Akademy…”
  • “Let’s go?”
  • “Let’s go”

And so three days later we traveled to Wien to meet with the KDE community. On arrival, we were pleased by a friendly and joyful ambient on the pre-registration party, which had no registration at all! We were happy to know these issues don’t happen only at GUADEC.

I only knew from before Aleix Pol, the vice-president, Lydia Pintscher, the president, and Alex Fiestas, a KDE dev I would have paid to had him as flatmate when I was at university 😜. Fortunately, we started meeting all the people around in no time .

Shortly after revealing I was part of GNOME I was tagged widely as the “GNOME spy”, and that is not far from the reality! I was there to learn what they do better, and of course finding ways that we can collaborate.

What KDE & Akademy does better

  • CoC and photo policy. They have all of this already sorted out, we are being a little late at GNOME. However, with my board hat on, we (mostly others at the board) have worked a lot to make this a reality and I’m confident the online CoC will be created relatively soon.
  • Non tech talks. I felt Akademy had some more interested talks than GUADEC, at least for someone that is already more or less aware of the technical happenings at GNOME. Things like 2 talks on newcomers experience, a talk on running events locally, privacy software, different board/community reports, etc.
  • Trainings. They had training in public speech and also training on how to behave constructively in a community. Both sound like excellent ideas to be done at GNOME.
  • A process to include 3rd party software. It’s called “Incubator” and it’s being handled by Aleix Pol. This is something we lack, and there is no easy way to convert a 3rd party project to be a GNOME project, therefore losing potential new GNOME developers on the way. The “World” group at GitLab partially alleviates this, but doesn’t fix it.
  • Marketing contractors. They now have marketing people available for the community. It’s something we lack, although the engagement team does a lot of work to improve that I have the feeling it would be useful to have someone with this background working on certain things at GNOME.
  • Board turnover. They have a 3 year 1/3 turnover of the board. This is quite good to build long term goals.
  • Compartmentalization of projects. Not sure how useful it would be for us, they have separated plasma apps, rest of apps, etc. So they have a defined product called Plasma and can be provided as is to OEMs as a branded product done by KDE.
  • Gender and age diversity. I was gladly surprised by the amount of women and age diversity at KDE. Things like the LGBT dinner are good ways to help the situation, and I hope it keeps improving at GNOME.

There are other areas KDE does better because of not-easily-fixable handicaps at GNOME such as QT documentation being better than gtk+, etc. But! I also realized how much GNOME does better in some other areas, something you don’t realize until you see what other projects are doing.

What GNOME does better

There are three main areas that struck me as game changers.

  • Newcomers experience. Seriously, the difference is huge. Thanks to Flatpak, Builder, GitLab and GitLab CI we stepped up the game massively. Not only compared to KDE but also compared to any other complex software or software organization I have seen so far.
  • GitLab. KDE uses a mix of tools such as Phabricator, Bugzilla, CLI tools, Travis for CI, etc. For us, switching to GitLab (or any tool that would integrate everything into a single tool as good as GitLab) has been a massive win both in sysadmin maintenance and in development workflows.
  • Focus.  Historically KDE has had a quite broad focus, several projects with a variety of designs and technical practices has been part of the product. GNOME puts a lot of effort in making a clear focus in order to improve quality, reduce the UX variety  and make sure we walk more or less in the same design direction, technical practices, etc. Seems KDE is trying to improve this now too, which is nice to hear.

There are other areas that GNOME does better just because of handicaps KDE might has such as not being default in most of well known distributions or being less enterprise-ready (work we do a lot at Red Hat for GNOME in order to comply with US regulations, etc.).

I tried to give my advice and offered my help on these areas from now on, as I believe what GNOME uses would benefit KDE and in the same way would benefit GNOME by using the same tooling as our colleagues at KDE.

What KDE wants to work on

Flatpak. Yes! Looks like KDE has a wide interest on use more and more Flatpak. Some aspects I could gather from them is that the community driven nature and FOSS stack of Flatpak aligns with the values of the KDE community. Seems they also appreciate the technical thought and technology behind that was put into Flatpak. Alex Larsson is truly an amazing dev.

However, they would appreciate the Flatpak community to proactively approaching KDE to help use Flatpak more, and it’s something I would like to see more too. I proposed to do a hackfest together to collaborate improving the usage of Flatpak and collaborate with the community as a first step. So hopefully there will be one soon!

Wayland. They really want to improve the Wayland status at KDE. At GNOME we have some (1.5?) developers working on it and at KDE they have one working on it. So I proposed to meet with the GNOME developers to share advice and see how the collaboration in Wayland freedesktop for new APIs such as screenshot, screen sharing, color picking, etc. can be improved. Hopefully soon we can all meet to discuss these out.

And yes, it was pretty interesting

I  also met and chat with Albert Vaca, the creator of KDE connect. He actually uses GNOME but only attends KDE events because he loves the community, which is quite interesting to me.

KWin maintainer Roman Gilg who I chatted with about Wayland and further collaboration between GNOME and KDE. Roman also tried to save me on Saturday for the lack of parties (I hope next Akademy there is a party on Saturday. GUADEC social events were amazing this year thanks to Ismael).

Slimbook CEO Alejandro López who I chatted with about hardware, OEMs and opportunities around that. Excellent and fun as always.

Ben Cooksley who is this amazing free time sysadmin contributor that runs the whole KDE infrastructure and we chatted about CI setup and funding.

The newcomers experience leader Neofytos Kolokotronis who is passionate about improving the newcomers on-boarding.

And last but not least, I interchanged with Aleix and Lydia valuable advice on how the board, community and funding is ran in both GNOME and KDE. I also ended having a passionate argument with Aleix (as always 😜) that ended up with a hug.

We have already some email threads and discussion on these ideas on the work, going to the conference is already paying off.

Overall, nice conference, nice people, and I’m thankful to all of you at KDE!

 

Thanks the GNOME Foundation for sponsoring both Felipe’s and my trip.

sponsored-by-foundation-round.png

August 23, 2018

Fun with SuperIO

While I’m waiting back for NVMe vendors (already one tentatively onboard!) I’ve started looking at “embedded controller” devices. The EC on your laptop historically used to just control the PS/2 keyboard and mouse, but now does fan control, power management, UARTs, GPIOs, LEDs, SMBUS, and various tasks the main CPU is too important to care about. Vendors issue firmware updates for this kind of device, but normally wrap up the EC update as part of the “BIOS” update as the system firmware and EC work together using various ACPI methods. Some vendors do the EC update out-of-band and so we need to teach fwupd about how to query the EC to get the model and version on that specific hardware. The Linux laptop vendor Tuxedo wants to update the EC and system firmware separately using the LVFS, and helpfully loaned me an InfinityBook Pro 13 that was immediately disassembled and connected to all kinds of exotic external programmers. On first impressions the N131WU seems quick, stable and really well designed internally — I’m sure would get a 10/10 for repairability.

At the moment I’m just concentrating on SuperIO devices from ITE. If you’re interested what SuperIO chip(s) you have on your machine you can either use superiotool from coreboot-utils or sensors-detect from lm_sensors. If you’ve got a SuperIO device from ITE please post what signature, vendor and model machine you have in the comments and I’ll ask if I need any more information from you. I’m especially interested in vendors that use devices with the signature 0x8587, which seems to be a favourite with the Clevo reference board. Thanks!

2018-08-23 Thursday.

  • Up in the night. Rather encouraged to read Bradley Kuhn's take on Gerv Markham's movement into the second phase of his mission to Glorify God, and enjoy him forever cf. going home. I'm particularly encouraged that someone in the feminist wing can have learned something new of the meaning of tolerance and mutual respect - it cheered my early morning. Pleased that at least in an earlier era of the web when the ochlocracy was only starting to tool up - that good people stood up and kept Gerv's head off the plate, unlike another notable historic critic of marriage ethics. Will really miss meeting up with him at FOSDEMs variously, and his refreshing perspctive on many topics and friendly advice.
  • Slideware hackery.

August 22, 2018

Getting back into Outreachy

Outreachy is a great organization that helps women and other minorities get involved in open source software. (Outreachy was formerly the GNOME Outreach Program for Women.) I've mentored several cycles in Outreachy, doing usability testing with GNOME. I had a wonderful time, and enjoyed working with all the talented individuals who did usability testing with us.

I haven't been part of Outreachy for a few years, since I changed jobs. I have a really hectic work schedule, and the timing hasn't really worked out for me. Outreachy recently posted their call for participation in the December-March cycle of Outreachy. December to March should be a relatively stable time on my calendar, so this is looking like a great time to get involved again.

I don't know if GNOME plans to hire interns for the upcoming cycle of Outreachy, at least for usability testing. But I am interested in mentoring if they do.

Following conversations with Allan Day and Jakub Steiner, from GNOME Design, I'm thinking about changing the schedule we would use in usability testing. In previous cycles, I set up the schedule like a course on usability. That was a great learning experience for the interns, as they had a ramp-up in learning about usability testing before we did a big usability project.

But Allan and I would like to get interns involved more quickly. Also, Allan would prefer to have testing be more integrated to a current design project. Allan has a great point in saying, "rather than releasing something and having it tested weeks or months later, we need to be testing what we are working on right now."

My idea for the next cycle is to accelerate usability testing! I would imagine restructuring the internship so applicants would do a "crash course" in usability concepts the first week, then start doing usability tests immediately after that. Learn by doing!

For a 13 week internship, I imagine an intern or interns could spend almost the whole time leading usability tests, especially focusing on "you need five testers for an iterative process" so they are doing basically the same test throughout the internship, against different iterations of different designs. That would provide more immediate feedback to designs you are working on at the time.

A possible schedule could be:
  1. Quick study of usability testing background and methods
  2. Review test needs with Design Team; plan test #1
  3. Execute test #1
  4. Discuss results with Design Team
  5. Plan test #2
  6. Execute test #2
  7. Discuss results with Design Team
  8. Plan test #3
  9. Execute test #3
  10. Discuss results with Design Team
  11. Plan test #4
  12. Execute test #4
  13. Discuss results with Design Team; wrap-up

Based on that schedule, we could fit 4 usability test iterations in 13 weeks. We might do as many as 5 if things accelerate toward the end, such as shorter turnaround in refining the design. This depends on the time required to generate a new design based on the input of the usability tests.

The key would be to connect the design process and timeline to the internship timeline. And then to be realistic in testing. For example, we might have the intern do a paper prototype test one week, then spend the next week discussing results and figuring out the next iteration, and the intern does another paper prototype test, etc.

Paper prototypes are probably the fastest to turn around, because they don't require coding. But if there's a working interface or some animatic, the intern could do usability tests based on iterations of that.

For a simpler paper prototype, a more aggressive schedule could be:
  1. Quick study of usability testing background and methods
  2. Review test needs with Design team; plan test #1
  3. Execute test #1
  4. Discuss results with Design Team; plan test #2
  5. Updated prototype; execute test #2
  6. Discuss results with Design Team; plan test #3
  7. Updated prototype; execute test #3
  8. Discuss results with Design Team; plan test #4
  9. Updated prototype; execute test #3
  10. Discuss results with Design Team; plan test #5
  11. Updated prototype; execute test #3
  12. Discuss results with Design Team; plan test #6
  13. Wrap-up

But that may not be realistic, even for a paper prototype. There's still logistical issues, such as finding test volunteers. We'll have to adjust the schedule as we go. I think the thing to remember is we'd target at least 4 and maybe 6 usability tests during the Outreachy cycle.

If you're interested in participating in Outreachy, the anticipated schedule is:

Sept. 10, 2018 Applications and the eligibility check opens. Applicants can see and apply to listed projects.
Oct. 2, 2018 Last date for new mentoring community listings
Oct. 9, 2018 Last date for new internship project listings
Oct. 23, 2018 Application deadline
Oct. 30, 2018 Late application deadline
Nov. 5, 2018 Deadline for mentors to select interns
Nov. 14, 2018 Accepted interns announced
Dec. 4, 2018 to March 4, 2019 Internships period

2018-08-22 Wednesday.

  • Mail chew, bits with Victor, partner call. Pleased to see Jos' thoughtful analysis of open-source engineering and the value of communicating directly with the relevant engineers doing the development.
  • Plugged away at this & that, interview with an old friend. ESC call - sadly the alternating Thursday / Wednesday slot seems under-loved.

New Videos & New Opportunities

Flatpak 1.0 has released which is a great milestone for the Linux Desktop. I was asked at GUADEC whether a release video could be in place. In response, I spontaneously arranged to produce a voice-over with Sam during the GUADEC Video Editing BoF. Since then, I have been storyboarding, animating and editing the project in Blender. The music and soundscape has been produced by Simon-Claudius who has done an amazing job. Britt edited the voice-over and has lended me a great load of rendering power (thanks Britt!).

The Flatpak Video
https://www.youtube.com/watch?v=jDVCITRWGgs

The GNOME 3.30 Release Video is also on its way, with release due at September the 5th. The video will be the 10th release video I have been involved since i started (time flies!).

Future

From 2019 I’ll be looking for full-time opportunities to continue working with UX, User Onboarding and Motion Graphics in FOSS (see also my website). This summer I graduated as MSc. Medialogy at Aalborg University in Denmark. Since then, I have been working for Aalborg University over the summer to design Learning Analytics UI. In parallel I have enrolled in The Drawing Academy to engage full-time in the visualization craft until 2019.

My past six years of world-wide remote collaboration to GNOME have been greatly rewarding. Apart from the release videos, I have designed the GNOME Newcomer Guide with Carlos, working on Polari UX in Google Summer of Code and most recently engaged in the Developer Center Initiative.

I am on the lookout for short-term or long-term occupation which allow me to continue my contributions in the GNOME and FOSS ecosphere.  Don’t be afraid to get in touch! :-)

Software Freedom Ensures the True Software Commons

[ A similar version was crossposted on Conservancy's blog. ]

Proprietary software has always been about a power relationship. Copyright and other legal systems give authors the power to decide what license to choose, and usually, they choose a license that favors themselves and takes rights and permissions away from others.

The so-called “Commons Clause” purposely confuses and conflates many issues. The initiative is backed by FOSSA, a company that sells materiel in the proprietary compliance industrial complex. This clause recently made news again since other parties have now adopted this same license.

This proprietary software license, which is not Open Source and does not respect the four freedoms of Free Software, seeks to hide a power imbalance ironically behind the guise “Open Source sustainability”. Their argument, once you look past their assertion that the only way to save Open Source is to not do open source, is quite plain: If we can't make money as quickly and as easily as we'd like with this software, then we have to make sure no one else can as well.

These observations are not new. Software freedom advocates have always admitted that if your primary goal is to make money, proprietary software is a better option. It's not that you can't earn a living writing only Free Software; it's that proprietary software makes it easier because you have monopolistic power, granted to you by a legal system ill-equipped to deal with modern technology. In my view, it's a power which you don't deserve — that allows you to restrict others.

Of course, we all want software freedom to exist and survive sustainably. But the environmental movement has already taught us that unbridled commerce and conspicuous consumption is not sustainable. Yet, companies still adopt strategies like this Commons Clause to prioritize rapid growth and revenue that the proprietary software industry expects, claiming these strategies bolster the Commons (even if it is a “partial commons in name only”). The two goals are often just incompatible.

At Software Freedom Conservancy (where I work), we ask our projects to be realistic about revenue. We don't typically see Conservancy projects grow at rapid rates. They grow at slow and steady rates, but they grow better, stronger, and more diverse because they take the time to invite everyone to get involved. The software takes longer to mature, but when it does it's more robust and survives longer.

I'll take a bet with anyone who'd like. Let's pick five projects under the Affero GPL and five projects under the Commons Clause, and then let's see which ones survive longer as vibrant communities with active codebases and diverse contributors.

Finally, it's not surprising that the authors chose the name “Commons”. Sadly, “commons” has for many years been a compromised term, often used by those who want to promote licenses or organizational models that do not guarantee all four freedoms inherent in software freedom. Proprietary software is the ultimate tragedy of the software commons, and while it's clever rhetoric for our opposition to claim that they can make FLOSS sustainable by proprietarizing it, such an argument is also sophistry.

Desktop icons goes beta

Hi all,

Today I have good news for “classic mode” users and those used to desktop icons.

Context & random thoughts

As you might know, few months ago we removed the handling of desktop icons in Nautilus. As mentioned in the past, the desktop icons code was blocking at that point any further major development of Nautilus, and the quality was not up to our standards that we want to deliver.

The most important responsibility I have as (now one of the two) maintainer is to ensure the project progresses in its main goal, which is being an excellent file manager app. This includes building and maintaining a healthy community of contributors around, which I’m proud to have. I take these points very seriously, and as such, sometimes I have to take hard decisions to make sure this is achieved. When put into a position where either the project stagnates in its main goal and lose all interest from community contributors I have no doubt what is the path to go. And sometimes those decisions has to be taken shortly, after years trying to overcome the problem. Sometimes that happens when no others resources/time/people for providing a complete drop-in alternative are available.

This is also true for users, when put into the choice of having reliable file operations, search not blocking the computer, proper views, etc.  the decision seems clear.

Good news is that this all is paying off! The gtk4 port of Nautilus is now almost ready, we are having a hackfest soon with gtk+ developers to plan putting the new views in Nautilus, the work on the reliable file operations and search is now free to continue, and we had put a testing framework that consolidates this effort. The community of contributors has also been working as ever, and the results are clear in the Nautilus 3.30 release.

Classic mode

For Fedora and RHEL we have had an option called classic desktop, where desktop icons and some shell extensions were enabled.

It’s useful to bring to those users an option that works better than what we had with Nautilus, so as part of that I spent my time at Red Hat working on providing this.

Desktop icons extension

So here we are, I worked a lot lately in Nautilus and in the extension I prototyped few months ago to reach a point where it’s ready to enter a beta phase, now for everyone to use!

It has the regular things someone would expect. Some screenshots:

Screenshot from 2018-08-22 11-23-49Screenshot from 2018-08-22 11-24-00Screenshot from 2018-08-22 11-23-56Screenshot from 2018-08-22 11-23-53

  • Opening files
  • Executing desktop files
  • Drag and drop to reorder (with no more overlapping as the old desktop icons in Nautilus had!)
  • Proper multi-monitor support, another big improvement compared to the old Nautilus code.
  • Open in terminal
  • Cut/copy
  • Integration with Nautilus for all operations
  • Undo/redo
  • Shortcuts
  • Rubber banding selection
  • Pure Wayland

Things that are missing are renaming files popover and DnD to “paste into a folder”.

Try it out

To try it out, you need latest Nautilus. Install from the nightly flatpaks following these instructions. Then you need GNOME Shell 3.28 and to install the extension from the extensions GNOME website. Then make sure to have development nightly Nautilus running and then in GNOME Tweaks enable the extension.

Note: For Fedora it’s needed to disable the “Fedora logo” extension, as it collides with the desktop icons.

Contribute!

Beta also means you may find some visible bugs, please report them. As I get more feedback I will change, remove, introduce stuff to make it ready for 1.0.

Also more importantly, merge request welcome! If you have a feature that would like to implement or fixing a bug or behavior, feel free to download the code and create a merge request in the extensions repository at GitLab. I’ll glad to review your code!

Thanks Antonio, Ernestas, Florian and Didier for helping with review, testing and planning.

They should have called it Mirrorball

TL;DR: there’s now an rsync server at rsync://images-dl.endlessm.com/public from which mirror operators can pull Endless OS images, along with an instance of Mirrorbits to redirect downloaders to their nearest—and hopefully fastest!—mirror. Our installer for Windows and the eos-download-image tool baked into Endless OS both now fetch images via this redirector, and from the next release of Endless OS our mirrors will be used as BitTorrent web seeds too. This should improve the download experience for users who are near our mirrors.

If you’re interested in mirroring Endless OS, check out these instructions and get in touch. We’re particularly interested in mirrors in Southeast Asia, Latin America and Africa, since our mission is to improve access to technology for people in these areas.

Big thanks to Niklas Edmundsson, who administers the mirror at Academic Computer Club, Umeå University, who recommended Mirrorbits and provided the nudge needed to get this work going, and to dotsrc.org and Mythic Beasts who are also mirroring Endless OS already.

Read on if you are interested in the gory details of setting this up.


We’ve received a fair few offers of mirroring over the years, but without us providing an rsync server, mirror operators would have had to fetch over HTTPS using our custom JSON manifest listing the available images: extra setup and ongoing admin for organisations who are already generously providing storage and bandwidth. So, let’s set up an rsync server! One problem: our images are not stored on a traditional filesystem, but in Amazon S3. So we need some way to provide an rsync server which is backed by S3.

I decided to use an S3-backed FUSE filesystem to mount the bucket holding our images. It needs to provide a 1:1 mapping from paths in S3 to paths on the mounted filesystem (preserving the directory hierarchy), perform reasonably well, and ideally offer local caching of file contents. I looked at two implementations (out of the many that are out there) which have these features:

  • s3fs-fuse, which is packaged for Debian as s3fs. Debian is the predominant OS in our server infrastructure, as well as the base for Endless OS itself, so it’s convenient to have a package.1
  • goofys, which claims to offer substantially better performance for file metadata than s3fs.

I went with s3fs first, but it is a bit rough around the edges:

  • Our S3 bucket name contains dots, which is not uncommon. By default, if you try to use one of these with s3fs, you’ll get TLS certificate errors. This turns out to be because s3fs accesses S3 buckets as $NAME.s3.amazonaws.com, and the certificate is for *.s3.amazonaws.com, which does not match foo.bar.s3.amazonaws.com. s3fs has a -o use_path_request_style flag which avoids this problem by putting the bucket name into the request path rather than the request domain, but this use of that parameter is only documented in a GitHub Issues comment.
  • If your bucket is in a non-default region, AWS serves up a redirect, but s3fs doesn’t follow it. Once again, there’s an option you can use to force it to use a different domain, which once again is documented in a comment on an issue.
  • Files created with s3fs have their permissions stored in an x-amz-meta-mode header. Files created by other tools (which is to say, all our files) do not have this header, so by default get mode 0000 (ie unreadable by anybody), and so the mounted filesystem is completely unusable (even by root, with FUSE’s default settings).

There are two ways to fix this last problem, short of adding this custom header to all existing and future files:

  1. The -o complement_stat option forces files without the magic header to have mode 0400 (user-readable) and directories 0500 (user-readable and -searchable).
  2. The -o umask=0222 option (from FUSE) makes the files and directories world-readable (an improvement on complement_stat in my opinion) at the cost of marking all files executable (which they are not)

I think these are all things that s3fs could do by itself, by default, rather than requiring users to rummage through documentation and bug reports to work out what combination of flags to add. None of these were showstoppers; in the end it was a catastrophic memory leak (since fixed in a new release) that made me give up and switch to goofys.

Due to its relaxed attitude towards POSIX filesystem semantics where performance would otherwise suffer, goofys’ author refers to it as a “Filey System”.2 In my testing, throughput is similar to s3fs, but walking the directory hierarchy is orders of magnitude faster. This is due to goofys making more assumptions about the bucket layout, not needing to HEAD each file to get its permissions (that x-amz-meta-mode thing is not free), and having heuristics to detect traversals of the directory tree and optimize for that case.3

For on-disk caching of file contents, goofys relies on catfs, a separate FUSE filesystem by the same author. It’s an interesting design: catfs just provides a write-through cache atop any other filesystem. The author has data showing that this arrangement performs pretty well. But catfs is very clearly labelled as alpha software (“Don’t use this if you value your data.”) and, as described in this bug report with a rather intemperate title, it was not hard to find cases where it DOSes itself or (worse) returns incorrect data. So we’re running without local caching of file data for now. This is not so bad, since this server only uses the file data for periodic synchronisation with mirrors: in day-to-day operation serving redirects, only the metadata is used.

With this set up, it’s plain sailing: a little rsync configuration generator that uses the filter directive to only publish the last two releases (rather than forcing terabytes of archived images onto our mirrors) and setting up Mirrorbits. Our Mirrorbits instance is configured with some extra “mirrors” for our CloudFront distribution so that users who are closer to a CloudFront-served region than any real mirrors are directed there; it could also have been configured to make the European mirrors (which they all are, as of today) only serve European users, and rely on its fallback mechanism to send the rest of the world to CloudFront. It’s a pretty nice piece of software.

If you’ve made it this far, and you operate a mirror in Southeast Asia, South America or Africa, we’d love to hear from you: our mission is to improve access to technology for people in these areas.

  1. Do not confuse s3fs-fuse with fuse-s3fs, a totally separate project packaged in Fedora. It uses its own flattened layout for the S3 bucket rather than mapping S3 paths to filesystem paths, so is not suitable for what we’re doing here.
  2. This inspired a terrible “joke”.
  3. I’ve implemented a similar optimization elsewhere in our codebase: since we have many "directories", it takes many less requests to ask S3 for the full contents of the bucket and transform that list into a tree locally than it does to list each directory individually.

August 21, 2018

Kick-starting the revolution 1.0

Yesterday marked the day when we finally released Flatpak 1.0 (check out the release video!). I want to thank everyone who helped make this a reality, from writing code, to Flathub packaging, to just testing stuff and spreading the word. Large projects like this can’t be done by a single person, its all about the community.

With 1.0 out, I expect the rate of change in Flatpak itself to slow down. Going forward the focus will be more on the infrastructure around it. Things like getting 1.0 into all distributions, making portals work well, ensuring Flathub works smoothly and keeps growing, improving our test-suites and working on the runtimes.

Most of my blog posts are about technical details, but the reason for the existence of Flatpak is not technical. I created flatpak because the Linux application desktop ecosystem is fundamentally broken. As a app developer you have no sane way to distribute the result of your work to users.

Unless you have massive resources, the only realistic way is to wait for distributions to pick up your app. However, there are many problems with this. First of all, not all distros pick up all apps, and even if they do they often wait until the app is well known leading to a chicken-and-egg problem for new apps. And when they finally ship it you have no control of what version is shipped, or when it is updated.

This is a real problem! For instance, maybe some web service it uses changed API. Its a quick fix for your app, but it takes a long time before it propagates to distros. In fact many stable distros never ever update to new versions.

This model leads to a disconnect between the developer and the users. Users file bugs against older versions, about bugs that are already fixed, yet don’t get any fixes. Developers add new features that users can’t use, and get no feedback on them.

With Flatpak, the goal is for the upstream developer to have control of updates. If the developer fixes an important bug, a new stable version is released that users can immediately use. Any bugs filed will be against the latest stable version, so they are not stale, and once the bug report is closed the user will actually get the fix. That means reporting bugs is useful to the user. Similarly, any new feature development will get immediate feedback, and user feedback will be based on the current state of the app.

This kind of virtuous cycle helps improving both speed of development and software quality. My hope is that this in turn will increase the interest in writing native Linux applications and trigger a revolution, leading to the YEAR OF THE LINUX DESKTOP! (ahem)

Five or More GSoC

So, Google Summer of Code came to an end. I did (try my best to) mentor Ruxandra on her quest to modernize Five or More.

Some quick thoughts on the reasoning why using Vala as the language. And no flame-wars intended here. Just my personal thoughts:
* GObject with C requires too much boilerplate code
* Vala source is translated to C
* Most of the games under the GNOME umbrella are in Vala (they have been ported some time ago), thus potential new contributors can get involved easier
* valadoc.org is a very comprehensive API reference, easy to browse (although similar ones exist for each language advertised on the developer.gnome.org - see javascript, c++, python, c with some of them being easier to browse than others) - hoping that the new Developer portal will solve this and make API references browsable and have a programming language combobox for toggling between all of the ones listed, regardless of the chosen technology stack for it.

There are some arguments against Vala (not that efficient, syntax not OK for everyone, Vala being internally developed by GNOME), and I even agree with some of them (not sure if maintaining a language or finding a language and maintaining proper bindings for it takes more development effort - I saw interest in Gtk development with Rust, but other than librsvg - from the GNOME world - no one seemed to really jump on it).

So, given the above, Ruxandra did successfully port the old game from C to Vala, carefully rewriting the game piece-by-piece, mostly independently from the existing code (although there are some parts which have been ported line by line) and the existing Vala port (used as a reference in some places). The only thing which didn't get in (and honestly, I think I can say fortunately) is raster-based themes for the game. I think that these days, when we are transitioning to having the application icons only in scalable vector format (svg) it's not worth investing in raster-based themes, which either look horrible on huge resolutions, or need huge resource files at a huge resolution to look fine on big screens/hidpi screens and downscaling on non-hidpi screens.

I am sorry if anyone will feel offended by the removal of the two raster-based themes (dots and gumball), please let me know, and I will personally handle them being replace by similar vector-based art. Until then, if you somehow preferred using any of those themes, after the next release you will fall back to the default theme, which is the color balls vector theme.

So, what next?
* I'm waiting until the 3.30 release mid-september
* the code changes Ruxandra proposed will get in after that release, feedback should probably get incorporated by then - thanks to everyone who did and/or will post feedback on the merge request
* the new vector theme I have created (as a replacement to the removed dots and gumball themes) will get in after adding the possibility of having themes with different animation frame counts, currently all animations only have 4 frames, the one I am proposing needs at least 5 to look smooth (see it in action/animated here). Design and artist feedback is more than welcome on it (I'm a developer, so constructive criticism only)

So, thanks to Ruxandra, Five or More 3.32 will be a completely new application, with a new theme. And ... ssh... also planning to support mobile (Librem 5).

August 19, 2018

Shotwell and face recognition

Please check Narendra’s post on the efforts of getting face recognition working in Shotwell : http://narenma.net/2018/08/18/shotwell-facedetect/

(I’m currently trying to get that to work inside flatpak for easier testing)

GNOME.Asia Summit 2018

GNOME.Asia Summit 2018 was co-hosted with COSCUP 2018 and openSUSE.Asia Summit in Taipei, Taiwan 11-12 August 2018.

GNOME-2018

At the conference there were fabulous welcome party, talks by experts, group photo sessions, Bird of Feather (BoF), committee member’s dinner 🙂 and unforgettable one day tour.  It was very well organized and a great conference. As always it was fun to meet Kat, Nuritzi, Max and his sweet wife, Emily, BinLi, Ben, Neil, Mathias, Harris, David king, Jonathan Kang, Eric sun, Siska, Sunny, Estu and open source users from all around the Globe. I also made many new friends namely Ana, Fuminobu TAKEYAMA, Denial, Rania.

42180348480_3bf629dd62_o

30120889818_422ff018e4_o

43940908852_9085816bee_o

Photos Courtesy of: Siska Restu Anggraeny

I was glad to meet Raju from India at the conference. He was the man who provided me with the Internet and his support all the time when I needed it. I would like to say many thanks to Umang Jain who was there all the time with me from take off from and to landing back to India. I enjoyed every moment out there. Many thanks to Max for the wonderful conference. I had to deliver my talk on the second day of the conference. The topic of my talk was “Humanitarian FOSS Projects”. The message that I wanted to deliver is simple. We have open source software to help humanity. GNOME’s accessibility project include software for people with various disabilities such as Orca for visually impaired, mouse trap for physically challenged people since 21 years.  Humanitarian FOSS  is community building project, started by a group of computing faculty and open source supporters in 2006 at Trinity College, Wesleyan University, and Connecticut College. The aim of this project was to make students learn computing by serving society by some or other ways. They work on real cause in a team that help them grow in multi-faceted ways. By working on HFOSS project they know what they are doing, how will it benefit society. That is the driving force for the students to participate in such projects. Students of these universities have contributed to POSIT, GNOME accessibility project, Sahana, InSTEDD, OpenMRS so far. Hope Humanitarian FOSS projects keep helping students learn open source technologies by doing good for the society all over the world and help us to have a better world. sponsored-badge-shadow

GNOME Asia 2018, Taipei

I am very pleased to attend to GNOME Asia(again!) that took place at National Taiwan University of Science and Technology, Taipei this year. Its always great to see GNOME folks around, hanging out and have a social side of things. GNOME Asia was co-hosted with OpenSUSE Asia summit and COSCUP.

Markdowm Image

Co-hosting with openSUSE and COSCUP surely had benefits as there was inter-community interaction. The keynote space was shared with GNOME and openSUSE board members to address the free software local community and the participants. Overall, it has been a good conference; both in terms of talks and having a social gathering. Tech talks that I found quite interesting:

  • Life inside a Sandbox by David King
  • Introduction to Silverblue By Matthias Clasen
  • Plan you testing by Kat

Apart from that, I attended a few talks around community and getting involved with open source like “Introduction to GSoC” by Ana María Martínez. There was a very intriguing talk was about designing GNOME Shoes in Inkspace where Iwan S. Tahari takes through the whole process of making shoes from scratch (I will mention the link here once videos are out. It’s a must watch!!).

Markdowm Image

We had a GNOME BoF to address couple of issues around conferences: Mostly around standardization of conference organization, budget, effect of local team presence at potential conference venues etc.

We all hung out in the evening for dinner & drinks. I stayed back in Taipei for one extra day for visiting the Endless office and meeting the kernel/hardware enablement team. Fortunately, I also met Daniel Drake and Jonathan Blandford who were in Taipei at the same time. I had my first day of working from the company’s office(I work remotely from India) :)

Markdowm Image

Endless office in Taipei

I really had a beautiful time in Taipei. It’s a great city to be in. We all bid good-byes until we meet again. I can’t wait to visit Taipei again. Also want to thank GNOME Foundation to assist with the travel.

Markdowm Image

#GNOMELove

Markdowm Image

Easier Flatpak manifest editing with VSCode

As flatpak-builder has gained numerous features over the years it is easy to not remember every single property and it becomes a bit of a chore to keep running man flatpak-manifest.

So over a year ago I wrote a JSON Schema for manifests but after doing so I never ended up actually using it since I found editor support to be lacking and Python libraries for validation gave useless output when I tried to add support to GNOME-Builder.

Lately I’ve been using VSCode and it happens to expose some configuration for schema files. The main problem we have is that flatpak manifests do not have a custom file name or extension so all automatic schema association fails. (I have proposed .flatpakmanifest in the past but nobody seems interested.)

Setup

The most simple method to use a schema in VSCode is this custom property in the manifest itself:

{
    "$schema": "https://raw.githubusercontent.com/TingPing/flatpak-manifest-schema/master/flatpak-manifest.schema"
}

If you want to keep the manifest clean of non-standard properties like that you set these properties in your workspace (YAML support is from redhat.vscode-yaml extension) :

{
    "json.schemas": [
        {
            "fileMatch": ["*.json"],
            "url": "https://raw.githubusercontent.com/TingPing/flatpak-manifest-schema/master/flatpak-manifest.schema"
        }
    ],
    "yaml.schemas": {
        "https://raw.githubusercontent.com/TingPing/flatpak-manifest-schema/master/flatpak-manifest.schema": "*.yml",
    }
}

Results

August 18, 2018

Linker symbol lookup order does not work the way you think

A common problem in linking problems has to do with circular dependencies. Suppose you have a program that looks like this:



Here program calls into function one, which is in library A. That calls into function two, which is in library B. Finally that calls into function three, which is back in library A again.

Let's assume that we use the following linker line to build the final executable:

gcc -o program prog.o liba.a libb.a

Because linkers were originally designed in the 70s, they are optimized for minimal resource usage. In this particular case the linker will first process the object file and then library A. It will detect that function one is used so it will take that function's implementation and then throw the rest of library A away. It will then process library B, take function two in the final program and note that function three is also needed. Because library A was thrown away the linker can not find three and errors out. The fix to this is to specify A twice on the command line.

This is how everyone has been told things work and if you search the Internet you will find many pages explaining this and how to set it up linker command lines correctly.

But is this what actually happens?

Let's start with Visual Studio

Suppose you were to do this in Visual Studio. What do you think would happen? There are four different possiblities:
  1. Linking fails with missing symbol three.
  2. Linking succeeds and program works.
  3. Either 1. or 2. happens, but there is not enough information to tell which.
  4. Linking succeeds but the final executable does not run.
The correct answer is 2. Visual Studio's linker is smart, keeps all specified libraries open and uses them to resolve symbols. This means that you don't have to add any library on the command line twice.

Onwards to macOS

Here we have the same question as above but using macOS's default LLD linker. The choices are also the same as above.

The correct answer is also 2. LLD keeps symbols around just like Visual Studio.

What about Linux?

What happens if you do the same thing on Linux using the default GNU linker? The choices are again the same as above.

Most people would probably guess that the correct answer here is 1. But it's not. What actually happens is 3. That is, the linking can either succeed or fail depending on external circumstances.

The difference here is whether functions one and three are defined in the same source file (and thus end up in the same object file) or not. If they are in the same source file, then linking will succeed and if they are in separate files, then it fails. This would indicate that the internal implementation of GNU ld does not work at the symbol level but instead just copies object files out from the AR archive wholesale if any of their symbols are used.

What does this mean?

For example it means that if you build your targets with unity builds, their entire symbol resolution logic changes. This is probably quite rare but can be extremely confusing when it happens. You might also have a fully working build, which breaks if you move a function from one file to another. This is a thing that really should not happen but when it does things get very confusing.

The bigger issue here is that symbol resolution works differently on different platforms. Normally this should not be an issue because symbol names must be unique (or they must be weak symbols but let's not go there) or the behaviour is undefined. It does, however, place a big burden on cross platform projects and build systems because you need to have very complex logic in place if you wish to deduplicate linker flags. This is a fairly common occurrance even if you don't have circular dependencies. For example when building GStreamer with Meson some time ago the undeduplicated linker line contained hundreds duplicated library entries (it still does but not nearly as many).

The best possible solution would be if GNU ld started behaving the same way as VS linker and LLD. That way all major platforms would behave the same and things would get a lot simpler. In the mean time one should be able to simulate this with linker grouping flags:
  1. Go through all linker arguments and split them to libraries that use link_whole and those that don't. Throw away any existing linker grouping.
  2. Deduplicate and put the former at the beginning of the link line with the requisite link_full arguments.
  3. Deduplicate all entries in the list of libraries that don't get linked fully.
  4. Put the result of 3 on the command line in a single linker group.
This should work and would match fairly accurately what VS and LLD already do, so at least all cross platform projects should work out of the box already.

What about other platforms?

The code is here, feel free to try it out yourself.

Reminder: Shotwell Facebook publishing no longer working

As announced earlier, since August 1st, 2018 Shotwell cannot publish to Facebook any more. The API that Shotwell used for that was removed and it is currently not clear to me how developers that do not use Android, iOS or Facebook’s web SDKs should provide similar functionality.

August 17, 2018

The Internet of 200 Kilogram Things: Challenges of Managing a Fleet of Slot Machines

In a previous post we talked about Finland's Linux powered slot machines. It was mentioned that there are about 20 000 of these machines in total. It turns out that managing and maintaining all those machines is a not as easy as it may first appear.

In the modern time of The Cloud, 20 thousand machines might not seem like much. Basic cloud management software such as Kubernetes scales to hundreds of thousands, even millions of machines without even breaking a sweat. Having "only" 20 thousand machines may seem like a small and simple thing that can be managed by one intern in their spare time. In reality things get difficult as there are many unique challenges to managing slot machines as opposed to regular servers.

The data center

Large scale computer fleets are housed in data centers. Slot machines are not. They are scattered across Finland in supermarkets and gas stations. This means that any management solution based on central control is useless. Another way of looking at this is that the data center housing the machines is around 337 thousand square kilometers in size. It is left as an exercise to the reader to calculate the average distance between two nearest machines assuming they are distributed evenly over the surface area.

Every machine is needed

The mantra of current data center design is that every machine must be expendable. That is, any computer may break down at any time, but the end user does not notice this because all operations are hidden behind a reliable layer. Workloads can be transferred from one machine to another either in the same rack, or possibly even to the other side of the world without anyone noticing.

Slot machines have the exact opposite requirements. Every machine must keep working all the time. If any machine breaks down, money is lost. Transferring the work load from a broken machine in the countryside to Frankfurt or Washington is not feasible, because it would require also moving the players to the new location. This is not very profitable, as atoms are much more expensive and slow to transfer between continents than electrons.

The reliability requirements are further increased by the distributed locations of the machines. It is not uncommon that in the sparsely populated areas the closest maintenance person may be more than 400 km away.

The Internet connection

Data centers nowadays have 10 Gb Ethernet connections or something even faster. In contrast it is the responsibility of the machine operator to provide a net connection to a slot machine. This means that the connections vary quite a lot. At the lowest end are locations that get poor quality 3G reception some of the time.

Remote management is also an issue. Some machines are housed in corporate networks behind ten different firewalls all administered by different IT provider organisations, some of which may be outsourced. Others are slightly less well protected but flakier. Being able to directly access any machine is the norm in data centers. Devices housed in random networks do not have this luxury.

The money problem

Slot machines deal with physical money. That makes them a prime target for criminals. The devices also have no physical security: you must be able to physically touch them to be able to play them. This is a challenging and unusual combination from a security point of view. Most companies would not leave their production servers outside for people to fiddle around with, but for these devices it is a mandatory requirement.

The beer attack

Many machines are located in bars. That means that they need to withstand the forces of angry intoxicated players. And, as we all know, drunk people are surprisingly inventive. A few years ago some people noticed that the machines have ventilation holes. They then noticed that pouring a pint of beer in those holes would cause a short circuit inside the machine causing all the coins to be spit out.

This issue was fixed fairly quickly, because you really don't want to be in a situation where drunk people would have financial motivation to pour liquids on high voltage equipment in crowded rooms. This is not a problem one has to face in most data centers.

Update challenges

There are roughly two different ways of updating an operating system install: image based updates and package based updates. Neither of these works particularly well in slot machine usage. Games are big, so downloading full images is not feasible, especially for machines that have poor network connections. Package based updates have the major downside that they are not atomic. In desktop and server usage this is not really an issue because you can apply updates at a known good time. For remote devices this does not work because they can be powered off at any time without any warning. If this happens during an upgrade you have a broken machine requiring a physical visit from a maintenance person. As mentioned above this is slow and expensive.

August 16, 2018

libinput's "new" trackpoint acceleration method

This is mostly a request for testing, because I've received zero feedback on the patches that I merged a month ago and libinput 1.12 is due to be out. No comments so far on the RC1 and RC2 either, so... well, maybe this gets a bit broader attention so we can address some things before the release. One can hope.

Required reading for this article: Observations on trackpoint input data and X server pointer acceleration analysis - part 5.

As the blog posts linked above explain, the trackpoint input data is difficult and largely arbitrary between different devices. The previous pointer acceleration libinput had relied on a fixed reporting rate which isn't true at low speeds, so the new acceleration method switches back to velocity-based acceleration. i.e. we convert the input deltas to a speed, then apply the acceleration curve on that. It's not speed, it's pressure, but it doesn't really matter unless you're a stickler for technicalities.

Because basically every trackpoint has different random data ranges not linked to anything easily measurable, libinput's device quirks now support a magic multiplier to scale the trackpoint range into something resembling a sane range. This is basically what we did before with the systemd POINTINGSTICK_CONST_ACCEL property except that we're handling this in libinput now (which is where acceleration is handled, so it kinda makes sense to move it here). There is no good conversion from the previous trackpoint range property to the new multiplier because the range didn't really have any relation to the physical input users expected.

So what does this mean for you? Test the libinput RCs or, better, libinput from master (because it's stable anyway), or from the Fedora COPR and check if the trackpoint works. If not, check the Trackpoint Configuration page and follow the instructions there.

August 15, 2018

Builder Session Restore

People have asked for more advanced session restore for quite some time, and now Builder can do it. Builder will now restore your previous session, and in particular, horizontal and vertical splits.

Like previously, you can disable session restore in preferences if that’s not your jam.

You can write plugins which hook into session save/restore by implementing IdeSessionAddin.

August 14, 2018

GSoC’18 - Final Report

As GSoC’18 ends, I’m writing this final report as a summary of the current state of the project.

Summary

My project can be majorly divided into the following subtasks. Deatils about my project and subtasks can be found here- 1, 2, 3.

Fetching metadata to Games

Currently Games never made use of any metadata provided by thegamesdb. My first task was to enable Games to fetch this metadata. This is the core of my projct as all the other tasks are dependent on this one. My changes for this have already been pushed to master.

Allow grilo to set values for non-registered keys

My following task was to add missing Grl KeyIDs to grilo in order to succesfully fetch them to Games, but adding too many KeyIDs to core grilo was not suitable. I wrote a new grio API to succesfully register and set data to non-registered keys which took a lot more time than expected. The first goal to be met in order to achieve that was allowing grilo to set values for non-registered keys. My patches for this have already been pushed to master.

Allow Lua plugins to self register keys

In order to fetch all of metadata to games, the only task remaining was allowing lua sources to register keys by themselves. This would allow anyone to use any key for lua based sources without having to worry about adding those keys as core system keys. This has already been pushed to master.

Segregating-view

Here I make the first use of the metadata I added to Games. Two new views, developers view and platforms view were added to Games which display games grouped by a particular developer or platform respectively. This was the most enjoyable task to work on as this provided a much needed UI upgrade to Games. You can try this as this has already been merged to master.

Description-view

This made use of all the remaining metadata that I was fetching to Games. Description view displays all of the game metadata like

  • Description
  • Cover
  • Title
  • Rating
  • Developer
  • Publisher
  • Genre
  • Coop
  • Release Date
  • Players

This is almost complete, it can be built and used through my patches. Some minor fixes and a review are required to get this merged to master.

Bugs

I fixed a few bugs I encountered along the way of my project. All of these bug fixes were pushed to master.

Tasks left

Some of the tasks I had originally planned took a lot more time than expected. My last task was to add stats to games that track and store your overall game statistics. I’ve already began working on this and will get it merged after thoroughly getting it reviewed by my mentors.

Work Product

I’ve uploaded all my current patches to a drive folder for easy availability. Additionally all my patches are also avaialable on Gitlab.

A few words

I had a wonderful time contributing to GNOME since I started this February. The amazing community and even more amazing mentors helped me learn new things and guided me all along the way which I would like to thank them for. I will surely keep contributing to GNOME.

Special thanks to Adrien, theawless and exalm. Thank you for making my summers super exciting.

August 13, 2018

GTK+ 4 and Nautilus </GSoC>

Another summer here at GNOME HQ comes to an end. While certainly eventful, it unfortunately did not result in a production-ready Nautilus port to GTK+ 4 (unless you don’t intend to use the location entry or any other entry, but more on that later).

It works, and you can try it if you really (really) want to by doing so:

flatpak install https://gitlab.gnome.org/GNOME/gnome-apps-nightly/raw/master/NautilusGtk4.flatpakref

You will notice that DnD works differently as compared to the current stable version:

  • the default action isn’t set by Nautilus to “move” when dragging a file in its parent directory, but rather all possible actions (“copy”, “move”, “link”, “ask”) – that is due to changes in the DnD API in GTK+ in an effort to consolidate all the different action types (actions, suggested actions, selected actions);
  • “link” cannot be accessed with ctrl-shift when using Wayland, “ask” does not work in X;
  • “ask” does not result in a context menu being popped up, likely due to a bug in GTK+ and not in Nautilus;
  • sidebar drops are rejected and cause the “New bookmark” row to remain visible, which is caused by another reported GTK+ bug.

Another big issue is actions and their (global) accelerators. Due to changes in how these are activated in GTK+, Nautilus can no longer do the trick where it prevents that from happening if the currently-focused widget is an editable. Not being able to do that means that pressing ctrl-a while, say, renaming a file will select the files in the view instead of the text. Pressing delete immediately after will result in you fainting, as you’ve just trashed all the files (as had happened to me several times, and thank goodness it’s not permanent deletion). Given the shortcuts reworking effort in GTK+ that is currently in progress, it might be a breeze to fix the issues in the near future.

Aside from all that, there are some other small-ish and annoying issues, like drag icons not being offset in Wayland, the operations button losing its reveal animation, and probably something else I’m forgetting at the moment. So, if you’re interested in turning your computer into a jet engine or benchmarking GitLab, feel free to take a look at https://gitlab.gnome.org/GNOME/nautilus/merge_requests/266.

GSoC 2018 Final Evaluation

As GSoC is coming to an end, I am required to put my work altogether in order for it to be easily available and hopefully help fellow/potential contributors work on their own projects. 🙂

All of my work (during the GSoC period!) can be found on this link.

GSoC has been an amazing experience and opportunity. I have learned lots, made a lot of friends and found something I’d really love to keep on doing further. 🙂

Our initial plan can be found in its own issue right here, issue 224.

First off, a few goals have been unfortunately left out as there has been a lot of going back and forth regarding how we want to do certain things, which ones to prioritize and how to best set an example for the consequent work.

At its prestige, through this project we will have tests both for most critical and used operations of Nautilus, and for the search engines we use. Further on, I’ll provide links for all of my merge requests and dwell a bit on their ins and outs while posting links to my commits:

Add dir-has-files test, the code can be found here: a fairly small and simple function I started to work on in order to get used to working with the GTest framework (although I had some experience from the merged file-utilities test which was merged before the start of GSoC) and set the cornerstone on how we want files and folders to be managed.

Add file-operations-move test, the code available here: the first large operation whose tests we merged. With this we learned we need to create several different hierarchies and that we’d need synchronous alternatives to the operations we’re about to test.

Copy, trash-or-delete file operations tests, whose code is in this folder: after the move operation, things must have went smoother, right? Copy was more or less straight forward, yet trashing and deleting posed a few difficulties as they involve interacting with the user a lot (e.g. confirmation for deletion, confirmation for permanent deletion and the likes of these dialogs which we wanted to avoid as we were doing testing). Once these were added, we realized (as the tests are being ran in parallel) we wanted to prefix the file hierarchies in order to prevent multiple tests from deleting (or trying to access) the same file (which might have been wrongfully deleted by another executable subsequently).

Test library, undo/redo of operations and search engines, which can be found here. This is still a work in progress at the moment of writing this, although the final pushes are more or less about polishing up the code, its documentation and making sure the commits are consistent and make sense in the work tree. Undo and redo operations took quite a bit of time as they have several callbacks in the call chain and they proved cumbersome to debug and work out. With a little help, we decided it would be best not to go with synchronous alternatives, but rather isolate the calls in their own main loop, which signals when it reaches the final callback. Although undo and redo have mostly been merged, some follow up work will be needed as our environment right now does not allow trashing (which means undoing copying a file for instance would work, but not its redo). Search tests were not as difficult as we had a small test regarding the main engine which we only needed to update and make sure runs fine, before proceeding with the following engines. Definitely, the one which proved most difficult was the tracker engine as we need to index a virtual file in the tracker before we run the engine, in order for it to be found. After all this work, I decided we’d reduce the lines of code by a good chunk using a test library, which allows undoing/redoing operations in a blocking main thread, creating and deleting different and varying in size file hierarchies for the purpose of testing (library which will hopefully make following contributions on Nautilus a bit easier and maybe set an example for other GNOME apps).

 

Bottom line, there’s still a bit of work ahead of us, but I’m confident I’ll manage to do some of them myself, while also trying to make it more approachable for other (or newer) Nautilus contributors.

August 12, 2018

[GSoC’18] Pitivi’s UI Polishing – Final Report

As part of Google Summer of Code 2018, I worked on the project Pitivi: UI Polishing. This is my final report to showcase the work that I have done during the program.

Work Done

I worked on the issue #1302 to integrate Pitivi’s welcome dialog box into its main window and display projects in a more informative, discoverable, and user friendly layout. This is how Pitivi greeted users before I started working on the issue:

Screenshot from 2018-08-11 13-59-26

My first task was to remove the welcome dialog from Pitivi and integrate a simple greeter perspective that displays the name of recent projects in a listbox with all the relevant buttons placed in the headerbar. This was a big task and took nearly 5 weeks to complete. The main challenge of this task was to understand the big codebase of main window and refactor it. The refactoration lead to the following architectural changes:

pitivi_architecture (2)

In the the new architecture, I have developed a perspective based system which allows easily adding more perspectives to Pitivi. Every class has single responsibility in the new architecture as opposed to everything being managed by main window in the previous architecture.

!33 is the merge request for this change and 330d62a4 is the committed code. This is how the greeter perspective looked like after this change:

pitivi_greeter_ad

I also did some code clean up to conclude my greeter perspective’s integration:

  • Remove unused “ignore_saved_changes” param from newBlankProject() function in project class – b2f19ea3
  • Make newly added shortcuts available when users upgrade Pitivi – 8b3a691a
  • Move dialog for locating missing asset to dialogs package – c08007a8
  • Remove directly called callback methods – 7a8d23ba
  • Remove project title functionality because we only care about project file name – 4ea9d89f

My next task was to display meta info regarding a project such as its directory and last updated timestamp in the greeter perspective. !43 is the merge request for this change and ff672460 is the committed code. This is how the greeter perspective looked like after this change:

aaa

My next task was to show a welcome message on greeter perspective when there are no recent projects. !44 is the merge request for this change and d9c8f6c8 is the committed code. This is how the empty (no recent projects) greeter screen looks like:

empty_greeter

Finally, I added the following to greeter perspective:

  1. “Search” feature for easy browsing of recent projects, and
  2. “Selection” feature for removing project(s) from recent projects list.

!46 is the merge request for search feature and c72ebce7 is the committed code. !48 is the merge request for selection feature and 809d7599 is the committed code.

searchSearch feature
removeSelection feature

After resolving issue #1302, I worked on the issue #1462 to integrate project thumbnails in greeter perspective. The main idea behind these thumbnails is to give the user a hint what a certain project is about. The main challange of this task was to come up with a simple and robust algorithm for thumbnail generation (link). More details about our project thumbnail generation approach can be found here.

!51 is the merge request for this change and a6511c1e is the committed code.

I also made the following optimizations to project thumbnails (!54):

  • Use assets thumbnails if no thumbnails are present in XDG cache – cb47d926
  • While opening greeter perspective, defer loading of project thumbnails until the main thread is idle – 37c973e1

So this is how the greeter perspective looks finally after all these changes:

Screenshot from 2018-07-21 00-29-40

After wrapping integration of project thumbnails, I added the following features to greeter perspective:

  • Right click on a recent project to select it and start selection mode – !67 is the merge request and 6db06472 is the committed code
  • Drag & drop a project file from Nautilus onto greeter to open it – !68 is the merge request and 817d885a is the committed  code

Next, I worked on the issue #1816 to allow video viewer resizing.

The first task was to integrate a marker on the bottom left corner of the viewer container to allow for easy resizing of the viewer. !60 is the merge request for this change and 25609f3a is the committed code.

resize1Viewer resizing using corner marker

Work in Progress…

Two more tasks are left under issue #1816:

  1. Displaying resize status (in %) over slightly dimmed video, and
  2. Snapping of viewer to 100% and 50% if the current resize status is between [100%, 100% + Δ ] and [50%, 50% + Δ ] respectively.

I have submitted a merge request !70 for review for both these tasks.

resize2Displaying resize status over dimmed video

Final Words

I would like to thank my mentor Alexandru Băluț (aleb) for his help and guidance throughout this project. I would also like to thank all other members of GNOME community who helped me along the way. This turned out to be an amazing summer for me with lots of learning and fun. I will continue to contribute to Pitivi as I feel like this is just the beginning of many exciting things yet to be done in Pitivi to make it the best video editing application.

Until next time 🙂

August 11, 2018

Five-or-More Modernisation: It's a Wrap

As probably most of you already know, or recently found out, at the beginning of this week the GSoC coding period officially ended, and it is time for us, GSoC students, to submit our final evaluations and the results we achieved thus far. This blog post, as you can probably tell from the title, will be a summary of all of the work I put into modernising Five or More throughout the summer months.

My main task was rewriting Five or More in Vala since this simple and fun game did not find its way to the list of those included in the Games Modernisation Initiative. This fun, strategy game consists of aligning, as often as possible, five or more objects of the same shape and color, to make them disappear and score points.

Besides the Vala rewrite, there were also some other tasks included, such as migrating to Meson and dropping autotools, as well as keeping the view and logic separated and updating the UI to make this game more relatable for the public and more fresh-looking. However, after thoroughly discussing the details with my mentor, Robert Roth (IRC: evfool), more emphasis was placed upon rewriting the code to Vala, since the GSoC program is specifically designed for software development. However, slight UI modifications were integrated as to match the visual layout guidelines.

Some of the tasks, namely porting to gettext, porting to Meson and dropping autotools happened earlier on, during the pre-GSoC time frame, in the attempt to familiarise myself with the project and the tasks that would come with it.

Afterward, I started with the easier tasks and advanced towards the more complex ones. At first, I ported the application window and the preferences menu and added callbacks for each of the preferences buttons. I then continued with porting the preview widget displaying the next objects to be rendered on the game board.

Next, it was time to channel my attention towards porting the game board, handling board colour changes and resizing the board alongside the window resize, by using the grid frame functionality inside the libgnome-games-support library.

The following target was implementing the actual gameplay logic, which consisted of a pathfinding algorithm based on A*, erasing all objects of the same shape and colour aligned in a row, column or diagonal from the board, if there were either five or more than five, and adding to the score whenever that happened. I also made the object movement possible with both clicking and keyboard keys, for more ease of use.

Finishing touches included adding the high scores tables via the libgnome-games-support library, displaying an option to change the theme of the game, adding a grid to be able to make out easier the actual cells in which different shaped and coloured objects should reside, as well as updating some information contained by the help pages.

Some features, however, could not be done during the GSoC period. These included handling raster game themes, making significant UI updates, as well as some other extra-features I wanted to add, which were not part of the original project description such as gamepad support or sound effects. The fist feature mentioned in this list, handling raster images was decided upon skipping as a suggestion from my mentor, as the existing raster themes were low-resolution and did not scale well to large size and HiDPI displays.

For easier reading, I decided to also include this list of the tasks successfully done during GSoC:

  • Ported from autotools to Meson
  • Ported application window and preferences menu
  • Added callbacks for all buttons
  • Ported widget of next objects to be rendered on the game board
  • Handled the resize and colour change of the game board
  • Implemented the gameplay logic (pathfinding, matching objects and calculating scores)
  • Made gameplay accessible for both mouse clicks and keyboard controls
  • Added high scores via the libgnome-games-support library
  • Handling vector image theme changing
  • Made slight modifications to the UI to serve as a template and changed the spacing as recommended in the visual layout guidelines.
  • Updated documentation.

All code is available by accessing this merge request link.

The end of GSoC

After three months of hard work and a lot of coding the Google Summer of Code is over. I learned a lot and had a lot fun. GSoC was an amazing experience and I encourage everybody to participate in future editions. At this point I’ve been a contributor to GNOME for nearly a year, and I plan on sticking around for a long time. I really hope that other GSoC students also found it so enjoyable, and keep contributing to GNOME or other Free Software Projects.

This will be my final GSoC post, so I will summarize what I did for Fractal in the last couple of months, and what I didn’t finish before the deadline. I completed some of the tasks I proposed for GSoC already before the Coding Phase. It was really nice that we had a meeting with Daniel (my mentor) and Eisha (my co-GSoC student) every week, and also some other core members working for Fractal joined sometimes. During these meetings I could get feedback from Daniel and discuss tasks for the next week. We had a really good workflow. I guess that was also a reason that I started working on some imported tasks and not only on things I initially proposed.

Main Tasks

These are my main Tasks I completed during the GSoC. They are all already merged, and included in the current release:

These are the task I could not finish in time:

  • Spell Check: Add spellcheck to Fractal, based on gspell. I implemented this (though not with the UI we’ll eventually want) and it was merged, but since then we made some big changes to the message input widget and therefore it doesn’t work anymore at the moment. We also decided that other tasks were more important. I will work on this in the next couple of months.
  • refactor room history: A big change to how the message history is displayed, loaded, and stored. This will lay the groundwork for a lot of new features we want, and the backend refactor we need to split the app. This is one of the biggest tasks I worked on during the summer, and I will try to finish it as soon as possible. Since we started restructuring the code base I had a lot of stuff to figure out and therefore it was delayed.
  • Use libhandy to have adaptive columns: We want Fractal to work on big screens as well as on small screens and therefore we use a widget in libhandy to center columns and limit the width of, mostly, gtkListboxes.

During the Summer I made also some other contributions, which were tangentially related to Fractal and my GSoC tasks, e.g. I fixed the GTK emoji picker layout and styling (this has been merged now, so look forward to a much nicer emoji picker in GTK 3.24).

My Code

Most of my work is upstream and already integrated into Fractal. This is a list of all commits merged into Master:

* b9d7a5a - message_menu: remove appOP dependecy from message_menu 
* 6897ad0 - roomsettings: update .po file and add i18n 
* f96eeef - roomsettings: fix member list disappearing when closing 
* c772d9f - roomsettings: hide confirm button when the topic/name didn"t change 
* cf7dffd - roomsettings: clean up code from old memberslist 
* 44b2d28 - roomsettings: reload room settings when members are avaible 
* 089b001 - headerbar: remove avatar form the headerbar 
* 20c9ebd - roomsettings: move room settings panel to a custom widget 
* 692045f - roomsettings: make room description label a dim-label 
* 78c5be1 - roomsettings: set max with for centerd column 
* 0c99e0e - roomheaderbar: move room menu to new settings button 
* a2de6ac - roomsettings: hide not implemented widgets 
* 5097931 - roomsettings: request avatar when entering the room settings 
* 0244a23 - roomsettings: hide settings not needed for a room type 
* dc83201 - roomsettings: add invite button 
* 544fc52 - roomsettings: show members in the room settings 
* 4e6b802 - room: add room settings panel 
* 8993f5f - avatar: refactor avatar loading and caching 
* b81f1ec - accountsettings: add closures to address button callback 
* 200440d - accountsettings: remove seperator in user settings menu 
* e88663e - accountsettings: make animation for destruction revealer slower 
* 0068fde - accountsettings: fix valignment of label name/email/phone 
* 140da51 - accountsettings: scroll to advanced/destruction section when open 
* f1e86bd - accountsettings: add placeholder text 
* c4d608b - accountsettings: show spinner will updating the avatar 
* 41a07a8 - accountsettings: generate client secret for each request 
* 7c459d3 - accountsettings: use stored identity server 
* 708c5c0 - accountsettings: add button to submit new display name 
* e019e48 - accountsettings: move account settings to the main window 
* 6ee5c99 - accountsettings: show confirm dialog when deleting account 
* 9a2dada - accountsettings: remove loading spinner once password is set 
* 835ab15 - accountsettings: password change and account destruction 
* ba74610 - accountsettings: managment for phone and email threePIDs 
* 345cb8e - accountsettings: add buttons to address entries 
* 04ed27f - accountsettings: load threePIDs (email and phone addresses) 
* a214504 - accountsettings: password validation 
* 8711c01 - accountsettings: add password change dialog 
* 1d31eab - accountsettings: add api for changing account avatar 
* de83098 - accountsettings: add api for changing account name 
* 96bcb94 - accountsettings: add UI for account settings 
* 1fec2e5 - login: use global constant for homeserver and identity server 
* ee1ad9c - login: store identity server 
* 206035c - gspell: add basic spell check 
* f92387c - mention: highlight own username in mentions using pango attributes 
* 3d3a873 - fix: add joining members to the there own room instate to the active room 
* d6145e8 - message-history: set space after last message to 18px 
* 972d44a - mention: blue highlight for messages with mentions and cleanup css file 
* 0571245 - autoscroll: add ease-out when scrolling to last message 
* b35dec6 - autoscroll: add button to scroll down to last message 
* 789048f - autoscroll: move to new message when the user send a message 
* 55dcaf1 - autoscroll: no delay before autoscroll 
* 77051f6 - center inapp notification 
* 5c1cd22 - fix autoscroll, disable autoscroll when not at the end of message history, fix #137 
* f59c3d2 - autocomplete: fix change usernames font color to white when selection ends at the same position as the username 
* f6a3088 - move autocomplete popover to a seperate file 
* 0f2d8a8 - messages-history: add spacing after last message 
* b548fc9 - messages-history: remove blue selection and add more space inside each row 
* dc0f1e1 - add Julian Sparber to the author list 
* 20beb87 -add spacing bethween avatar and msg body 
* b721c96 - add padding to the message history 
* 533a736 - implement redesin of the autocomplete popover, fix #146 
* 9a9f215 - remove " (IRC)" from the end of suggested username for mentions, fix #126 
* 101c7cd - use only @ with tab-compleation 
* 798c6d3 - match also uid for mentions 
* 3caf5a0 - limit user name suggests to 5 and allow usage of @ for mention 
* b2c5a17 - fix spacing in user menu popover and in room settings popover 
* 8fa9b39 - reoder options in add room menu, add separator and change some lables 
* 5f15909 - fix title of dialog for joining a room by ID 
* b475b98 - center all dialog title on dialog width and refactor glade file 
* d9059f0 - fix the text of some lables 
* e5827c5 - spawn popover from room name and remove room-menu-button 
* 7b72f59 - add image to no room view and add new style #132 
* e92b20a - fix alignment of no room selected message by splitting the text to two label 
* 6f2fa3b - fix alignment for the text when no room is selected 
* fcb8b41 - set focus for each stack view, fix #118 
* c2a2816 - increase avatar size in the sidebar to 24px 
* d7dc175 - make user menu button avatar 24px 
* f94c558 - make room avatar in header 24px if no description 
* 7b4fe55 - show user info in popover 
* 9a0d36b - split user menu in two menus and remove the title in the left headerbar 
* 3c80382 - add spinner to initial sync notification 
* 1db7847 - [refactor] use headerbars in the titlebar instate of boxes 
* 2538993 - adjust spacing on first message in group 
* 6db14b6 - set timestamp fontsize to small 
* 3a92de6 - fix spacing around messages 
* f355fdc - add spacing to load more button and center it 

Some of my work went into libraries so it can be used by other applications, and one big merge request is still work in progress. I will continue to work on these git repositories, therefore I added the last commit for each repo done during GSoC:

The Gspell rust bindings:
https://gitlab.gnome.org/jsparber/gspell-rs
(last commit: 1501c997e2450378ea7e8291c94fa8189bb360df)
https://gitlab.gnome.org/jsparber/gspell-sys-rs
(last commit: 158e8ebbe5aeafc96663e3e76fc9a25715198264)

The libhandy rust bindings:
https://gitlab.gnome.org/jsparber/libhandy-rs
(last commit: f174e76882896c32959d0d16fe11eaadfce1c674)
https://gitlab.gnome.org/jsparber/libhandy-sys-rs
(last commit: 2caa9f6e0b68d391a6cdb45123f85c73b9852553)

I also created a libhandy test branch in Fractal, but I got never around merging it, because of a bug in libhandy (though it is fixed now, so it could be merged soon)
https://gitlab.gnome.org/jsparber/fractal/tree/handy
(last commit: b28da9a7443c34989dc8703402e6bc154a962f57)

The code to generate an avatar based on usernames:
https://gitlab.gnome.org/jsparber/letter-avatar
(last commit: 083e65e6ae8f63dc4071d3fac47adaa81e99d607)

There’s already a work in progress MR for the room history refactor:
https://gitlab.gnome.org/World/fractal/merge_requests/184
(last commit: 38abd3ceb3eb6d0ef1e70f66c7b0139ad65413b4)

August 10, 2018

The birth of a new runtime

Runtimes are a core part of the flatpak design. They are a way to make bundling feasible, while still fully isolating from the host system. Application authors can bundle the libraries specific to the application, but don’t have to care about the lowlevel dependencies that are uninteresting (yet important) for the application.

Many people think of runtimes primarily as a way to avoid duplication (and thus bloat). However, they play two other important roles. First of all they allow an independent stream of updates for core libraries, so even dead apps get fixes. And secondly, they allow the work of the bundling to be shared between all application authors.

There are some runtimes bases on pre-existing distribution packages, such as Fedora and Debian. These are very useful if you want to produce flatpaks of the packages from these distributions. However, most of the “native” Flatpaks these days are based on the Freedesktop 1.6 runtime or one of its derivates (like the Gnome and KDE runtimes).

Unfortunately this runtime is starting to show its age.

The freedesktop runtime is built in two steps. The first is based on Yocto, which is a cross-compilation system maintained by the Linux Foundation. An image is created from this build which is then further extended using flatpak-builder. This was a great way to get something going initially. However, Yocto focuses mainly on cross compilation and embedded which isn’t a great fit, and the weird 2 layer split and the complex yocto build files lead to very few people being able to build or do any work on the runtime. It also didn’t help that the build system was a bunch of crappy scripts that needed a lot of handholding by me.

Fortunately this is now getting much better, because today the new Freedesktop runtime, version 18.08, was released!

This runtime has the same name, and its content is very similar, but it is really a complete re-implementation. It is based on a new build system called BuildStream, which is much nicer and a great fit for flatpak. So, no more Yocto, no more buildbake, no multi-layer builds!

Additionally, it has an entire group of people working on it, including support from Codethink. Its already using gitlab, with automatic builds, CI, etc, etc. There is also a new release model (year.month) with a well-defined support time. Also, all the packages are much newer!

Gnome is also looking at using this as the basics for its releases, its CI system and eventually the Gnome runtime.

The old freedesktop runtime is dead, long live the freedesktop runtime!

GSoC 2018: Overview

Introduction

Throughout the summer I was working on librsvg, a GNOME library for rendering SVG files to Cairo surfaces. This post is an overview of the work I did with relevant links.

My Results

For the project I was to port the SVG filter infrastructure of librsvg from C to Rust, adding all missing filter tests from the SVG test suite along the way. I was also expected to implement abstractions to make the filter implementation more convenient, including Rust iterators over the surface pixels.

Here’s a list of all merge requests accepted into librsvg as part of my GSoC project:

Here’s a convenient link to see all of these merge requests in GitLab: https://gitlab.gnome.org/GNOME/librsvg/merge_requests?scope=all&utf8=%E2%9C%93&state=all&author_username=YaLTeR&label_name[]=GSoC%202018

All of this code was accepted into the mainline and will appear in the next stable release of librsvg.

I also wrote the following blog posts detailing some interesting things I worked on as part of the GSoC project:

Further Work

There are a couple of fixes which still need to be done for filters to be feature-complete:

  • Fixing filters operating on off-screen nodes. Currently all intermediate surfaces are limited to the original SVG view area so anything off-screen is inaccessible to filters even when it should be. This is blocked on some considerable refactoring in the main librsvg node drawing code which is currently underway.
  • Implementing the filterRes property. This property allows to set the pixel resolution for filter operations and is one of the ways of achieving more resolution-independent rendering results. While it can be implemented with the current code as is, it will be much more convenient to account for it while refactoring the code to fix the previous issue.
  • Implementing the enable-background property. The BackgroundImage filter input should adhere to this property when picking which nodes to include in the background image, whereas it currently doesn’t.

GSoC 2018: Parallelizing Filters with Rayon

Introduction

I’m working on SVG filter effects in librsvg, a GNOME library for rendering SVG files to Cairo surfaces. After finishing porting all filters from C to Rust and adding tests, I started investigating the filter performance. With the codebase converted to Rust, I am able to confidently apply important optimizations such as parallelization. In this post I’ll show how I parallelized two computation-intensive filter primitives.

Rayon

Rayon is a Rust crate for introducing parallelism into existing code. It utilizes Rust’s type system to guarantee memory safety and data-race free execution. Rayon mimics the standard Rust iterators, so it’s possible to convert the existing iterator-using code with next to no changes. Compare the following single-threaded and parallel code:

// Single-threaded.
fn sum_of_squares(input: &[i32]) -> i32 {
    input.iter()
         .map(|i| i * i)
         .sum()
}

// Parallelized with rayon.
use rayon::prelude::*;
fn sum_of_squares(input: &[i32]) -> i32 {
    input.par_iter()
         .map(|i| i * i)
         .sum()
}

By merely using .par_iter() instead of .iter(), the computation becomes parallelized.

Parallelizing Lighting Filters

Going forward with analyzing and improving the performance of the infamous mobile phone case, the two biggest time sinks were the lighting and the Gaussian blur filter primitives. It can be easily seen on the callgrind graph from KCachegrind:

KCachegrind graph for filter rendering

Since I was working on optimizing the lighting filters, I decided to try out parallelization there first.

The lighting filter primitives in SVG (feDiffuseLighting and feSpecularLighting) can be used to cast light onto the canvas using a render of an existing SVG node as a bump map. The computation is quite involved, but it boils down to performing a number of arithmetic operations for each pixel of the input surface independently of the others—a perfect target for parallelization.

This is what the code initially looked like:

let mut output_surface = ImageSurface::create(
    cairo::Format::ARgb32,
    input_surface.width(),
    input_surface.height(),
)?;

let output_stride = output_surface.get_stride() as usize;
let mut output_data = output_surface.get_data().unwrap();

let mut compute_output_pixel = |x, y, normal: Normal| {
    let output_pixel = /* expensive computations */;

    output_data.set_pixel(output_stride, output_pixel, x, y);
};

// Compute the edge pixels
// <...>

// Compute the interior pixels
for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
    for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
        compute_output_pixel(
            x,
            y,
            interior_normal(&input_surface, bounds, x, y),
        );
    }
}

The edge pixel computation is separated out for optimization reasons and it’s not important. We want to focus on the main loop over the interior pixels: it takes up the most time.

What we’d like to do is to take the outer loop over the image rows and run it in parallel on a thread pool. Since each row (well, each pixel in this case) is computed independently of the others, we should be able to do it without much hassle. However, we cannot do it right away: the compute_output_pixel closure mutably borrows output_data, so sharing it over multiple threads would mean multiple threads get concurrent mutable access to all image pixels, which could result in a data race and so won’t pass the borrow checker of the Rust compiler.

Instead, we can split the output_data slice into row-sized non-overlapping chunks and feed each thread only those chunks that it needs to process. This way no thread can access the data of another thread.

Let’s change the closure to accept the target slice (as opposed to borrowing it from the enclosing scope). Since the slices will start from the beginning of each row rather than the beginning of the whole image, we’ll also add an additional base_y argument to correct the offsets.

let compute_output_pixel =
    |mut output_slice: &mut [u8],
     base_y,
     x,
     y,
     normal: Normal| {
        let output_pixel = /* expensive computations */;

        output_slice.set_pixel(
            output_stride,
            output_pixel,
            x,
            y - base_y,
        );
    };

// Compute the interior pixels
for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
    for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
        compute_output_pixel(
            output_data,
            0,
            x,
            y,
            interior_normal(&input_surface, bounds, x, y),
        );
    }
}

Now we can convert the outer loop to operate through iterators using the chunks_mut() method of a slice which does exactly what we want: returns the slice in evenly sized non-overlapping mutable chunks.

let compute_output_pixel =
    |mut output_slice: &mut [u8],
     base_y,
     x,
     y,
     normal: Normal| {
        let output_pixel = /* expensive computations */;

        output_slice.set_pixel(
            output_stride,
            output_pixel,
            x,
            y - base_y,
        );
    };

// Compute the interior pixels
let first_row = bounds.y0 as u32 + 1;
let one_past_last_row = bounds.y1 as u32 - 1;
let first_pixel = (first_row as usize) * output_stride;
let one_past_last_pixel =
    (one_past_last_row as usize) * output_stride;

output_data[first_pixel..one_past_last_pixel]
    .chunks_mut(output_stride)
    .zip(first_row..one_past_last_row)
    .for_each(|(slice, y)| {
        for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
            compute_output_pixel(
                slice,
                y,
                x,
                y,
                interior_normal(
                    &input_surface,
                    bounds,
                    x,
                    y,
                ),
            );
        }
    });

And finally, parallelize by simply changing chunks_mut() to par_chunks_mut():

use rayon::prelude::*;

output_data[first_pixel..one_past_last_pixel]
    .par_chunks_mut(output_stride)
    .zip(first_row..one_past_last_row)
    .for_each(|(slice, y)| {
        for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
            compute_output_pixel(
                slice,
                y,
                x,
                y,
                interior_normal(
                    &input_surface,
                    bounds,
                    x,
                    y,
                ),
            );
        }
    });

Let’s see if the parallelization worked! Here I’m using time to measure how long it takes to render the mobile phone SVG.

Before parallelization:

└─ time ./rsvg-convert -o temp.png mobile_phone_01.svg
6.95user 0.66system 0:07.62elapsed 99%CPU (0avgtext+0avgdata 270904maxresident)k
0inputs+2432outputs (0major+714373minor)pagefaults 0swaps

After parallelization:

└─ time ./rsvg-convert -o temp.png mobile_phone_01.svg
7.47user 0.63system 0:06.04elapsed 134%CPU (0avgtext+0avgdata 271328maxresident)k
0inputs+2432outputs (0major+714460minor)pagefaults 0swaps

Note that even though the user time went up, the elapsed time went down by 1.5 seconds, and the CPU utilization increased past 100%. Success!

Parallelizing Gaussian Blur

Next, I set out to parallelize Gaussian blur, the biggest timesink for the phone and arguably one of the most used SVG filters altogether.

The SVG specification hints that for all reasonable values of the standard deviation parameter the blur can be implemented as three box blurs (taking the average value of the pixels) instead of the much more costly Gaussian kernel convolution. Pretty much every SVG rendering agent implements it this way since it’s much faster and librsvg is no exception. This is why you’ll see functions called box_blur and not gaussian_blur.

Both box blur and Gaussian blur are separable convolutions, which means it’s possible to implement them as two passes, one of which is a loop blurring each row of the image  and another is a loop blurring each column of the image independently of the others. For box blur specifically it allows for a much more optimized convolution implementation.

In librsvg, the box blur function contains an outer loop over the rows or the columns of the input image, depending on the vertical argument and an inner loop over the columns or the rows, respectively. It uses i and j for the outer and inner loop indices and has some helper functions to convert those to the actual coordinates, depending on the direction.

// Helper functions for getting and setting the pixels.
let pixel = |i, j| {
    let (x, y) = if vertical { (i, j) } else { (j, i) };

    input_surface.get_pixel_or_transparent(bounds, x, y)
};

let mut set_pixel = |i, j, pixel| {
    let (x, y) = if vertical { (i, j) } else { (j, i) };

    output_data.set_pixel(output_stride, pixel, x, y);
};

// Main loop
for i in other_axis_min..other_axis_max {
    // Processing the first pixel
    // <...>

    // Inner loop
    for j in main_axis_min + 1..main_axis_max {
        // <...>
    }
}

Trying to convert this code to use chunks_mut() just like the lighting filters, we stumble on an issue: if the outer loop is iterating over columns, rather than rows, the output slices for all individual columns overlap (because the pixels are stored in row-major order). We need some abstraction, like a matrix slice, that can be split into non-overlapping mutable subslices by rows or by columns.

The first thing that comes to mind is to try using the Matrix type from the nalgebra crate which does have that functionality. However, it turns out that nalgebra doesn’t currently support rayon or even have by-row or by-column iterators. I tried implementing my own iterators but that required some very non-obvious unsafe code with odd trait bound restrictions which I really wasn’t sure were correct. Thus, I scrapped that code and made my own wrapper for the ImageSurface which only contains things needed for this particular use case.

To reiterate, we need a wrapper that:

  • provides write access to the image pixels,
  • can be split by row or by column into non-overlapping chunks,
  • is Send, i.e. can be safely sent between threads (for parallelizing).

Here’s what I came up with:

struct UnsafeSendPixelData<'a> {
    width: u32,
    height: u32,
    stride: isize,
    ptr: NonNull<u8>,
    _marker: PhantomData<&'a mut ()>,
}

unsafe impl<'a> Send for UnsafeSendPixelData<'a> {}

impl<'a> UnsafeSendPixelData<'a> {
    /// Creates a new `UnsafeSendPixelData`.
    ///
    /// # Safety
    /// You must call `cairo_surface_mark_dirty()` on the
    /// surface once all instances of `UnsafeSendPixelData`
    /// are dropped to make sure the pixel changes are
    /// committed to Cairo.
    #[inline]
    unsafe fn new(
        surface: &mut cairo::ImageSurface,
    ) -> Self {
        assert_eq!(
            surface.get_format(),
            cairo::Format::ARgb32
        );
        let ptr = surface.get_data().unwrap().as_mut_ptr();

        Self {
            width: surface.get_width() as u32,
            height: surface.get_height() as u32,
            stride: surface.get_stride() as isize,
            ptr: NonNull::new(ptr).unwrap(),
            _marker: PhantomData,
        }
    }

    /// Sets a pixel value at the given coordinates.
    #[inline]
    fn set_pixel(&mut self, pixel: Pixel, x: u32, y: u32) {
        assert!(x < self.width);
        assert!(y < self.height);

        let value = pixel.to_u32();

        unsafe {
            let ptr = self.ptr.as_ptr().offset(
                y as isize * self.stride + x as isize * 4,
            ) as *mut u32;
            *ptr = value;
        }
    }

    /// Splits this `UnsafeSendPixelData` into two at the
    /// given row.
    ///
    /// The first one contains rows `0..index` (index not
    /// included) and the second one contains rows
    /// `index..height`.
    #[inline]
    fn split_at_row(self, index: u32) -> (Self, Self) {
        assert!(index <= self.height);

        (
            UnsafeSendPixelData {
                width: self.width,
                height: index,
                stride: self.stride,
                ptr: self.ptr,
                _marker: PhantomData,
            },
            UnsafeSendPixelData {
                width: self.width,
                height: self.height - index,
                stride: self.stride,
                ptr: NonNull::new(unsafe {
                    self.ptr
                        .as_ptr()
                        .offset(index as isize * self.stride)
                }).unwrap(),
                _marker: PhantomData,
            },
        )
    }

    /// Splits this `UnsafeSendPixelData` into two at the
    /// given column.
    ///
    /// The first one contains columns `0..index` (index not
    /// included) and the second one contains columns
    /// `index..width`.
    #[inline]
    fn split_at_column(self, index: u32) -> (Self, Self) {
        assert!(index <= self.width);

        (
            UnsafeSendPixelData {
                width: index,
                height: self.height,
                stride: self.stride,
                ptr: self.ptr,
                _marker: PhantomData,
            },
            UnsafeSendPixelData {
                width: self.width - index,
                height: self.height,
                stride: self.stride,
                ptr: NonNull::new(unsafe {
                    self.ptr
                        .as_ptr()
                        .offset(index as isize * 4)
                }).unwrap(),
                _marker: PhantomData,
            },
        )
    }
}

The wrapper contains a pointer to the data rather than a mutable slice of the data, so the intermediate pixels (which cannot be accessed through set_pixel()) are not mutably aliased between different instances of UnsafeSendPixelData.

Now it’s possible to implement an iterator over the rows or the columns for this wrapper, however I went with a different, simpler approach: using rayon‘s scope functionality which allows spawning worker threads directly into rayon‘s thread pool.

First, let’s change the existing code to operate on individual rows or columns, just like we did with the lighting filters:

// The following loop assumes the first row or column of
// `output_data` is the first row or column inside `bounds`.
let mut output_data = if vertical {
    output_data.split_at_column(bounds.x0 as u32).1
} else {
    output_data.split_at_row(bounds.y0 as u32).1
};

for i in other_axis_min..other_axis_max {
    // Split off one row or column and launch its processing
    // on another thread. Thanks to the initial split before
    // the loop, there's no special case for the very first
    // split.
    let (mut current, remaining) = if vertical {
        output_data.split_at_column(1)
    } else {
        output_data.split_at_row(1)
    };

    output_data = remaining;

    // Helper function for setting the pixels.
    let mut set_pixel = |j, pixel| {
        // We're processing rows or columns one-by-one, so
        // the other coordinate is always 0.
        let (x, y) = if vertical { (0, j) } else { (j, 0) };
        current.set_pixel(pixel, x, y);
    };

    // Processing the first pixel
    // <...>

    // Inner loop
    for j in main_axis_min + 1..main_axis_max {
        // <...>
    }
}

I could avoid the current and the base_i arguments to the set_pixel closure because I can declare the closure from within the loop, whereas in the lighting filters code the compute_output_pixel closure had to be used and so declared outside of the main loop.

Now it’s a simple change to split the work across rayon‘s threads:

// The following loop assumes the first row or column of
// `output_data` is the first row or column inside `bounds`.
let mut output_data = if vertical {
    output_data.split_at_column(bounds.x0 as u32).1
} else {
    output_data.split_at_row(bounds.y0 as u32).1
};

// Establish a scope for the threads.
rayon::scope(|s| {
    for i in other_axis_min..other_axis_max {
        // Split off one row or column and launch its
        // processing on another thread. Thanks to the
        // initial split before the loop, there's no special
        // case for the very first split.
        let (mut current, remaining) = if vertical {
            output_data.split_at_column(1)
        } else {
            output_data.split_at_row(1)
        };

        output_data = remaining;

        // Spawn the thread for this row or column.
        s.spawn(move |_| {
            // Helper function for setting the pixels.
            let mut set_pixel = |j, pixel| {
                // We're processing rows or columns
                // one-by-one, so the other coordinate is
                // always 0.
                let (x, y) =
                    if vertical { (0, j) } else { (j, 0) };
                current.set_pixel(pixel, x, y);
            };

            // Processing the first pixel
            // <...>

            // Inner loop
            for j in main_axis_min + 1..main_axis_max {
                // <...>
            }
        });
    }
});

Let’s measure the performance. In the end of the previous section we had:

└─ time ./rsvg-convert -o temp.png mobile_phone_01.svg
7.47user 0.63system 0:06.04elapsed 134%CPU (0avgtext+0avgdata 271328maxresident)k
0inputs+2432outputs (0major+714460minor)pagefaults 0swaps

And now after the parallelization:

└─ time ./rsvg-convert -o temp.png mobile_phone_01.svg
10.32user 1.10system 0:04.57elapsed 250%CPU (0avgtext+0avgdata 272588maxresident)k
0inputs+2432outputs (0major+1009498minor)pagefaults 0swaps

We cut another 1.5 seconds and further increased the CPU utilization!

Conclusion

Rayon is an excellent crate which provides a multitude of ways for safely parallelizing Rust code. It builds on idiomatic concepts such as iterators, but also contains a number of other convenient ways for parallelizing code when standard iterators aren’t sufficient or wouldn’t be very convenient. It uses the Rust’s type system to statically guarantee the absence of data races and manages the low-level details on its own allowing the programmer to focus on the actual computation.

The parallelized filters are now included in the latest development release of librsvg for testing if using multiple threads leads to any issues downstream.

Feeds