February 13, 2020

GTK 3.98

A few days ago, I’ve released a GTK 3.98 tarball. This is another step towards GTK 4. It is a little bit behind schedule, and does not quite include all the things we wanted to get into it, but it gets a lot closer to what we want to ship in GTK 4.

Almost 9 months have passed since the 3.96 snapshot, so there are quite a few new things to look at. Too many to cover them all, but here are some highlights:

Performance

The GL renderer has seen a steady flow of optimizations and performance improvements.

After the Westcoast hackfest last year, the GtkTextView scrolling performance has been greatly improved by making it cache render nodes for the visible range. At the same hackfest, the text caret blinking was changed to be smoothly animated, which is not relevant for performance at all, but looks cool.

Since the new year, a big focus has been on improving the performance of the CSS machinery. The CSS value implementation has been optimized to avoid computing values whenever possible. CSS lookups are using a Bloom filter now. And the IO for icon loading has been moved to a thread.

Most of the recent work was made possible by the sysprof profiling support that was added after the performance hackfest, and has recently been enhanced to report more information. To use it, simply start a GTK application with GTK_TRACE=1 in the environment, and load the resulting syscap file with sysprof.

DND

The DND refactoring has been completed. The GTK API for DND has been turned into event controllers: GtkDragSource and GtkDropTarget. Support for file transfers via file transfer portal has been added for both DND and the clipboard.  The underlying new infrastructure for data transfers has been covered in detail before.

GDK

The move of GDK towards Wayland concepts is continuing. This cleanup is not 100% complete yet.

Child surfaces have been removed. GDK only supports toplevel and popup surfaces now. The client-side window implementation has been removed too. Global positions and related APIs such as gdk_surface_move() are no longer available.

Grabs are no longer exposed as API. As a replacement, popup surfaces can be configured to hide on outside clicks.

XI2 is now mandatory when building the X11 backend, and support for the xim input method has been removed in favor of IBus.

The Wayland backend no longer relies on libwayland-cursor to load cursor themes, and loads individual cursors on demand.

GTK removals

Many classes have been made explicitly non-subclassable, and the widget hierarchy has been simplified, by making widgets derive directly from GtkWidget where possible.

GtkMenu, GtkMenuBar, GtkToolbar and related classes have been removed. They are being replaced by GMenu and popover-based variants. Popover menus can now do traditional, nested menus, and also show accelerators.

Context menus are no longer created with ::populate-popup signals, but also use menu models and actions. Creating those actions has been made easier with APIs like gtk_widget_class_install_action(), to create them in class_init.

GtkGestureMultiPress has been renamed to GtkGestureClick, to make it more obvious what this event controller is for.

GTK additions

We did not just remove things. Some new things have been added too.

The GtkNative interface has been introduced for widgets that have their own surface. This has been split off from the GtkRoot interface, which is exclusively for toplevel widgets without a parent.

A constraint-based layout manager has been added. It would be great to see people try this out. Please give us feedback if you do.

GtkTextView and other text widgets have gained a simple undo stack that can be used with Ctrl-Z.

The Emoji chooser widget has been made public.

Whats ahead

After 3.98, I’m planning to do more frequent snapshots, as the remaining outstanding items are landing. What are those items, you are asking ?

Here are the things that we still want to integrate before GTK 4:

– Event controllers for keyboard shortcuts
– Movable popovers
– Row-recycling list and grid views
– Revamped accessibility infrastructure
– Animation API

 

 

February 12, 2020

Welcome 2020

Disclaimer: this article represents my own personal opinions and thoughts. Not my employer’s, nor GNOME’s, but my own.

This is coming a bit late, and I was not convinced it would be a good idea to publish, but ultimately concluded it was important enough. Bear with me, this is an chaotic set of written ideas. They are not supposed to make sense, and I’m not trying to construct a narrative here.

Year is now 2020. Depending on the lens you use to observe reality, this may be a good or a terrible year.

After taking a few weeks off in January, I realized how much I did not do in my personal life. This is definitely an area that should be improved. A few years ago, taking time off from work and open source wasn’t an exciting thought; nowadays, though, I’m feeling the weight of spending too much time in front of a computer.

I’m continuously failing to keep up with the Friends of GNOME donors. I really have to empty this queue as soon as possible.

I’m really unhappy with the political situation of my country, Brazil. A halfwitted, fascism flirting populist was democratically elected. The public institutions were dominated by their inapt followers. Rich are getting richier, poor are getting poorer, inequality is skyrocketing, and despite all of that, a massive number of citizens seems to be applauding this madness, regardless if they’re profiting or not with this situation. I’m not comfortable with the idea of living here. I’m also not comfortable with the idea of leaving family behind. It seems this trend is spreading all around the world, so where else could I go anyway?

I’ve also stopped training martial arts. I got involved with Aikido when I was 14. I was a vulnerable, not intellectually emancipated teenager that needed emotional crutches to carry on. For years, Aikido was part of my identity, and I would ignore blatant problems that surrounded it for the sake of keeping the narrative. Feeling like a virtuous warrior was good, after all. Over time, and with the maturity that came with it, the toxicity of it took a toll on me. Quitting it was traumatic. I still feel a big void.

Quitting martial arts meant I stopped exercising. Turns out, the lack of physical activities, together with an awful political climate, and the stress of being an open source maintainer, is an express highway to depression. When I wrote “On Being a Free Software Maintainer“, I was already going downhill. Things got progressively worse until around GUADEC. Fortunately, the support from the GNOME community, my wife, family, and friends, were strong enough to allow me break this downward spiral.

I do not know what would have happened without this support. To my family, wife, friends, and the GNOME community: thank you all so much for being here when I most needed.

2020 is the year that I’ll try to not drop the ball again. I’ve started attending a gym. My live coding sessions are going well, with the community coming together and having a good time. I did take some time off, and am traveling with a slightly higher frequency, and that feels really good too.

This will be a technically challenging year: I’ve added 2 other modules to my maintainership list (Mutter and GNOME Shell), and have many ideas for them, including code refactorings, new features, cleanups, etc. We have bootstrapped a new blog for Mutter & GNOME Shell development, with the expectation that it will prevent misinformation from spreading, and will be a reliable source for tech news to use. I’m also resuming my Scrum routine to Calendar, To Do, and Settings. I’ll continue experimenting with ways to organize my routine to optimize it as much as possible, while still preserving personal time for marriage, house, and social life.

Fortunately, my employer Endless allows me to work on upstream GNOME features from time to time, and allows me to do some maintainership tasks using work time. This is a huge motivational boost to me.

Welcome 2020. I hope you’ll be a good year.

How do you say “desktop environment” in Flemish? FOSDEM 2020 Trip Report

A cake shaped like two gears with a red "20" printed on it.
Photo courtesy of FOSDEM. Licensed CC-BY

FOSDEM is one of the biggest community organized conferences in Europe. Run by a team of dedicated volunteers, the conference has been going for 20 years. It’s one of the biggest yearly events for us at GNOME Foundation and a rare opportunity for the staff to come together.

As a fully remote team, the GNOME Foundation staff all get together twice a year to strategize, plan, and collaborate at GUADEC and at FOSDEM. This is also when the Foundation Board of Directors and Advisory Board have the chance to meet in person.

In the four days leading up to the event, GTK Core Developer Emanuelle Bassi and Matthias Classen hosted a hackfest focused on GKT and the future of accessibility in GNOME. We really appreciate everyone who showed up, and would especially like to thank the blind participants and those with vision issues and expertise as those using the accessibility tools.

A stack of liege waffles on a red plate.
Photo courtesy of osiristhe on Flickr. Licensed CC BY-ND 2.0

Prior to the conference, we had two days of meetings – one for the Board and one for what we affectionately call the “AdBoard.” At both we learned and planned, which we’re looking forward to sharing with you over the upcoming months.

While Executive Director Neil McGovern and Director of Operations Rosanna Yuen met with the Board of Directors, I attended Sustain Summit. I led a session on diversity in open source with a focus on building global movements.

On Friday the entire staff met with the AdBoard. These meetings are focused around what our AdBoard members are up to, what GNOME has done over the past six months, and what we’re looking forward to over the next six months. It is a time for questions and answers, and also a place to find points of collaboration and to help the Foundation understand what is happening for organization stakeholders, where their interests, successes, and pain points lie.

Buildings of Grand Place in Brussels, Belgium.
Photo courtesy of Friar’s Balsam on Flickr. Licensed CC BY 2.0

FOSDEM itself was a whirwind. On the first day we participated in three sessions. Neil participated in a friendly debate over whether the four freedoms and the Open Source Definition are still relevant today. I had a lot of fun arguing in support of the creation and adoption of licenses to advance social issues. I could also be found in the main track talking about the ethics of Internet of Things devices and how they impact communities.

While all of this was going on, the rest of the staff attended sessions, had meetings, and helped with the GNOME booth in the exhibit hall. The GNOME booth was amazingly successful. Caroline Henriksen, Brand Manager, and Melissa Wu, who is coordinating the GNOME Education Challenge, talked about our upcoming plans for Challenge. Program Coordinator Kristi Progri spent her time representing the GNOME Foundation to stakeholders, meeting participants in the project, and forged new connections for the future.

Saturday night we had a fun evening at BAR NAME SOMETHING BOOI for GNOME Beers. Contributors and FOSDEM attendees came to hang out, have a drink, and have a chance to meet one another in person. Meanwhile, I attended Open Source Funding Speed Dating, serving as a judge to help fund up-and-coming free and open source projects.

The Sunday of FOSDEM was full of meetings, more sessions, and, of course, the GNOME booth.The GNOME booth wouldn’t be possible without the awesome volunteers who took on so much of the responsibility of running it. Thank you, Adrien, Anisa, Bastian, Ben, Bilal, and Kat!

On Monday, several of us attended CopyLeft Conf, a conference hosted by our friends at the Software Freedom Conservancy.

As I write this, I am on the plane headed home. Reflecting on the great week I had in Brussels, Belgium, I recognize the role all of you had in making it happen. Your support of the GNOME Foundation makes it possible for us to attend events like this. Without you we wouldn’t be able to bring the Board of Directors together in person. We wouldn’t be representing our project and the value of community built, free and open software. We wouldn’t have been able to host the hackfest that pushed forward GTK development and accessibility planning. We wouldn’t have been able to bring other GNOME contributors to the event, by funding their travel.

Thank you so much! I believe this was my fourth FOSDEM, and my first with GNOME. It was definitely the best yet.

February 11, 2020

Robocode and others

As expressed in a previous post, I prefer to spend my free time with my kids than with technology (for technology I already have my job). However, when there is an exception to that, I do like to do some sort of smaller projects, like “porting” stuff to Flatpak.

I did my share of Debian and RPM packaging in the past, and honestly I have never enjoyed it (for a number of reasons not really interesting for this post). But “flatpaking” stuff is completely different to me. Maybe it’s my early involvement with it, or maybe it’s my admiration for how its designed, but the feeling when making a Flatpak is of reward, rather than a chore.

Robocode

Around 15 (fifteen!) years ago, while at University, my colleagues and I enjoyed this quirky little program called Robocode: a game where you load multiple tanks coded (hopefully by you and your friends) using a small Java API, and let them fight each other until one stands. At the time, my parents still didn’t have broadband at home, so when I visited them, I spent some time coding “the best” tank (it was more fun and less sad than it sounds…)!

Thus, roughly 5500 days later, I thought it’d be cool to have Robocode in the easy to install/run way that Flatpak and Flathub give us, and I finally made this into a small Christmas project a couple of months ago.

Small detour into other tanks

A couple of years ago, in a brainstorm with my Endless’ colleagues at the time, I mentioned my nostalgic fascination for Robocode, and how it could inspire a mode for an Endless’ game we were testing called Tank Warriors.

Matt liked the idea so I wrote a draft of this game mode for the game developers to follow, and got to test a couple of early versions once it was built. Thus, if you are interested in a more modern approach to Robocode (and using Javascript rather than Java), check out the Tank Warriors on Flathub.

Get it while it’s hot warm

Robocode has been available on Flathub since early January, but only now I found the time to blog about it…
In any case, if you’re getting into programming now, I really hope you enjoy Robocode and that it inspires you to keep learning!

Get Robocode for Flatpak now!

Other oldies

As mentioned, I enjoy porting software on Flatpak in general, but in particular I like porting old stuff. It’s a chance to have often forgotten projects in a way that’s expected to just run. So I am taking this chance to mention a couple of other games I ported a couple of years ago but didn’t blog about.
Those are rRootage and noiz2sa, two psycadelic shooters created by the Japanese game developer Kenta Cho, and they’re among the first games I played on Linux. If you’re into minimalist and psychadelic graphics, be sure to check them out.

I wonder what other cool old software should be made available as Flatpak.

How to Run a Usability Test

One of the most important steps of the design process is “usability testing”, it gives designers the chance to put themselves in other people’s shoes by gathering direct feedback from people in real time to determine how usable an interface may be. This is just as important for free and open source software development process as it is any other.

Though free software projects often lack sufficient resources for other more extensive testing methods, there are some basic techniques that can be done by non-experts with just a bit of planning and time—anyone can do this!

Free Software Usability

Perhaps notoriously, free software interfaces have long been unapproachable; how many times have you heard: “this software is great…once you figure it out.” The steep learning curve of many free software applications is not representative of how usable or useful it is. More often than not it’s indicative of free software’s relative complexity, and that can be attributed to the focus on baking features into a piece of software without regard for how usable they are.

A screenshot of the calibre e-book management app's poor UI

Free software developers are often making applications for themselves and their peers, and the steps in development where you’d figure out how easy it is for other people to use—testing—gets skipped. In other words, as the author of the application you are of course familiar with how the user interface is laid out and how to access all the functionality, you wrote it. A new user would not be, and may need time or knowledge to discover the functionality, this is where usability testing can come in to help you figure out how easy your software is to use.

What is “Usability Testing”?

For those unfamiliar with the concept, usability testing is a set of methods in user-centric design meant to evaluate a product or application’s capacity to meet its intended purpose. Careful observation of people while they use your product, to see if it matches what it was intended for, is the foundation of usability testing.

The great thing is that you don’t need years of experience to run some basic usability tests, you need only sit down with a small group of people, get them to use your software, and listen and observe.

What Usability Testing is Not

Gathering people’s opinion (solicited or otherwise) on a product is not usability testing, that’s market research. Usability testing isn’t about querying people’s already formed thoughts on a product or design, it’s about determining if they understand a given function of a product or its purpose by having them use said product and gather feedback.

Usability is not subjective, it is concrete and measureable and therefore testable.

Preparing a Usability Test

To start, pick a series of tasks within the application that you want to test that you believe would be straightforward for the average person to complete. For example: “Set the desktop background” in a photos app, “Save a file with a new name” in a text editor, “Compose a new email” in an email client, etc. It is easiest to pick tasks that correspond to functions of your application that are (intended to be) evident in the user interface and not something more abstract. Remember: you are testing the user interface not the participant’s own ability to do a task.

You should also pick tasks that you would expect to take no more than a few minutes each, if participants fail to complete a task in a timely manner that is okay and is useful information.

Create Relatable Scenarios

To help would-be participants of your test, draft simple hypothetical scenarios or stories around these tasks which they can empathize with to make them more comfortable. It is very important in these scenarios that you do not use the same phrasing as present in the user interface or reference the interface as it would be too influential on the testers’ process. For instance, if you were testing whether an email client’s compose action was discoverable, you would not say:

Compose an email to your aunt using the new message button.

This gives too much away about the interface as it would prompt people to look for the button. The scenario should be more general and have aspects that everyone can relate to:

It’s your aunt’s birthday and you want to send her a well-wishes message. Please compose a new email wishing her a happy birthday.

These “relatable” aspects gives the participant something to latch onto and it makes the goal of the task clearer for them by allowing them to insert themselves into the scenario.

Finding Participants

Speaking of participants, you need at least five people for your test, after five there are diminishing returns as the more people you add, the less you learn as you’ll begin to see things repeat. This article goes into more detail, but to quote its summary:

Elaborate usability tests are a waste of resources. The best results come from testing no more than 5 users and running as many small tests as you can afford.

This is not to say that you stop after a single test with five individuals, it’s that repetitive tests with small groups allow you to uncover problems that you can address and retest efficiently, given limited resources.

Also, the more random the selection group is, the better the results of your test will be—“random” as if you grabbed passers-by the hallway or on the street. As a bonus, it’s also best to offer some sort of small gratuity for participating, to motivate people to sign up.

Warming Up Participants

It’s also important to not have the participants jump into a test cold. Give participants some background and context for the tests and brief them on what you are trying to accomplish. Make it absolutely clear that the goal is to test the interface, not them or their abilities; it is very important to stress to the participants that their completion of a task is not the purpose of the test but determining the usability of the product is. Inability to complete a task is a reflection of the design not of their abilities.

Preliminary Data Gathering

Before testing, gather important demographic information from your participants, things like age, gender (how they identify), etc. and gauge their level of familiarity with or knowledge of the product category, such as: “how familiar are you with Linux/GNOME/free software on a scale from 1-5?” All this will be helpful as you break down the test results for analysis to see trends or patterns across test results.

Running the Test

Present the scenarios for each task one at a time and separately as to not overload the participants. Encourage participants to give vocal feedback as they do the test, and to be as frank and critical as possible as to make the results more valuable, assuring them your feelings will not be hurt by doing so.

During the task you must but attentive and observe several things at once: the routes they take through your app, what they do or say during or about the process, their body language and the problems they encounter—this is where extensive note-taking comes in.

No Hints!

Do not interfere in the task at hand by giving hints or directly helping the participant. While the correct action may be obvious or apparent to you, the value is in learning what isn’t obvious to other people.

If participants ask for help it is best to respond with guiding questions; if a participant gets stuck, prompt them to continue with questions such as “what do you think you should do?” or “where do you think you should click?” but if they choose not finish or are unable to, that is okay.

Be Watchful

The vast majority of stumbling blocks are found by watching the body language of people during testing. Watch for signs of confusion or frustration—frowning, squinting, sighing, hunched shoulders, etc.—when a participant is testing your product and make note of it, but do not make assumptions about why they became frustrated or confused: ask them why.

It is perfectly alright to pause the test when you see signs of confusion or frustration and say:

I noticed you seemed confused/frustrated, care to tell me what was going through your mind when you were [the specific thing they were doing]?

It’s here where you will learn why someone got lost in your application and that insight is valuable.

Take Notes

For the love of GNU, pay close attention to the participants and take notes. Closely note how difficult a participant finds a task, what their body language is while they do the task, how long it takes them, and problems and criticisms participants have. Having participants think aloud or periodically asking them how they feel about aspects of the task, is extremely beneficial for your note-taking as well.

To supplement your later analysis, you may make use of screen and/or voice-recording during testing but only if your participants are comfortable with it and give informed consent. Do not rely on direct recording methods as they can often be distracting or disconcerting and you want people to be relaxed during testing so they can focus, and not be wary of the recording device.

Concluding the Test

When the tasks are all complete you can choose to debrief participants about the full purpose of the test and answer any outstanding questions they may have. If all goes well you will have some data that can be insightful to the development of your application and for addressing design problems, after further analysis.

Collating Results

Usability testing data is extremely useful to user experience and interaction designers as it can inform our decision-making over interface layouts, interaction models, etc. and help us solve problems that get uncovered.

Regardless of whether the testing and research is not conducted ourselves, it’s important that the data gathered is clearly presented. Graphs, charts and spreadsheets are incredibly useful in your write-up for communicating the break down of test results.

Heat Maps

It helps to visualize issues with tasks in a heat map, which is an illustration that accounts for the perceived difficulty of a given task for each participant by colour-coding them in a table.

Example Heat Map

The above is a non-specific example that illustrates how the data can be represented: green for successful completion of the task, yellow for moderate difficulty, red for a lot of difficulty, and black for an incomplete. From this heat map, we can immediately see patterns that we can address by looking deeper in the results; we can see how “Task 1” and Task 6” presented a lot of difficulty for most of the participants, and that requires further investigation.

More Usable Free Software

Conducting usability testing on free software shouldn’t be an afterthought of the development process but rather it should be a deeply integrated component. However, the reality is that the resources of free software projects (including large ones like GNOME) are quite limited, so one of my goals with this post is to empower you to do more usability testing on your own—you don’t have to be an expert—and to help out and contribute to larger software projects to make up for the limits on resources.

Usability Testing GNOME

Since I work on the design of GNOME, I would be more than happy to help you facilitate usability testing for GNOME applications and software. So do not hesitate to reach out if you would like me to review your plans for usability testing or to share results of any testing that you do.


Further Reading

If you’re interested in more resources or information about usability, I can recommend some additional reading:

Joining Purism!

Personal news time! Starting in August I’m going to be joining the team at Purism working on the design of PureOS and related software products, but what I’m very excited about is that I get to continue to work on GNOME design!

Purism Logo

I have to thank Purism for even offering me this opportunity it is beyond my wildest expectations that I would get to to work on Free Software professionally let alone in design!

Clean and linear history with GitLab

Many GNOME projects still use clean and linear commit history without merge commits even after porting to GitLab. That means that each commit represents one comprehensive feature or bug fix and there are not any side branches. I am not about to discuss the pros and cons of this approach here, you can find many and many posts on this topic on the internet. I would like to explain some common issues for newcomers when using GitLab forks.

To make some contribution, one has to create a fork of some repository, push desired changes in a new branch and create a merge request to the original project. Please be sure that the “Allows commits from members who can merge to the target branch” checkbox is checked when creating the merge request (or later using the “Edit” button on the top of the page). This is needed to simplify the consequent workflow for the contributor (and maintainers as well). One of the reasons, why this is needed is the fact, that the changes need to be often rebased before they can be merged (to ensure the linear history). Maintainers can’t do this when this feature is not enabled and have to ask contributors to do so. Another reason is that the maintainers can do some changes when the contributor needs help or doesn’t have time to do the changes itself.

When the merge request is created, maintainers usually make a review of the proposed changes and ask the contributor to change something. The existing commits need to be updated using e.g. git commit --amend, or git rebase --interactive (to ensure clean history). Then git push --force has to be used to update the remote branch. This is the right way and it is expected that the --force option is used, there is no other way to update the existing commits.

See GNOME Wiki, GitLab Docs and Git Documentation to get more info about these topics.

February 09, 2020

state of the gnunion 2020

Greetings, GNU hackers! This blog post rounds up GNU happenings over 2019. My goal is to celebrate the software we produced over the last year and to help us plan a successful 2020.

Over the past few months I have been discussing project health with a group of GNU maintainers and we were wondering how the project was doing. We had impressions, but little in the way of data. To that end I wrote some scripts to collect dates and versions for all releases made by GNU projects, as far back as data is available.

In 2019, I count 243 releases, from 98 projects. Nice! Notably, on ftp.gnu.org we have the first stable releases from three projects:

GNU Guix
GNU Guix is perhaps the most exciting project in GNU these days. It's a package manager! It's a distribution! It's a container construction tool! It's a package-manager-cum-distribution-cum-container-construction-tool! Hearty congratulations to Guix on their first stable release.
GNU Shepherd
The GNU Daemon Shepherd is a modern dependency-based init service, written in Guile Scheme, and used in Guix. When you install Guix as an operating system, it actually stages Scheme programs from the operating system definition into the Shepherd configuration. So cool!
GNU Backgammon
Version 1.06.002 is not GNU Backgammon's first stable release, but it is the earliest version which is available on ftp.gnu.org. Formerly hosted on the now-defunct gnubg.org, GNU Backgammon is a venerable foe, and uses neural networks since before they were cool. Welcome back, GNU Backgammon!

The total release counts above are slightly above what Mike Gerwitz's scripts count in his "GNU Spotlight", posted on the FSF blog. This could be because in addition to files released on ftp.gnu.org, I also manually collected release dates for most packages that upload their software somewhere other than gnu.org. I don't count alpha.gnu.org releases, and there were a handful of packages for which I wasn't successful at retrieving their release dates. But as a first approximation, it's a relatively complete data set.

I put my scripts in git repository if anyone is interested in playing with the data. Some raw CSV files are there as well.

where we at?

Hair toss, check my nails, baby how you GNUing? Hard to tell!

To get us closer to an answer, I calculated the active package count per year. There can be other definitions, but my reading is that an active package is one that has had a stable release within the preceding 3 calendar years. So for 2019, for example, a GNU package is considered active if it had a stable release in 2017, 2018, or 2019. What I got was a graph that looks like this:

What we see is nothing before 1991 -- surely pointing to lacunae in my data set -- then a more or less linear rise in active package count until 2002, some stuttering growth rising to a peak in 2014 at 208 active packages, and from there a steady decline down to 153 active packages in 2019.

Of course, as a metric, active package count isn't precisely the same as project health; GNU ed is indeed the standard editor but it's not GCC. But we need to look for measurements that indirectly indicate project health and this is what I could come up with.

Looking a little deeper, I tabulated the first and last release date for each GNU package, and then grouped them by year. In this graph, the left blue bars indicate the number of packages making their first recorded release, and the right green bars indicate the number of packages making their last release. Obviously a last release in 2019 indicates an active package, so it's to be expected that we have a spike in green bars on the right.

What this graph indicates is that GNU had an uninterrupted growth phase from its beginning until 2006, with more projects being born than dying. Things are mixed until 2012 or so, and since then we see many more projects making their last release and above all, very few packages "being born".

where we going?

I am not sure exactly what steps GNU should take in the future but I hope that this analysis can be a good conversation-starter. I do have some thoughts but will post in a follow-up. Until then, happy hacking in 2020!

Trying to build a slightly larger slice of Libreoffice with Meson

One of the few comments I got on my previous blog post was that building only the sal library is uninteresting because it is so small. So let's go deeper and build the base platform of LO, which is called Uno. Based on docs and slides from conference presentations, it is roughly the marked area in LO's full dependency graph.


The gloves come off

A large fraction of the code is generated from IDL files. So one might imagine that if one has a file like com/sun/foo/bar.idl then one would convert that into a header file com/sun/foo/bar.hpp using a program called idlc. One would be wrong. That is not what happens at all.

When trying to understand what humongous Make builds are doing, the best approach is not to even try. Do not look at the Makefiles, or the macro code they include. Instead run the build, capture all executed commands with Make's verbose mode and reverse engineer what the sysetm is doing based on them. Implementing the same functionality is a lot easier this way, because you know exactly what command lines you must end up with. In this particular case we find that the idl files are compiled to an intermediate binary blob using a program called unoidl-write.  After that the header files (yes, multiple per idl file) are created from this blob with a different program called cppumaker.

This has the unfortunate side effect that when you run the header generator you can not know in advance what files the program will generate and what sort of a directory hierarchy they are put in without a side channel. This makes it very difficult to integrate with build systems, because they need to know outputs exactly in order to make build steps reliable. Java has the exact same problem. Because of the way it handles inner classes, the output set of a Java compilation is unknowable beforehand. If you ever find yourself designing a code generator tool, please make sure you don't have this Sunism in your program, as it makes integration needlessly complicated and unreliable.

Even if you manage to recreate the command lines with a different system, things may still fail in interesting ways. In LO's case the internal SAL file system library has a policy that callers are not allowed to create temporary files in a directory with a relative pathname. This limitation is reported with the helpful error message of "could not open  for writing". (In case blog aggregators break the formatting, there are two spaces between the words "open" and "for".) It is also possible that relative paths appear to succeed but produce error messages such as "can not read file in legacy format" later in the process.

Other fun stuff

If one were to look at the command lines that eventually get invoked, they would look like this:


This may seem overwhelming, so let's add some markers.


An interesting question is how many processes does this spawn per source file? I don't know the correct answer, but a reasonable guess would be either 4 or 7 (5 or 8 if you count the outer shell).

Status

The code can be obtained via Github. It's Linux only and some bits are fairly ugly. It does generate the UNO code and build all the libs, but unit tests, Java and other pieces are missing and dependency tracking does not work for code generators. The build definitions consist of ~550 lines of Meson. There are a few Python helper programs in addition to these. They have a lot of duplicated code, but for a prototype such as this one it's tolerable.

I tried to go a bit further and build basegfx, but then I got stuck. The generated code has #ifdef toggles that define whether some variables are defined as ints or enums. Other libraries fail to build either when the define is set or when it is unset. For some reason basegfx has multiple files that fail both when the define is set and when it is unset (with different error messages, though). Either the code generation step goes awry or there are even more magic defines that have to be set in order for things to work.

February 08, 2020

How to Flatpak a Rust application

Distributing and packaging your Rust GUI application and making it available for Linux users can be hard, I will try to explain the various ways of doing that using Flatpak as a packaging format.

I will be using a GTK + Rust application as an example to distribute it as a Flatpak in two different ways.

  • The first one, involves adding a build system, on top of Cargo to handle installing the different files we will need to ensure our application can be found, installed and used by the users. We will be using Meson
  • The second one, will just use Cargo and install those files manually during the Flatpak build

1 – Creating a simple application

Let’s start with creating a simple Rust project by typing

cargo new rust-flatpak

The first step would be adding our dependencies to Cargo.toml

[package]
name = "rust-flatpak"
version = "0.1.0"
authors = ["Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
gtk = "0.8"
gio = "0.8"

and create a simple window, the code is from github.com/gtk-rs/examples/. You can check that repository if you need more examples.

use gio::prelude::*;
use gtk::prelude::*;
use std::env::args;

fn build_ui(application: &gtk::Application) {
    let window = gtk::ApplicationWindow::new(application);

    window.set_title("First GTK+ Program");
    window.set_border_width(10);
    window.set_position(gtk::WindowPosition::Center);
    window.set_default_size(350, 70);

    let button = gtk::Button::new_with_label("Click me!");

    window.add(&button);

    window.show_all();
}

fn main() {
    let application =
        gtk::Application::new(Some("com.belmoussaoui.RustFlatpak"), Default::default())
            .expect("Initialization failed...");

    application.connect_activate(|app| {
        build_ui(app);
    });

    application.run(&args().collect::<Vec<_>>());
}

One important thing about how we create our a gtk::Application is the Application identifier we pass to it. If you don’t have an idea what an application id is, this guide should be complete https://developer.gnome.org/ChooseApplicationID/. Once we have finished building our application, the next step would be the packaging and distribution of what we just created.

We will need to prepare few files before doing so:

  • A metainfo, which is a a file that describes your application for stores like Flathub, GNOME Software, KDE Discover or elementary’s AppCenter. The specifications of this file can be found at https://www.freedesktop.org/software/appstream/docs/.
  • A desktop file, which is the launcher of the application that the user finds on their app launcher thing/dashboard. It specifies the app name, the binary that should be executed, an icon name and probably other stuff likes categories/keywords. You can read more about that here https://specifications.freedesktop.org/desktop-entry-spec/latest/
  • An icon, to make your app easier to find 🙂

2 – Packaging related files

Metainfo file

Metainfo file, should include as much information about the application that allows the user to find it easily. You should provide some decent screenshots of the application, release tags and OARS tag. Providing a URL to your website or where to translate/report issues/donate could be added too.

The specs contains enough information about that, but here’s an example of such file, it should be named following $app-id.metainfo.xml and installed under $prefix/$datadir/metainfo.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Bilal Elmoussaoui 2020 <bilal.elmoussaoui@gnome.org> -->
<component type="desktop-application">
  <id>com.belmoussaoui.RustFlatpak</id>
  <metadata_license>CC0</metadata_license>
  <project_license>GPL-3.0+</project_license>
  <name>Rust Flatpak</name>
  <summary>Rust Flatpak-ed app?</summary>
  <description>
    <p>This metainfo is part of a Rust Flatpak-ed application</p>
  </description>
  <screenshots>
    <screenshot type="default">
      <image>https://gitlab.gnome.org/World/design/contrast/raw/master/data/resources/screenshots/screenshot1.png</image>
      <caption>Main Window</caption>
    </screenshot>
  </screenshots>
  <url type="homepage">https://belmoussaoui.com/</url>
<!--
Open Age Rating Service
https://hughsie.github.io/oars/index.html
-->
  <content_rating type="oars-1.0" />
  <releases>
    <release version="0.0.1" date="2020-02-08">
      <description>
        <p>First release of Rust Flatpak App</p>
      </description>
    </release>
  </releases>
  <kudos>
    <!--
    GNOME Software kudos:
    https://gitlab.gnome.org/GNOME/gnome-software/blob/master/doc/kudos.md
    -->
    <kudo>ModernToolkit</kudo>
    <kudo>HiDpiIcon</kudo>
  </kudos>
  <developer_name>Bilal Elmoussaoui</developer_name>
  <update_contact>bilal.elmoussaoui@gnome.org</update_contact>

  <launchable type="desktop-id">com.belmoussaoui.RustFlatpak.desktop</launchable>
</component>


Desktop file

A desktop file is a simple launcher for the application, it shouldn’t contain more than. The file should be named com.belmoussaoui.RustFlatpak.desktop, if you look at the bottom of the metainfo file, we link the desktop file to it using the launchable tag. This allows the software center to figure out how to start your application.

The file should be installed under $prefix/$datadir/applications

[Desktop Entry]
Name=Rust Flatpak
Comment=Rust Flapaked application
Type=Application
# should be the same as the Cargo project name
Exec=rust-flatpak
Terminal=false
Categories=Utility;GTK;
Keywords=Rust;Flatpak;GTK;
Icon=com.belmoussaoui.RustFlatpak
StartupNotify=true

Icon

The application should provide an icon, either in various sizes or as a scalable SVG version. Most stores uses either 64px or 128px icons, but the application icon might be used at smaller sizes like 48px somewhere else.

The icon should be installed under $prefix/$datadir/icons/hicolor/scalable/apps/ in case you’re shipping an SVG icon. Otherwise it should be installed under $prefix/$datadir/icons/hicolor/48x48/apps/, the default available sizes are: 16, 32, 48, 64, 128, 256

3 – Flatpak!

3.1 Using Meson Build System

The usage of Meson might seem not very useful if you’re not used to the GNOME Stack, but it provides a ton of features in a human readable way of writing things. It makes it pretty easy to use gettext for translating the desktop & metainfo files for examples, installing the icons at right place and so on. Features that Cargo can’t provide, at least, not in a simple way.

First step would be adding a meson.build files at the the same level as our main Cargo.toml in case your application is built split into different workspaces.

project('rust-flatpak',
        'rust',
        version: '0.1.0')

We started by defining our dependencies here, it might seems repetitive to have that here even if we do specify that in Cargo.toml, but it’s different. GTK is a a C library, and GTK Rust bindings just links to the library while providing a nice Rust wrapper around the C one. So you can’t build the application if those libraries can’t be detected by Cargo. Meson allows you to check that they are actually available before starting the build.

dependency('glib-2.0')
dependency('gio-2.0')
dependency('gtk+-3.0')

Let’s create a data directory and put our desktop, metainfo files and icons on it. You can name it differently, it’s just a convention.

We need to tell Meson to check the build receipt we will have in src and data

subdir('data')
subdir('src')
Current files structure

Let’s start by telling Meson how to install the files under data

datadir = get_option('prefix') / get_option('datadir')

application_id = 'com.belmoussaoui.RustFlatpak'
# Read more https://mesonbuild.com/Reference-manual.html#install_data

install_data(
    '@0@.desktop'.format(application_id),
    install_dir: datadir / 'applications'
)

install_data(
    '@0@.metainfo.xml'.format(application_id),
    install_dir: datadir / 'metainfo'
)

install_data(
    '@0@.svg'.format(application_id),
    install_dir: datadir / 'icons' / 'hicolor' / 'scalable' / 'apps'
)

You can notice that we are using get_option to get the prefix and datadir which could be defined at build time by passing --prefix=/usr --datadir=share.

Now, to the most complicated part of this, making Cargo & Meson happy friends. We can easily call Cargo from Meson and build our application, but we would like to define $CARGO_HOME during build, so we won’t have to re-download everything every time we rebuild the Flatpak. Sadly, ninja, “the build backend” used by Meson doesn’t support setting an env variable for a custom target. Until that’s fixed in both, we need to call a bash script from Meson that does call Cargo for us.

sources = [
    'main.rs',
]

cargo_script = find_program(join_paths(meson.source_root(), 'build-aux/cargo.sh'))
cargo_release = custom_target(
  'cargo-build',
  build_by_default: true,
  input: sources,
  output: meson.project_name(),
  console: true,
  install: true,
  install_dir: get_option('bindir'),
  command: [
    cargo_script,
    meson.build_root(),
    meson.source_root(),
    '@OUTPUT@',
    get_option('buildtype'),
    meson.project_name(),
  ]
)

And by convention I put the script to call under build-aux/cargo.sh

The content of the file should be

#!/bin/sh

export MESON_BUILD_ROOT="$1"
export MESON_SOURCE_ROOT="$2"
export CARGO_TARGET_DIR="$MESON_BUILD_ROOT"/target
export CARGO_HOME="$CARGO_TARGET_DIR"/cargo-home
export OUTPUT="$3"
export BUILDTYPE="$4"
export APP_BIN="$5"


if [[ $BUILDTYPE = "release" ]]
then
    echo "RELEASE MODE"
    cargo build --manifest-path \
        "$MESON_SOURCE_ROOT"/Cargo.toml --release && \
        cp "$CARGO_TARGET_DIR"/release/"$APP_BIN" "$OUTPUT"
else
    echo "DEBUG MODE"
    cargo build --manifest-path \
        "$MESON_SOURCE_ROOT"/Cargo.toml --verbose && \
        cp "$CARGO_TARGET_DIR"/debug/"$APP_BIN" "$OUTPUT"
fi

We should have everything by now, and the latest step would be preparing our Flatpak manifest.

We can try that our Meson build is working by typing

meson -C _builddir --prefix=/tmp
ninja -C _builddir 

Before we start writing our Flatpak manifest, please take a short tour at https://docs.flatpak.org/en/latest/ to read about the basics of Flatpak.

We will be writing our manifest in JSON, you could use a YAML file too.

Flatpak Manifest

{
    "app-id": "com.belmoussaoui.RustFlatpak",
    "runtime": "org.freedesktop.Platform",
    "runtime-version": "19.08",
    "sdk": "org.freedesktop.Sdk",
    "sdk-extensions" : [
        "org.freedesktop.Sdk.Extension.rust-stable"
    ],
    "command": "rust-flatpak",
    "finish-args": [
        "--share=ipc",
        "--socket=fallback-x11",
        "--socket=wayland",
        "--device=dri"
    ],
    "build-options": {
        "append-path" : "/usr/lib/sdk/rust-stable/bin",
        "build-args" : [
            "--share=network"
        ],
        "env" : {
            "RUSTFLAGS" : "--remap-path-prefix =../",
            "CARGO_HOME" : "/run/build/rust-flatpak/cargo"
        }
    },
    "modules": [
        {
            "name": "rust-flatpak",
            "buildsystem": "meson",
            "sources": [
                {
                    "type": "dir",
                    "url": "../"
                }
            ]
        }
    ]
}

Building our application using Flatpak isn’t hard, as the freedesktop runtime gives us already all what we could need to build our app, on top of it we have the Rust SDK extension that adds Rust support.

You can notice that the application is built with network access from the build-args. Flatpak by default, downloads all the sources and don’t give network access to anything during the build process. Except we need Cargo to be able to download our dependencies. Such manifest is the way to go if you’re using it for CI. If your plan is to use it to distribute your application on a Flatpak based store like Flathub, all the dependencies have to be downloaded before starting the build.

Meson allows you to create a tarball of your application using ninja -C _builddir dist. Except, we have to tell Meson that we need to bundle all our Cargo dependencies too, basically calling cargo vendor and moving the tarballs to $DIST.

meson.add_dist_script(
  'build-aux/dist-vendor.sh',
  meson.build_root() / 'meson-dist' / meson.project_name() + '-' + version,
  meson.source_root()
)

And we add our dist-vendor script

#!/bin/sh
export DIST="$1"
export SOURCE_ROOT="$2"

cd "$SOURCE_ROOT"
mkdir "$DIST"/.cargo
cargo vendor | sed 's/^directory = ".*"/directory = "vendor"/g' > $DIST/.cargo/config
# Move vendor into dist tarball directory
mv vendor "$DIST"

If we want to create a new release and publish it on Falthub, all we have to do is generate a tarball and upload it to our tagged release on Gitlab/Github/somewhere.

meson -C _builddir
ninja -C _builddir/ dist

Which should create a tarball under _builddir/meson-dist/. We can now remove the network access from build-argsand replace our source from a git type to an archive one and we should be ready to publish it on Flathub for a review!

3.2 Using flatpak-cargo-generator

Flatpak cargo generator is one of the many community made scripts that allows you to generate a Flatpak manifest of all the dependencies of your application for various programming languages.

The scripts takes your Cargo.lock as an input and generates a manifest that Flatpak can understand from it. Which could work perfectly fine in case you’re using Meson too (you just won’t have to create a dist script for bundling Cargo dependencies).

You can just grab the flatpak-cargo-generator script from https://github.com/flatpak/flatpak-builder-tools/blob/master/cargo/flatpak-cargo-generator.py and run it with

python3 ./flatpak-cargo-generator.py ./Cargo.lock -o cargo-sources.json

The script depends on the python module siphash

{
    "app-id": "com.belmoussaoui.RustFlatpak",
    "runtime": "org.freedesktop.Platform",
    "runtime-version": "19.08",
    "sdk": "org.freedesktop.Sdk",
    "sdk-extensions" : [
        "org.freedesktop.Sdk.Extension.rust-stable"
    ],
    "command": "rust-flatpak",
    "finish-args": [
        "--share=ipc",
        "--socket=fallback-x11",
        "--socket=wayland",
        "--device=dri"
    ],
    "build-options": {
        "append-path" : "/usr/lib/sdk/rust-stable/bin",
        "env" : {
            "RUSTFLAGS" : "--remap-path-prefix =../",
            "CARGO_HOME" : "/run/build/rust-flatpak/cargo"
        }
    },
    "modules": [
        {
            "name": "rust-flatpak",
            "buildsystem": "simple",
            "build-commands": [
                "cargo --offline fetch --manifest-path Cargo.toml --verbose",
                "cargo --offline build --release --verbose",
                "install -Dm755 ./target/debug/rust-flatpak -t /app/bin/",
                "install -Dm644 ./data/${FLATPAK_ID}.metainfo.xml -t /app/share/metainfo/",
                "install -Dm644 ./data/${FLATPAK_ID}.desktop -t /app/share/applications/",
                "install -Dm644 ./data/${FLATPAK_ID}.svg -t /app/share/icons/hicolor/scalable/apps/"
            ],
            "sources": [
                {
                    "type": "dir",
                    "path": "../"
                },
                "generated-sources.json"
            ]
        }
    ]
}

Where the generated-sources.json is a the generated file using flatpak-cargo-generator

4 – Trying your Flatpak manifest

You can build and test the Flatpak package locally using flatpak-builder

flatpak-builder --install repo build-aux/com.belmoussaoui.RustFlatpak.json --force-clean --user -y

You can run the application later using

flatpak run com.belmoussaoui.RustFlatpak

Publishing the application later on Flathub should be straight forward. You can read more about that from here

5 – Bonus: Gitlab CI bundles

At GNOME we have a CI template that makes integrating a Flatpak pipeline pretty easy. It requires using Meson as a build system, but you can easily tweak that to your build preferences. Sadly, we can’t achieve similar results easily with Github Actions for now.

include:
  - project: 'gnome/citemplates'
    file: 'flatpak/flatpak-ci-initiative-sdk-extensions.yml'

flatpak:
  image: 'registry.gitlab.gnome.org/gnome/gnome-runtime-images/rust_bundle:3.34'
  variables:
    BUNDLE: "rust-flatpak-nightly.flatpak"
    MANIFEST_PATH: "build-aux/com.belmoussaoui.RustFlatpak.json"
    FLATPAK_MODULE: "rust-flatpak"
    APP_ID: "com.belmoussaoui.RustFlatpak"
    RUNTIME_REPO: "https://flathub.org/repo/flathub.flatpakrepo"
  extends: '.flatpak'

That should be it 🙂 You should have the basics to publish a simple Rust application as a Flatpak.

Useful links:

L’article How to Flatpak a Rust application est apparu en premier sur Bilal Elmoussaoui.

Xamarin forks and whatnots

Busy days in geewallet world! I just released version 0.4.2.198 which brings some interesting fixes, but I wanted to talk about the internal work that had to be done for each of them, in case you're interested.
  • In Linux(GTK), cold storage mode when pairing was broken, because the absence of internet connection was not being detected properly. The bug was in a 3rd-party nuget library we were using: Xam.Plugin.Connectivity. But we couldn't migrate to Xamarin.Essentials for this feature because Xamarin.Essentials lacks support for some platforms that we already supported (not officially, but we know geewallet worked on them even if we haven't released binaries/packages for all of them yet). The solution? We forked Xamarin.Essentials to include support for these platforms (macOS and Linux), fixed the bug in our fork, and published our fork in nuget under the name `DotNetEssentials`. Whenever Xamarin.Essentials starts supporting these platforms, we will stop using our fork.
  • The clipboard functionality in geewallet depended on another 3rd-party nuget library: Xamarin.Plugins.Clipboard. The GTK bits of this were actually contributed by me to their github repository as a Pull Request some time ago, so we just packaged the same code to include it in our new DotNetEssentials fork. One dependency less to care about!
  • Xamarin.Forms had a strange bug that caused some buttons sometimes to not be re-enabled. This bug has been fixed by one of our developers and its fix was included in the new pre-release of Xamarin.Forms 4.5, so we have upgraded geewallet to use this new version instead of v4.3.
Last but not least, I wanted to mention something not strictly related to this new release. We got accepted in GNOME's gitlab, so now the canonical place to find our repository is here:


Technically speaking, this will allow us to drop GithubActions for our non-Linux CI lanes (we were already happy with our gitlab-ci recipes before we had to use GH! only limitation was that GitLab.com's free tier CI minutes for agents was Linux-only).

But in general we're just happy to be hosted by infrastructure from one of the most important opensource projects in the world. Special thanks go to Carlos Soriano and Andrea Veri for helping us with the migration.

PS: Apologies if the previous blogpost to this one shows up in planets again, as it might be a side-effect of updating its links to point to the new git repo!

February 07, 2020

GtkSourceView Branched

I’ve branched GtkSourceView for 4.6 (gtksourceview-4-6) which you should be using instead of master for your application’s Nightly Flatpak builds. I will land the GTK 4 port on master early next week. A message to gnome-announce-list has been sent and will hopefully make it into distribution packagers inbox shortly.

Long story short is that the 4.6 series will be our long-term (and last) series for GTK 3 applications. I expect this to be maintained for many years. Master will become the beginning of our transition to GTK 4 and the place we land lots of upstream features for Next.0.

This Month in Mutter & GNOME Shell | December 2019 & January 2020

GNOME Shell

Unified Layout for Dialogs

One area of focus during this cycle was unifying the layout, content structure, and feel of dialogs in GNOME Shell. Many dialogs were redesigned, polished, and updated as a result of this effort:

Improved Gesture Support

The gestures to switch workspaces, and navigate through the application grid pages, were dramatically improved, and now follow the touchpad movement.

Password Peek

The password entries in GNOME Shell now support peeking passwords! Take a look:

Folders as Dialogs

A major change on how GNOME Shell handles application folders has landed this month. Instead of being displayed as popups within the application grid, folders are now displayed above it in the form of a dialog.

Miscellaneous

A new blur effect was added to GNOME Shell. It will be used by future work, such as a redesigned lock screen, among others.

Due to the VP9 encoder being too CPU-intensive, the built-in screen recorder changed back to the VP8 encoder.

As part of a code refactoring effort, the bits of StBoxLayout that deal with hiding overflowing content were moved to a new class, StViewport. This simplifies in various ways the implementation of overflowed content in GNOME Shell.

When managing the icons in the application grid, such as creating, renaming, or deleting folders, or moving app icons in and out of these folders, the icons will animate to their positions smoothly now.

GNOME Shell’s performance profiling framework was fixed and updated to also work on Wayland.

The SCSS code that is used to generate the default GNOME Shell theme was completely revamped and reorganized, making it easier to find specific theme classes. It also received a small visual update.

At last, GNOME Shell now supports NVidia GPU offloading by showing the “Launch on Discrete GPU” entry on systems with a NVidia discrete GPU available.

Mutter

More Cogl Cleanups

Cogl is the GPU rendering abstraction used by Clutter to render its contents on screen. Clutter itself is the base of Mutter and GNOME Shell, which add window management and compositing on top of Clutter.

During the past few months, there were continued efforts to cleanup and modernize Cogl by removing dead and unused code, using more modern OpenGL features, etc.

This work continued during December and January too, with more dead code saying goodbye, and more modern code being welcomed to the codebase.

Implicit Cogl API Removal

Due to historical reasons, Cogl has two major APIs: an explicit one, with properly defined types, and functions that receive the objects they should act on; and an implicit one, that is closer to the OpenGL model, and acts on an implicit state machine with the objects to be worked on.

Many parts of the Clutter, Mutter, and GNOME Shell codebases were using the implicit Cogl API. Those usages were updated, and allowed us to remove most of the implicit API from Cogl.

ClutterOffscreenEffect Improvements

ClutterOffscreenEffect is an effect applied on UI elements that allow them to be rendered in offscreen framebuffers. This allows interesting, shader-based effects to be applied to these elements, such as contrast and brightness, blur, among others.

A few bugs were found in this effect, and were ironed out. It also received some optimizations that allow the effect to release GPU memory more often.

Introduction of ClutterSeat

Clutter’s representation of input devices was designed in the X11/XI2 times, and the API was modeled after it. This change brings the internal input model closer to Wayland.

While it may sound an unimpressive change, it’s the cornerstone for an unification of the multiple input grabbing mechanisms in use by GNOME Shell.

Between other internal refactors that are now possible, it will also facilitate the introduction of an input thread in the native backend, so the pointer cursor is no longer frozen on stalls.

This will be covered in detail in a separate article.

Improve layout phase of no-layout actors

The Clutter toolkit follows the traditional rendering pipeline, composed of the layout, paint, and pick phases. The layout phase is where the elements (in Clutter terminology, “actors”) get to know their position and size; the paint phase is where these actors paint their contents on the framebuffer; and the pick phase is where Clutter figures out which actor is hovered by the pointer.

When we know beforehand that an actor is not going to move around the screen, we can tell Clutter about it, and the layout phase can be optimized to avoid some calculations. This mode is called “no-layout”.

During January, Clutter received another set of optimizations when dealing with no-layout actors. This can reduce CPU use on some specific scenarios, such as when dragging windows.

Miscellaneous

The ClutterContent interface now is better integrated in the size negotiation phase of ClutterActors, by making actors that follow their content’s sizes report their content’s geometry.

Many improvements to how Mutter tracks Wayland windows landed. Specifically, a crasher related to wl_subsurface was ironed out, a unit test framework was introduced, and a harmless runtime warning was fixed.

When dealing with multi-GPU setups, Mutter needs to share framebuffers across GPUs. There are currently three ways to do that:

  1. The secondary GPU does an accelerated copy of the primary GPU framebuffer;
  2. The primary GPU does an accelerated copy of its own contents to the secondary GPU framebuffer;
  3. A slow, non-accelerated framebuffer is created on the secondary GPU, mapped to CPU memory, and the primary GPU contents are copied to this memory;

Mutter now features an additional way to share framebuffers: the primary GPU exports a framebuffer that is imported and read directly by the secondary GPU. This method is useful with virtual secondary GPUs, such as DisplayLink docks.

lessons learned from guile, the ancient & spry

Greets, hackfolk!

Like just about every year, last week I took the train up to Brussels for FOSDEM, the messy and wonderful carnival of free software and of those that make it. Mostly I go for the hallway track: to see old friends, catch up, scheme about future plans, and refill my hacker culture reserves.

I usually try to see if I can get a talk or two in, and this year was no exception. First on my mind was the recent release of Guile 3. This was the culmination of a 10-year plan of work and so obviously there are some things to say! But at the same time, I wanted to reflect back a bit and look at the past with a bit of distance.

So in the end, my one talk was two talks. Let's start with the first one. (I'm trying a new thing where I share my talks as blog posts. We'll see how this goes. I know the rendering can be a bit off relative to the slides, but hopefully it's good enough. If you prefer, you can just watch the video instead!)

Celebrating Guile 3

FOSDEM 2020, Brussels

Andy Wingo | wingo@igalia.com

wingolog.org | @andywingo

So yeah let's celebrate! I co-maintain the Guile implementation of Scheme. It's a programming language. Guile 3, in summary, is just Guile, but faster. We added a simple just-in-time compiler as well as a bunch of ahead-of-time optimizations. The result is that it runs faster -- sometimes by a lot!

In the image above you can see Guile 3's performance on a number of microbenchmarks, relative to Guile 2.2, sorted by speedup. The baseline is 1.0x as fast. You can see that besides the first couple microbenchmarks where things are a bit inconclusive (click for full-size image), everything gets faster. Most are at least 2x as fast, and one benchmark is even 32x as fast. (Note the logarithmic scale on the Y axis.)

I only took a look at microbenchmarks at the end of the Guile 3 series; before that, I was mostly going by instinct. It's a relief to find out that in this case, my instincts did align with improvement.

mini-benchmark: eval

(primitive-eval
 ’(let fib ((n 30))
    (if (< n 2)
        n
        (+ (fib (- n 1)) (fib (- n 2))))))

Guile 1.8: primitive-eval written in C

Guile 2.0+: primitive-eval in Scheme

Taking a look at a more medium-sized benchmark, let's compute the 30th fibonacci number, but using the interpreter instead of compiling the procedure. In Guile 2.0 and up, the interpreter (primitive-eval) is implemented in Scheme, so it's a good test of an important small Scheme program.

Before 2.0, though, primitive-eval was actually implemented in C. This had a number of disadvantages, notably that it prevented tail calls between interpreted and compiled code. When we switched to a Scheme implementation of primitive-eval, we knew we would have a performance hit, but we thought that we would gain it back eventually as the compiler got better.

As you can see, it took a while before the compiler and run-time improved to the point that primitive-eval in Scheme reached the speed of its old hand-tuned C implementation, but for Guile 3, we finally got there. Note again the logarithmic scale on the Y axis.

macro-benchmark: guix

guix build libreoffice ghc-pandoc guix \
  –dry-run --derivation

7% faster

guix system build config.scm \
  –dry-run --derivation

10% faster

Finally, taking a real-world benchmark, the Guix package manager is implemented entirely in Scheme. All ten thousand packages are defined in Scheme, the building scripts are in Scheme, the initial RAM disk is in Scheme -- you get the idea. Guile performance in Guix can have an important effect on user experience. As you can see, Guile 3 lowered elapsed time for some operations by around 10 percent or so. Of course there's a lot of I/O going on in addition to computation, so Guile running twice as fast will rarely make Guix run twice as fast (Amdahl's law and all that).

spry /sprī/

  • adjective: active; lively

So, when I was thinking about words that describe Guile, the word "spry" came to mind.

spry /sprī/

  • adjective: (especially of an old person) active; lively

But actually when I went to look up the meaning of "spry", Collins Dictionary says that it especially applies to the agèd. At first I was a bit offended, but I knew in my heart that the dictionary was right.

Lessons Learned from Guile, the Ancient & Spry

FOSDEM 2020, Brussels

Andy Wingo | wingo@igalia.com

wingolog.org | @andywingo

That leads me into my second talk.

guile is ancient

2010: Rust

2009: Go

2007: Clojure

1995: Ruby

1995: PHP

1995: JavaScript

1993: Guile (33 years before 3.0!)

It's common for a new project to be lively, but Guile is definitely not new. People have been born, raised, and earned doctorates in programming languages in the time that Guile has been around.

built from ancient parts

1991: Python

1990: Haskell

1990: SCM

1989: Bash

1988: Tcl

1988: SIOD

Guile didn't appear out of nothing, though. It was hacked up from the pieces of another Scheme implementation called SCM, which itself was initially based on Scheme in One Defun (SIOD), back before the Berlin Wall fell.

written in an ancient language

1987: Perl

1984: C++

1975: Scheme

1972: C

1958: Lisp

1958: Algol

1954: Fortran

1958: Lisp

1930s: λ-calculus (34 years ago!)

But it goes back further! The Scheme language, of which Guile is an implementation, dates from 1975, before I was born; and you can, if you choose, trace the lines back to the lambda calculus, created in mid-30s as a notation for computation. I suppose at this point I should say mid-2030s, to disambiguate.

The point is, Guile is old! Statistically, most software projects from olden times are now dead. How has Guile managed to survive and (sometimes) thrive? Surely there must be some lesson or other that can be learned here.

ancient & spry

Men make their own history, but they do not make it as they please; they do not make it under self-selected circumstances, but under circumstances existing already, given and transmitted from the past.

The tradition of all dead generations weighs like a nightmare on the brains of the living. [...]

Eighteenth Brumaire of Louis Bonaparte, Marx, 1852

I am no philospher of history, but I know that there are some ways of looking at the past that do not help me understand things. One is the arrow of enlightened progress, in which events exist in a causal chain, each producing the next. It doesn't help me understand the atmosphere, tensions, and possibilities inherent at any particular point. I find the "progress" theory of history to be an extreme form of selection bias.

Much more helpful to me is the Hegelian notion of dialectics: that at an given point in time there are various tensions at work. In our field, an example could be memory safety versus systems programming. These tensions create an environment that favors actions that lead towards resolution of the tensions. It doesn't mean that there's only one way to resolve the tensions, and it's not an automatic process -- people still have to do things. But the tendency is to ratchet history forward to a new set of tensions.

The history of a project, to me, is then a process of dialectic tensions and resolutions. If the project survives, as Guile has, then it should teach us something about the way this process works in practice.

ancient & spry

Languages evolve; how to remain minimal?

Dialectic opposites

  • world and guile

  • stable and active

  • ...

Lessons learned from inside Hegel’s motor of history

One dialectic is the tension between the world's problems and what tools Guile offers to understand and solve them. In 1993, the web didn't really exist. In 2033, if Guile doesn't run well in a web browser, probably it will be dead. But this process operates very slowly, for an old project; Guile isn't built on CORBA or something ephemeral like that, so we don't have very much data here.

The tension between being a stable base for others to build on, and in being a dynamic project that improves and changes, is a key tension that this talk investigates.

In the specific context of Guile, and for the audience of the FOSDEM minimal languages devroom, we should recognize that for a software project, age and minimalism don't necessarily go together. Software gets features over time and becomes bigger. What does it mean for a minimal language to evolve?

hill-climbing is insufficient

Ex: Guile 1.8; Extend vs Embed

One key lesson that I have learned is that the strategy of making only incremental improvements is a recipe for death, in the long term. The natural result is that you reach what you perceive to be the most optimal state of your project. Any change can only make it worse, so you stop moving.

This is what happened to Guile around version 1.8: we had taken the paradigm of the interpreter as language implementation strategy as far as it could go. There were only around 150 commits to Guile in 2007. We were stuck.

users stay unless pushed away

Inertial factor: interface

  • Source (API)

  • Binary (ABI)

  • Embedding (API)

  • CLI

  • ...

Ex: Python 3; local-eval; R6RS syntax; set!, set-car!

So how do we make change, in such a circumstance? You could start a new project, but then you wouldn't have any users. It would be nice to change and keep your users. Fortunately, it turns out that users don't really go away; yes, they trickle out if you don't do anything, but unless you change in an incompatible way, they stay with you, out of inertia.

Inertia is good and bad. It does conflict with minimalism as a principle; if you were to design Scheme in 2020, you would not include mutable variables or even mutable pairs. But they are still with us because if we removed them, we'd break too many users.

Users can even make you add back things that you had removed. In Guile 2.0, we removed the capability to evaluate an expression at run-time within the lexical environment of an expression, as we didn't know how to implement this outside an interpreter. It turns out this was so important to users that we had to add local-eval back to Guile, later in the 2.0 series. (Fortunately we were able to do it in a way that layered on lower-level facilities; this approach reconciled me to the solution.)

you can’t keep all users

What users say: don’t change or remove existing behavior

But: sometimes losing users is OK. Hard to know when, though

No change at all == death

  • Natural result of hill-climbing

Ex: psyntax; BDW-GC mark & finalize; compile-time; Unicode / locales

Unfortunately, the need to change means that sometimes you will lose users. It's either a dead project, or losing users.

In Guile 1.8, for example, the macro expander ran lazily: it would only expand code the first time it ran it. This was good for start-up time, because not all code is evaluated in the course of a simple script. Lazy expansion allowed us to start doing important work sooner. However, this approach caused immense pain to people that wanted "proper" Scheme macros that preserved lexical scoping; the state of the art was to eagerly expand an entire file. So we switched, and at the same time added a notion of compile-time. This compromise kept good start-up time while allowing fancy macros.

But eager expansion was a change. Users that relied on side effects from macro expansion would see them at compile-time instead of run-time. Users of old "defmacros" that could previously splice in live Scheme closures as literals in expanded source could no longer do that. I think it was the right choice but it did lose some users. In fact I just got another bug report related to this 10-year-old change last week.

every interface is a cost

Guile binary ABI: libguile.so; compiled Scheme files

Make compatibility easier: minimize interface

Ex: scm_sym_unquote, GOOPS, Go, Guix

So if you don't want to lose users, don't change any interface. The easiest way to do this is to minimize your interface surface. In Go, for example, they mostly haven't had dynamic-linking problems because that's not a thing they do: all code is statically linked into binaries. Similarly, Guix doesn't define a stable API, because all of its code is maintained in one "monorepo" that can develop in lock-step.

You always have some interfaces, though. For example Guix can't change its command-line interface from one day to the next, for example, because users would complain. But it's been surprising to me the extent to which Guile has interfaces that I didn't consider. Recently for example in the 3.0 release, we unexported some symbols by mistake. Users complained, so we're putting them back in now.

parallel installs for the win

Highly effective pattern for change

  • libguile-2.0.so

  • libguile-3.0.so

https://ometer.com/parallel.html

Changed ABI is new ABI; it should have a new name

Ex: make-struct/no-tail, GUILE_PKG([2.2]), libtool

So how does one do incompatible change? If "don't" isn't a sufficient answer, then parallel installs is a good strategy. For example in Guile, users don't have to upgrade to 3.0 until they are ready. Guile 2.2 happily installs in parallel with Guile 3.0.

As another small example, there's a function in Guile called make-struct (old doc link), whose first argument is the number of "tail" slots, followed by initializers for all slots (normal and "tail"). This tail feature is weird and I would like to remove it. Unfortunately I can't just remove the argument, so I had to make a new function, make-struct/no-tail, which exists in parallel with the old version that I can't break.

deprecation facilitates migration

__attribute__ ((__deprecated__))
(issue-deprecation-warning
 "(ice-9 mapping) is deprecated."
 "  Use srfi-69 or rnrs hash tables instead.")
scm_c_issue_deprecation_warning
  ("Arbiters are deprecated.  "
   "Use mutexes or atomic variables instead.");

begin-deprecated, SCM_ENABLE_DEPRECATED

Fortunately there is a way to encourage users to migrate from old interfaces to new ones: deprecation. In Guile this applies to all of our interfaces (binary, source, etc). If a feature is marked as deprecated, we cause its use to issue a warning, ideally at compile-time when users responsible for the package can fix it. You can even add __attribute__((__deprecated__)) on C types!

the arch-pattern

Replace, Deprecate, Remove

All change is possible; question is only length of deprecation period

Applies to all interfaces

Guile deprecation period generally one stable series

Ex: scm_t_uint8; make-struct; Foreign objects; uniform vectors

Finally, you end up in a situation where you have replaced the old interface and issued deprecation warnings to help users migrate. The next step is to remove the old interface. If you don't do this, you are failing as a project maintainer -- your project becomes literally unmaintainable as it just grows and grows.

This strategy applies to all changes. The deprecation period may last a while, and it may be that the replacement you built doesn't serve the purpose. There is still a dialog with the users that needs to happen. As an example, I made a replacement for the "SMOB" facility in Guile that allows users to define new types, backed by C interfaces. This new "foreign object" facility might not actually be good enough to replace SMOBs; since I haven't formally deprecatd SMOBs, I don't know yet because users are still using the old thing!

change produces a new stable point

Stability within series: only additions

Corollary: dependencies must be at least as stable as you!

  • for your definition of stable

  • social norms help (GNU, semver)

Ex: libtool; unistring; gnulib

In my experience, the old management dictum that "the only constant is change" does not describe software. Guile changes, then it becomes stable for a while. You need an unstable series escape hill-climbing, then once you found your new hill, you start climbing again in the stable series.

Once you reach your stable point, the projects you rely on need to exhibit the same degree of stability that you envision for your project. You can't build a web site that you expect to maintain for 10 years on technology that fundamentally changes every 6 months. But stable dependencies isn't something you can ensure technically; rather it relies on social norms of who makes the software you use.

who can crank the motor of history?

All libraries define languages

Allow user to evolve the language

  • User functionality: modules (Guix)

  • User syntax: macros (yay Scheme)

Guile 1.8 perf created tension

  • incorporate code into Guile

  • large C interface “for speed”

Compiler removed pressure on C ABI

Empowered users need less from you

A dialectic process does not progress on its own: it requires actions. As a project maintainer, some of my actions are because I want to do them. Others are because users want me to do them. The user-driven actions are generally a burden and as a lazy maintainer, I want to minimize them.

Here I think Guile has to a large degree escaped some of the pressures that weigh on other languages, for example Python. Because Scheme allows users to define language features that exist on par with "built-in" features, users don't need my approval or intervention to add (say) new syntax to the language they work in. Furthermore, their work can still compose with the work of others, even if the others don't buy in to their language extensions.

Still, Guile 1.8 did have a dynamic whereby the relatively poor performance of having to run all code through primitive-eval meant that users were pushed towards writing extensions in C. This in turn pushed Guile to expose all of its guts for access from C, which obviously has led to an overbloated C API and ABI. Happily the work on the Scheme compiler has mostly relieved this pressure, and we may therefore be able to trim the size of the C API and ABI over time.

contributions and risk

From maintenance point of view, all interface is legacy

Guile: Sometimes OK to accept user modules when they are more stable than Guile

In-tree users keep you honest

Ex: SSAX, fibers, SRFI

It can be a good strategy to "sediment" solutions to common use cases into Guile itself. This can improve the minimalism of an entire ecosystem of code. The maintenance burden has to be minimal, however; Guile has sometimes adopted experimental code into its repository, and without active maintenance, it soon becomes stale relative to what users and the module maintainers expect.

I would note an interesting effect: pieces of code that were adopted into Guile become a snapshot of the coding style at that time. It's useful to have some in-tree users because it gives you a better idea about how a project is seen from the outside, from a code perspective.

sticky bits

Memory management is an ongoing thorn

Local maximum: Boehm-Demers-Weiser conservative collector

How to get to precise, generational GC?

Not just Guile; e.g. CPython __del__

There are some points that resist change. The stickiest of these is the representation of heap-allocated Scheme objects in C. Guile currently uses a garbage collector that "automatically" finds all live Scheme values on the C stack and in registers. It was the right choice at the time, given our maintenance budget. But to get the next bump in performance, we need to switch to a generational garbage collector. It's hard to do that without a lot of pain to C users, essentially because the C language is too weak to express the patterns that we would need. I don't know how to proceed.

I would note, though, that memory management is a kind of cross-cutting interface, and that it's not just Guile that's having problems changing; I understand PyPy has had a lot of problems regarding changes on when Python destructors get called due to its switch from reference counting to a proper GC.

future

We are here: stability

And then?

  • Parallel-installability for source languages: #lang

  • Sediment idioms from Racket to evolve Guile user base

Remove myself from “holding the crank”

So where are we going? Nowhere, for the moment; or rather, up the hill. We just released Guile 3.0, so let's just appreciate that for the time being.

But as far as next steps in language evolution, I think in the short term they are essentially to further enable change while further sedimenting good practices into Guile. On the change side, we need parallel installability for entire languages. Racket did a great job facilitating this with #lang and we should just adopt that.

As for sedimentation, we should step back and if any common Guile use patterns built by our users should be include core Guile, and widen our gaze to Racket also. It will take some effort both on a technical perspective but also on a social/emotional consensus about how much change is good and how bold versus conservative to be: putting the dialog into dialectic.

dialectic, boogie woogie woogie

https://gnu.org/s/guile

https://wingolog.org/

#guile on freenode

@andywingo

wingo@igalia.com

Happy hacking!

Hey that was the talk! Hope you enjoyed the writeup. Again, video and slides available on the FOSDEM web site. Happy hacking!

February 06, 2020

Coding style checks in CI

For a few weeks now, we’ve had coding style and thinko checks running in CI for GLib, long enough to see that it’s working OK (check out this pipeline!) and is perhaps time to share with others.

There are two jobs in the checks, both of which run in a new style-check stage of our CI pipeline, which runs before anything else. One job checks the coding style of a merge request, using clang-format. The other job checks for any lines which introduce TODO comments (or similar). These jobs are intended to be fast, but also to not fail the pipeline if they produce warnings. Coding style is subjective, and nobody has yet come up with a mechanical style description which doesn’t have ugly corner cases. So the intention of these jobs is to help remind people of the coding style, to avoid reviewers having to check coding style manually, and hence to only end up thinking about it or discussing it when the CI says the style is wrong.

The first job, style-check-diff, uses a script to work out the diff from the merge request’s branch point, and then passes that to clang-format-diff.py, a script from LLVM which uses clang-format to reformat only the changed lines in a diff. If any reformatting occurs, that means the merge request differs from the GLib coding style (as described by .clang-format) and should be warned about.

There are some limitations to clang-format: it can’t completely describe how the GLib coding style aligns function arguments. That’s unfortunate, because GNOME Builder aligns function arguments by default (and it’s nice behaviour). Hopefully somebody will implement support in clang-format for this sometime, so that it can accurately describe the coding style which is used by a large number of GNOME projects.

These coding style checks are work by Pavlo Solntsev and Emmanuel Fleury. Thanks!

The second job, check-todos, also uses a script to work out the diff from the merge request’s branch point. It passes that to a little Python program which checks for banned words in the commit message and added lines in the diff. The aim is to prevent any comments containing TODO or WIP from accidentally getting merged, as these strings are often used by developers to indicate something they need to come back to before they’re finished — and it’s easy to miss them!

I’ve been using the convention that comments containing FIXME are OK to be merged, as they indicate something that will need updating in future, but can be merged as-is for now (such as a workaround).

Feedback, improvements, and copying welcome. These scripts should run on any CI image which has git, clang and Python.

User-specific XKB configuration - part 1

The xkeyboard-config project is the repository for all XKB descriptions, or "keyboard layouts" as the layman would say. But languages are weird and thus xkeyboard-config contains an obscene amount of different layouts. And of course there are additional layouts that are more experimental than common [1].

The fault, as usual, lies with us (the pronoun, not the layout). XKB is weird and its flexible to the point of driving even bananas bananas but due to some historic accidents it's largely non-editable. All XKB files are installed in system folders and we all know the 11th commandment was "thou shalt not edit things in /usr/share". But, luckily, that is all about to change. Or rather: it has changed as of libxkbcommon 0.10.0, released Jan 20 2020.

xkeyboard-config provides two types of files. The ones that actually set up your keyboard layout and the ones that allow you to keep sane while doing so, despite your best efforts to the contrary.

KcCGST

Let's look at the first set of files. XKB uses "keycodes, compat, geometry, symbols, types", conveniently if unpronouncably called KcCGST. Keycodes map your "physical" scancode to an internal code-name. For example, your key with the digit 1 on it is AE01 (alphabetic key, row E from bottom, key number 1 from left). Then you map those keycodes into symbols (1 and !). This happens based on the key's type which defines the combination of modifiers to produce the symbols [2]. Compat is largely magic weird stuff (locking modifiers, pointer control) and geometry would let you draw a pretty picture of your keyboard if it was defined for your keyboard which it won't be.

To see the full keyboard layout simply run xkbcomp -xkb $DISPLAY - and marvel. xkeyboard-config keeps all these parts so your X server or Wayland compositor can load it at runtime depending on your layout.

RMLVO

But when it comes to actual layout selection we like our users enough that we don't make them handle KcCGST but rather provide them with RMLVO instead - "rules, models, layouts, variants, options". You select layout "us" and something converts this into the right components to actually load. Run setxkbmap -layout us -print to see this happening.

"layouts" is what you'd usually associate with a country (except politics is still a thing, so more weirdness here) and "variants" are variations of those. Layout "us" gives you QWERTY and "fr" gives you AZERTY but the "us(dvorak)" variant gives you whatever heresy dvorak applies to those physical keys. And of course, things don't stop there - options are tack-on thingies that do stuff. Like remapping caps lock to compose so you're less capable of shouting at me. Come to think of it, it should really be enabled by default for that reason. You can combine multiple options largely at-will. "models" are largely obsolete (except where they aren't) thanks to the Linux kernel evdev interface which makes all keyboard look the same. But they used to be a thing and maybe one day they'll make a comeback like bell bottom jeans. Disliked by everyone but some weirdos insist on using them.

Rules

Finally, we have rules and thus come to the core of the matter of this post. Rules are magic files that tell the various tools how to go from RMLVO to KcCGST. It's a weird format but it's quite understandable, just open /usr/share/X11/xkb/rules/evdev and have a looksie. It'll make you the popular kid at the next frat party.

Many many moons ago before the Y2K bug was even in its larvae stage, the idea was that you could configure all of those because every UNIX tool had to be more flexible than your yoga teacher. I'm unsure to what extent this was actually ever the case but around 2007-ish the old keyboard driver got deprecated and the evdev driver made it's grand entrance. And one side-effect of that was that things broke. evdev uses different keycodes, so all those users that copy-pasted unnecessary XKB configuration into their xorg.conf now had broken keys because they were applying the wrong rules. After whacking enough moles that we got in trouble with the RSPCA we started hardcoding the "evdev" ruleset everywhere. The xorg.conf option "XKBRules" became a noop and thus stopped breaking users' setups.

Except that it also stopped users from deploying their own rules files - something that probably didn't really matter anyway. This had some unintended side-effects though. First, to have a working custom XKB layout you basically had to get it merged upstream. Yes, you could edit the files locally but they'd just be overwritten next time you update the packages. Second, getting rid of hardcoded things is hard so we're stuck with the evdev ruleset for the forseeable future. This was the situation until, well, now.

User-specific rules and layouts

The new libxkbcommon release changes two things: it prepends $XDG_CONFIG_HOME/xkb/ to the lookup path for XKB rules (and other files). So any file in that path will be picked before the system paths. This makes it possible to have KcCGST files in your home directory and actually use them. This was somewhat possible before by passing the right flags to the various tools but now it's on by default - at least where libxkbcommon is used (Wayland).

Secondly, rules files now support an include statement. This means you can set your own rules and include system rules. Because everything is hardcoded to evdev this effectively means your new rule file will be $XDG_CONFIG_HOME/xkb/rules/evdev and have at least one line: ! include %S/rules/evdev. If you do just that, you get the evdev ruleset from the system installation path. And any lines you add before or after that line will be loaded. Have a look at the git commit for the details but the summary is that you'll have a rules file that looks like this:


$ cat $XDG_CONFIG_DIR/xkb/rules/evdev
! option = symbols
custom:foo = +custom(foo)
custom:bar = +custom(baz)

! include %S/evdev
This file will define the option->symbol mappings as above and then include the system-provided evdev rules file, i.e. it'll behave like before with those two added. To get those to do something, you need to have the actual symbols files:

$ cat $XDG_CONFIG_HOME/xkb/symbols/custom
partial alphanumeric_keys
xkb_symbols "foo" {
key <TLDE> { [ VoidSymbol ] };
};

partial alphanumeric_keys
xkb_symbols "baz" {
key <AB01> { [ k, K ] };
};

And voila, you can now use the XKB option "custom:foo" and/or "custom:bar" to remap your tilde or Z key. The rest is left to the reader as an exercise in creativity.

Remaining work

The libxkbcommon change was only the first part of the full feature. The remaining parts is to have libxkbcommon actually resolve XDG_CONFIG_HOME when running in gnome-shell which doesn't work right now thanks to secure_getenv() always returning NULL. That's an issue with gnome-shell in particular thanks to the rt-scheduler feature, enabled by default on Fedora already.

The second part, and harder, is to make the new options appear in the various graphical configuration tools. xkeyboard-config ships an XML file [3] that lists every possible combination with some human description for it. This XML file is used by the various tools directly but none of those tools support XML's XI:Include statements. So we'd either have to update all those tools to extend the parsing accordingly or, most likely a smarter long-term solution, write a wrapper library that provides a stable API to get at the same info. That way we can update the include paths under the hood without having to update every tool. Of course this requires every tool to update to the new library first, so, well, chicken, egg, usual problem. Anyway, we'll get there eventually.

[1] For example, I suspect a meetup of icelandic dvorak users doesn't qualify for a group discount.
[2] Each key has "levels" with one symbol each and modifiers that switch between those levels. Most keys have two levels - normal and shift. But there's a key type for EIGHT_LEVEL_ALPHABETIC_LEVEL_FIVE_LOCK and you can cry or laugh and both reactions are appropriate
[3] ask your grandparents about that, it's basically JSON for old people

February 04, 2020

Building (a very small subset of) LibreOffice with Meson

At FOSDEM I got into a discussion with a LibreOffice dev about whether it would be possible to switch LO's build system to Meson. It would be a lot of manual work for sure, but would there be any fundamental problems. Since a simple test can eliminate a ton of guesswork, I chose to take a look.

Like most cross platform programs, LO has its own platform abstraction layer called Sal. According to experience, these kinds of libraries usually have the nastiest build configurations requiring a ton of configure checks and the like. The most prominent example is GLib, whose configure steps are awe-inspiring.

Sal turned out to be fairly simple to port to Meson. It did not require all that much in platform setup, probably because the C++ stdlib provides a lot more out of the box than libc. After a few hours I could compile all of Sal and run some unit tests. The results of the experiment can be found in this Github repo. The filenames and layouts are probably not the same as in the "real" LO build, but for a simple experiment like this they'll do.

So what did we learn?

  • Basic compilation seems to be straightforward, but there were some perennial favourites including source files that you must not compile standalone, magic compilation defines, using declarations hidden at the bottom of public headers behind #ifdefs and so on.
  • There may be further platform funkiness in the GUI toolkit configuration step.
  • A lot of code seems to be generated from IDL files, which might require some work.
  • Meson's Java support probably needs some work to build all the JARs.
  • Meson should scale at least to building LO itself, building all dependency projects at the same time is a different matter.
The last of these is not really an issue on Linux, since you get almost all deps from the system. On Windows and Mac you can achieve the same by using something like Conan for dependency management. However Meson might be scalable enough to build LO and all deps in a single go, and if it's not then making it do that would be an interesting optimization challenge.

How would one express LO's sheer size with a single number?

It has more than 150 000 lines of Makefiles.

Read It Later, yet an other GTK & Rust application

Late last year, I wanted to find an application that I can try the Rust async features with it. I started looking for a service that had a nice Rust API wrapper already and I started prototyping Read It Later on top of wallabag-rs.

Read It Later, is a well designed Wallabag client.  It’s built with Rust, GTK & libhandy on the UI side. It’s fully adaptive which makes it Linux on pocket ready and also comes with a beautiful icon designed by Tobias Bernard.

Screenshot taken using App Icon Preview

It allows you to do the basic things:

  • Login/Logout/Sync
  • Add/Favorite/Archive/Delete an article
  • Dark Mode Reader
  • Adaptive UI

You can grab it already from Flathub at https://flathub.org/apps/details/com.belmoussaoui.ReadItLater. If you would like to contribute, the source code is available at https://gitlab.gnome.org/bilelmoussaoui/read-it-later

So far, writing apps with GTK & Rust have been a fun experience. GNOME Builder & Flatpak made the developer experience smooth 🙂

L’article Read It Later, yet an other GTK & Rust application est apparu en premier sur Bilal Elmoussaoui.

Games and retro-gtk happenings

Yesterday, I released GNOME Games 3.35.90, so we’re in feature freeze for 3.36.0. Let’s take a look at the changes during the 3.35.x cycle:

Faster collection loading

For a long time, Games loaded collection asynchronously using Vala async functions. While it didn’t block the UI completely, it was still slow and caused frequent UI stalls until it loaded completely. In 3.36, collection loading uses a separate thread instead and is noticeably faster as a result, while the UI is perfectly smooth the whole time.

Cover loading has been moved to a thread as well, so both initial loading and scrolling while covers are loading should now be fast and smooth.

There’s still lots of room for improvement, but for the moment this improves things somewhat. 🙂

Steam integration improvements

New-style vertical covers are now supported for Steam games.

Steam covers in Games 3.35.90

Additionally, Steam tools such as Proton and Steamworks Common Redistributables don’t show up as games anymore.

Restarting games

Veerasamy Sevagen added a way to restart games without exiting them.

Secondary menu in GNOME Games 3.35.90

Other changes

Savestates were renamed to snapshots, and backup/restore was renamed to export/import.

Nightly and development builds have a new icon made with App Icon Preview.

GNOME Games icon in App Icon Preview

Lists now look more consistent with each other on mobile:

Platforms in GNOME Games 3.35.90 on mobile Preferences in GNOME Games 3.35.90 on mobile Platform preferences in GNOME Games 3.35.90 on mobile

On desktop, the first two lists turn into sidebars and look mostly the same way as before, while other lists will still have rounded corners and separators.

And finally, there has been a lot of refactoring and code cleanups.


And that’s it? If the changelog for 3.36 seems a bit short, it’s because it is. Due to unfortunate timing, a lot of work planned for 3.36 has been postponed to 3.38 so that we get more than a few weeks before feature freeze to finish and test it. 🙂 So, let’s take a look at what retro-gtk 1.0 will bring.

retro-gtk 1.0

API break

First of all, retro-gtk 1.0 will not be API-compatible with the previous versions. In some cases the API is just simplified or made nicer to use (for example, RetroMainLoop is gone, having been merged into RetroCore), but a lot of changes have a very specific reason, more on that later. Since there will be more changes down the road, there’s no point in describing the API changes just yet, so instead let’s take a look at the bigger features:

More precise timing

Currently, retro-gtk uses g_timeout_add() for doing the main loop in the game. It’s… Not very precise. Instead, we have a custom GSource now implementing a more precise timer that allows an error of just a few microseconds instead of a few hundred.

Of course, it’s still not synced to the GTK refresh rate and so occasionally results in dropped frames. I’m not particularly happy with that, but that’s for later.

Audio playback improvements

With such an imprecise timing, it would be expected that the games would run at a wrong speed. And sometimes they do, but more often than not they still work fine. This happens because audio playback is blocking (each call waits until the previous audio has finished playing) and so the game can never run faster than its audio can play.

And as long as games output audio once per frame, it’s not an issue. However, libretro defines 2 callbacks for audio: one sends 2 samples, one for each channel, and the other one sends a batch of arbitrary length. And of course a lot of cores use the former.

At first retro-gtk just played samples immediately as they arrived. Of course, the cores using the former way were slowed down to a crawl because each call waited for the previous samples to play.

A year ago Adrien added a workaround after a report that a core was slowing down by batching up to 512 samples and playing them all at once. This solved the immediate problem, because it meant cores could do many consecutive calls sending samples and not get slowed down. However, outputting more than 512 samples at once still caused slowdowns (a lot of cores output 1000 or more samples per frame), and it also means that if a core sends a number of samples that isn’t power of 2 per frame, or if it sends a different amount of samples on every frame, there will be inconsistent slowdowns or jittery audio.

Initially I opted for making audio playback threaded and queueing any additional samples to resolve this. It worked, but led to some subtle desync issues if the game ran a little faster. It could be as subtle as 6 extra audio frames (12 samples) every 22 seconds (0.45% assuming the framerate is 60 fps), but it was still noticeable, so instead we now queue any samples sent during the frame and then play them once at the end of every frame. While this is not perfect (if the core sends a different number of samples, it’s still possible to get slowdowns after a “long” frame), it works and it solves slowdown in cores like PX68K.

Fast-forwarding that actually works

While it might be good that audio playback naturally prevents games from running too fast, it also means that the speed rate property (unused in Games currently) did nothing when trying to speed the game up rather than slow it down. To solve this, retro-gtk now resamples audio to match speed rate using libsamplerate. Now setting speed rate to values larger than 1 works, although with a “chipmunk effect”, as resampling also changes audio pitch. 🙂

Running cores in a separate process

ps output showing retro-runner process

This is a big change, and it’s something I’ve started working on not long after 3.34.0 release, though at one point put it into a hiatus and then resumed. It means moving the game logic into a separate process and only having a proxy that sends input and receives output in the UI process. It’s similar to what web browsers have been doing long before, with separate web processes for each tab.

Why?

Many reasons. For one, crash resilience. If a core crashes, currently the whole app is taken down. When the core runs in its own process, instead Games can show an error and offer to restart the game.

GNOME Games showing a crash screen

It ensures there are no UI stalls due to a slow core. Running multiple cores at the same time will start a separate runner process for each core, which means the runner process side code can assume there’s only ever one core and can be simplified a lot. It improves performance when drawing the game into the widget is slow, like with fractional scaling, as drawing now happens asynchronously and so even if the UI process can’t keep up drawing everything in time, the game still runs at full speed. After a core is stopped, it’s cleanly uninitialized because its process exits. And it also allows us to implement some new features, but more on that later.

How?

The implementation is very much inspired by Christian Hergert’s GUADEC 2019 talk and by Builder’s git plugin and Sysprof. The runner process helper is a separate lightweight binary called retro-runner that isn’t linked to retro-gtk or even to GTK.

For communication it uses both messaging and shared memory. Calls such as switching disks, saving/loading states, pausing and resuming use messaging, specifically D-Bus over a private socket connection. This makes it possible to use gdbus-codegen for generating boilerplate, though there’s still some boilerplate wrapping those calls to expose in a public API. It also allows to pass file descriptors, which are useful for…

…shared memory, specifically a memfd that is passed to the other process and mmap-ed on both sides, which is used for input and video.

This required some changes in how input works: libretro input is polling-based and retro-gtk follows that. However, in every single RetroController implementation retro_controller_poll() is no-op and instead the controller gets the input state on its own via signals, stores it and returns it on demand. With that in mind, poll() was removed and instead controller implementations can now notify about their state changes. When that happens, their complete state is serialized and written into a shared memory block. Since the complete state for one controller, including mouse, pointer, lightgun, gamepad and keyboard state is under 1kb, it’s fast enough to just write the whole thing and not only differing parts. Then, when the core polls input state, a copy of the contents of shared memory is made and then any queries return values from that copy. This means the input is still fast and has no noticeable latency increase over running in the same process, and at the same time we follow the spec closer because input is now actually polling-based on the runner process side, and asking for input state before polling it does actually return the previous state.

Additionally, the call to set controller rumble state previously returned a boolean value to indicate whether it was succcessful or not. That was previously exposed in RetroController as is, but calling it in sync just to get that value isn’t really an option. So instead there’s now a separate retro_controller_get_supports_rumble() call and retro_controller_set_rumble_state() returns nothing, allowing it to be called via D-Bus without degrading performance.

Video is passed similarly to input: the shared memory contains a framebuffer that runner process writes to and UI process reads from. Unfortunately, it’s not very efficient, because libretro gives us a preallocated framebuffer, resulting in a copy on the runner process side. It’s even worse on the UI process side, as there’s a copy (which is unnecessary, but I haven’t got to removing it yet) + uploading the texture to GPU to render it. And while libretro provides a callback to get an address of our framebuffer, retro-gtk doesn’t implement it yet and almost no core uses it. ðŸ™� Thankfully, the resolution is usually pretty low (160×144, 240×160, 320×240, or sometimes 640×480), so this pipeline works, and it’s even noticeably faster than it was in a single process for me.

The problematic part is that actually telling UI process to redraw is currently done via a message. ðŸ™� While it works, it’s extra latency that could be avoided. But there’s still time to change it.


OpenGL core support

One feature that we’ve wanted for a long time, but could never implement is supporting libretro cores that use hardware rendering. It should be simple, right? After all, we already use OpenGL in the widget that draws the game.

In the most basic form, the core sends us specs of a context and a framebuffer (API, version, framebuffer parameters such as whether it has depth/stencil buffer). We get a context from somewhere as asked and provide the framebuffer as needed. Additionally we’re expected to provide pointers to the GL functions by name.

The only quirk is that most cores want a compatibility profile context. And I couldn’t achieve it no matter what I did. Either it corrupted GTK state or eglCreateContext() created a core profile context even though I asked for compatibility profile.

Subprocess comes to the rescue! With that I can easily have whatever context the core wants on the runner process side and it just works. Hence:

Banjo-Kazooie for Nintendo 64 in GNOME Games Sonic Adventure for Sega Dreamcast in GNOME Games

With OpenGL cores working, we can run games for a lot of platforms we don’t currently support, such as Nintendo 64 and Sega Dreamcast. While I can’t say yet which platforms will be supported in 3.38, I’m pretty sure Nintendo 64 will make it. Less sure about Dreamcast, because while Flycast core runs pretty well, it has some quirks. For example, if a state was saved without a controller plugged in, it won’t ever see the controller after restoring that state again, and for some reason Sonic Adventure defaulted to Japanese language. More importantly, Dreamcast game detection in Games isn’t very good right now and needs improvements before it’s useful.

Additionally, for Nintendo 64 I implemented a simple controller expansion switcher, because with Nintendo 64 you have to choose between reliably saving games and having rumble. 😛

Nintendo 64 controller expansion switcher in GNOME Games

At least we can automatically disable rumble for controllers that don’t support it, such as the keyboard assigned for player 2 in the screenshot. The UI needs more work too, for example, I’m not particularly happy about Player 1/Player 2 labels, but I needed something for testing. 🙂

So, is it done?

Not yet. While OpenGL support works pretty well, there are other things to do, such as Vulkan support, as both of these cores can use it. That’s going to be interesting, because I have absolutely no experience working with Vulkan, unlike with OpenGL. Looking forward to learning it, and looking forward to an awesome 3.38 release, even though we still haven’t released 3.36 yet. 🙂

February 02, 2020

Upgrading Silverblue from Fedora 30 to 31 using GNOME Software

Last week, I took a look at fixing Fedora 30 GNOME Software to get Silverblue 30 to 31 distro upgrades working. The fixes are now available in F30 updates-testing (gnome-software-3.32.4-6.fc30), backported from upstream git.

Check out my YouTube video where I go in more details: https://www.youtube.com/watch?v=t_7jtwfnZkQ

February 01, 2020

Let’s Learn Spelling!

Were you looking forward to reading an exciting blog post about substantive technical issues affecting GNOME or the Linux desktop community? Sorry, not today.

GNOME

It used to be an acronym, so it’s all uppercase. Write “GNOME,” never “Gnome.” Please stop writing “Gnome.”

Would it help if you imagine an adorable little garden gnome dying each time you get it wrong?

If you’re lazy and hate capital letters, or for technical contexts like package or project names, then all-lowercase “gnome” might be appropriate, but “Gnome” certainly never is.

Red Hat

This one’s not that hard. Why are some people writing “RedHat” without any space? It doesn’t make sense. Red Hat. Easy!

SUSE and openSUSE

S.u.S.E. and SuSE are both older spellings for the company currently called SUSE. Apparently at some point in the past they realized that the lowercase u was stupid and causes readers’ eyes to bleed.  Can we please let it die?

Similarly, openSUSE is spelled “openSUSE,” not “OpenSUSE.” Do not capitalize the o, even if it’s the first word in a sentence. Do not write “openSuSE” or “OpenSuSE” (which people somehow manage to do even when they’re not trolling) or anything at all other than “openSUSE.” I know this is probably too much to ask, but once you get the hang of it, it’s not so hard.

elementary OS

I don’t often see this one messed up. If you can write elementary OS, you can probably write openSUSE properly too! They’re basically the same structure, right? All lowercase, then all caps. I have faith in you, dear reader! Don’t let me down!

GTK and WebKitGTK

We removed the + from the end of both of these, because it was awful. You’re welcome!

Again, all lowercase is probably OK in technical contexts. “gtk-webkit” is not. WebKitGTK.

2020-02-01 Saturday.

  • Up early, more slideware worked in taxi & on steps. Gave a talk on the story of LibreOffice - 150+ slides and still very incomplete: so many good people to credit, and so little time:
    LibreOffice turns ten presentation as hybrid PDF
  • Also had a talk on integrating
    Integrating Collabora Online into your web app as hybrid PDF
  • Gave another talk on the horrors of the Web Copy/Paste APIs.
  • Out for a lovely, relaxing LibreOffice team pasta dinner kindly hosted by Beta Co-working, and our Italian community.

Sculpting Tracker 3.0

We’re in the second phase of work to create version 3.0 of the Tracker desktop search engine.

Tracker’s database is now up to date with the latest SPARQL 1.1 standards, including the magical SERVICE statement that lets you combine results from multiple databases in a single query. Now we’re converting the database from a service into a library, and turning the previously monolithic architecture into something more flexible.

Carlos has already done most of this work and the code is pushed as #172 (tracker.git) and  #136 (tracker-miners.git). At times it feels like we’re carving a big block of stone into a sculpture — just look at the diffstats:

    tracker.git: +4214 -10234
    tracker-miners.git: +375 -718

Read merge request #172 for full details, but the highlights are that there’s no more tracker-store daemon, and the libtracker-sparql library which was previously only used for querying and inserting data can now be used to create and manage your own database. You can keep the database private, or you can expose it over D-Bus.

The code in tracker.git is now only about managing data. We may rename it to tracker-sparql in due course, or even to SPARQLite if this is okayed by the developers of SQLite. There’s perhaps a niche for a desktop-scale database that supports SPARQL queries, and it’s a niche that Tracker’s database fits in nicely.

All the code related to desktop indexing and search is now in tracker-miners.git. The tracker-miner-fs daemon will maintain the index in its own database, which you’ll be able to query by connecting over D-Bus just like you used to connect to tracker-store in Tracker 2.0. However, apps running inside Flatpak will not be able to talk directly to the tracker-miner-fs daemon — communication will go through a new portal that Carlos is currently working on, allowing us to implement per-app access controls to your data for the first time.

We are still pending a Tracker 2.3.2 bugfix release too! This month Victor Gal solved an issue that was causing photo geolocation metadata to be ignored. Rasmus Thomsen also added Alpine Linux to our CI, and the GNOME translation teams have been hard at work too.

If you want to help out by testing, developing, documenting Tracker – get in touch on  GNOME Discourse (use the ‘tracker’ tag) or irc.gnome.org #tracker.

January 31, 2020

Implementing a Vala Language Server

There is a Language Server Specification (LSP) published by Microsoft. Is a set of interfaces to manage information about a set of source files in an specific programming language, like symbols, classes, properties, variables, enumerations, and so on.

LSP uses JSON to communicate between a Server and a Client, so all interfaces must be translated to its counterpart in JSON before to transmit over. The communication is bidirectional, so when a request is sent from one side to another, a response is sent back with the result.

An LSP Client can used by Source Code Editors, like GNOME Builder or elementary’s code, or Anjuta, in order to provide specific services to the coder, like completion, goto symbol definitions, diagnostics, code formatting and others. A Client request all above services from a Server. This way, a source code editor can support multiple programming languages, just implementing an LSP Client and connect to an LSP Server.

Client and Server should cooperate in order to provide power full tools to the user. Some times one or other, provides limitations or the cooperation requires to changes in the source code editor design.

Anjuta for example, has implemented a database to track symbols definitions, this is no necessary as an LSP Client, because the Server should track them for you. Client just need to request if a symbol is found and get back its type and children (a very common concept on Object Oriented Programming Languages like Vala).

A Server requires lot of information from the Client, like the root directory and compilation flags used, so it can provide reliable diagnostics and, like in Vala, the --pkg switches in use so it can find the namespaces in use for both, completion and diagnostics.

Vala has its own Language Compiler and as many others, creates a tree of structured information about the code in order to translate it to C and then use another compiler to transform the resulted code to machine code. The Vala’s tree lot of the information we need to implement an LSP.

Recently GVls was accepted by GNOME Builder, as The Vala Language Server for source editing. It provides completion, goto definition and diagnostics, with more services coming.

There are improvements and new implementations in GVls to work on and hope a Client for Microsoft Visual Studio Code will be implemented soon.

Vala will be impacted by GVls for sure, in order to improve diagnostics messages, symbol resolution, completion and so on, just need to push it to its limits and find new opportunities for improvements.

GNOME Builder, has gained some new LSP implementations while integrating GVls and hope both projects found a way to provide more powerful features for Object Oriented Programmers. One area to work on, is on Vala Source Code Highlighting, in order to help programmers to know if an used symbol is correctly spelled by seeking for in the Server’s symbols database. Diagnostics is other one, where we need a way to introspect the Build System, in order to find Vala’s compiler’s switches in use, so we can improve error or warning messages.

JSON to/from GObject

In order to implement LSP in GVls, a new GVls.VariantObject interface to be implemented by any GObject class. It provides any GObject the capability to be converted to/from its GLib.Variant representation. In GVls we use JSON-GLIB, so a GLib.Variant is converted to JSON and back, so now you can use any JSON based service to serialize and parse information using, for example, JSON-RPC which in turn is the one used to implement a LSP Client and Server communication.

2020-01-31 Friday.

  • More TDF BoD meetings, adding the staff & MC all day - so much talent and possibility in one room. Took extensive minutes.
  • FLOSS foundations dinner in the evening, back early to write slides, up until rather early on that.

FOSDEM 2020

It’s that time of the year, and I need to thank my company Kinvolk for sponsoring this waffle-gobbling-presentation-hopping event we all love.

Hoping to meet all those people I usually see at this time of the year, and new ones too.

If you are interested in Kinvolk and our products, let’s have a beer (or a coffee) and talk about it!

January 30, 2020

GtkSourceView Snippets

I’m trying to blog about every week now this year, so here we go again.

The past week I’ve been pushing hard on finishing up the snippets work for the GTK 4 port. It’s always quite a bit more work to push something upstream because you have to be so much more complete while being generic at the same time.

I think at this point though I can move on to other features and projects as the branch seems to be in good shape. I’ve fixed a number of bugs in the GTK 4 port along the way and made tests, documentation, robustness fixes, style-scheme integration, a completion provider, file-format and parser, and support for layering snippet files the same way style-schemes and language-specs work.

As part of the GTK 4 work I’ve spent a great deal time modernizing the code-base. Now that we can depend on the same things that GTK 4 will depend on, we can use some more modern compiler features. Additionally, GObject has matured so much since most of the library was written and we can use that to our advantage.

January 29, 2020

Interested in a GUADEC remote attendance party in the UK, July 2020?

tl;dr: Fill out this 5 minute survey if you want to meet up in the UK to attend GUADEC remotely.

GUADEC is in Mexico this year, which is great! This means that, for once, the tables are turned and people in Europe will get to experience what everyone in the rest of the world normally experiences for GUADEC: long travel times. That’s no bad thing, but I suspect it means there’ll be more people from Europe who are taking a break from GUADEC this year.

I don’t want to travel to GUADEC, but do want to keep up with the conference and see people. So I’m looking at organising a UK remote attendance party for GUADEC, where anyone who isn’t going to Mexico is welcome to come along for a few days, follow the conference remotely, hack together, and socialise together.

This would be open to the same audience as GUADEC itself, but limited to people in the UK. It would be great if people in other countries who can’t make it to Mexico also got together to follow the conference, but I don’t want to encourage travel from Europe to the UK to attend this party. This is not (currently) an official GNOME event.

So, to get the ball rolling, here’s a 5 minute survey. Please fill it in if you’re at all interested in any of this. Thanks!

Data transfer in GTK4

The traditional methods for user-initiated data transfers between desktop apps are the clipboard or Drag-and-Drop. GTK+ has supported these methods since the beginning of time, but up until GTK3, the APIs we had for this kind of data transfer were thinly disguised copies of the corresponding X11 apis: selections, and properties and atoms. This is not too surprising, since the entire GDK api was modeled on X11. Unfortunately, the implementation includes horrors such as incremental transfers and string format conversions.

For GTK4, we’re leaving these things behind as we are moving things in GDK around to be closer to the Wayland API. Data transfer is one the areas in most urgent need of this modernization. Thankfully, it is almost complete at this point, so it is worth taking a look at what has changed, and how things will work in the future.

Concepts

If the data your application wants to send is not a string, it is probably some object, such as GFile, a GdkTexture, or a GdkRGBA. The app on the receiving side may be not use GTK or GLib, and thus won’t know these types. And even if it does, there’s no way to get the objects from one process to the other in one piece.

At the core, the data transfer works be sending a file descriptor from the source app, and the target app reading a stream of bytes from it. The protocols for clipboard and DND use mime types such as text/uri-list, image/png or application/x-color to identify the format of the byte stream.

Sending an object involves negotiating a data format that both sides support, serializing the object on the source side into a byte stream of that format, transferring the data, and deserializing the object on the target side.

The local case

Before moving on to concrete APIs, it is worth pausing a moment to consider another common case of data transfer: inside a single application. It is quite common to use the same clipboard and DND mechanisms to send data from the one side of the application to the other. Since we are not crossing process boundaries in this case, we can avoid the byte stream and the associated serialization and deserialization overhead, and transfer the data by just passing a reference to the object.

APIs

The objects we’ve alluded to in the previous section are described by GTypes such as G_TYPE_FILE or GDK_TYPE_TEXTURE, while, as mentioned, the data exchange formats in Wayland and X11 protocols are described by mime types.

The first API we have introduced to deal with these types is the GdkContentFormats object. It can contain a list of formats, which may be GTypes or mime types. We use GdkContentFormats objects to describe the formats that an application can offer data in, as well as the formats that an application can receive data it.

You may wonder why we are mixing GTypes and mime types in the same object. The answer is that we want to handle both the cross-process and the local case with the same APIs. And while we need to match mime types for the cross-process case, we need the GTypes for the local case.

Conversion

We still need a way to associate GTypes and mime types that we can convert into each other. This is handled by the GdkContentSerializer and GdkContentDeserializer APIs. These are essentially registries of conversion functions: GdkContentSerializer knows how to convert GTypes into mime types, and GdkContentDeserializer handles the other direction.

GDK has predefined conversions for common types, but the system is extensible using gdk_content_register_serializer and gdk_content_register_deserializer.

Content

Now we know how to describe formats and convert between them, but to  put this all together, we still need a convenient api that takes an object on the one side, and provides a bytes stream on the other.  For this, we added the GdkContentProvider API.

A GdkContentProvider lets you combine an object with a format description on the input side, and provides an async writer API on the output side that can be connected to the file descriptor that we want to send the data over.

Typical content providers are created like this:

gdk_content_provider_new_for_value (gvalue)
gdk_content_provider_new_for_bytes (gbytes, mimetype)

The GValue that it takes contains both an object and information about its type, so we don’t need extra type information, if we provide the object as  a GBytes (essentially a just a bit of memory), we need to provide the type information separately.

The Clipboard

GTK3 has a GtkClipboard object, which provides the implemention for copy/paste operations. Having this object in GTK is not ideal, since it needs different implementations on the platforms that are supported by GTK. Therefore, GTK4 moves the object to GDK, and consequently renames it to GdkClipboard. It has also been ported to the new data transfer apis that are described above. To put data on the clipboard in GTK4, you use one of the ‘set’ apis:

gdk_clipboard_set_content()
gdk_clipboard_set_value()
gdk_clipboard_set_text()

Ultimately, all of these functions end up associating a GdkContentProvider with the clipboard.

To read data from the clipboard in GTK4, you use one of the async ‘read’ apis:

gdk_clipboard_read_async()
gdk_clipboard_read_value_async()
gdk_clipboard_read_text_async()

Drag-and-Drop

The GTK3 Drag-and-Drop api involves listening to a number of signals on GtkWidget, and calling a few special setup functions for drag sources and destinations. It is flexible, but generally considered confusing, and we won’t describe it in detail here.

In GTK4,  the Drag-and-Drop api has been reorganized around the concepts of content providers and event controllers. To initiate a Drag-and-Drop operation, you create a GtkDragSource event controller that reacts to drag gestures (you can also start ‘one-off’ Drag-and-Drop operations by just calling gdk_drag_begin yourself), and you give it a GdkContentProvider for the data that you want to transfer. To receive Drag-and-Drop operations, you create a GtkDropTarget event controller and call an async read method when it emits the ::drop-done signal:

gdk_drop_read_value_async()
gdk_drop_read_text_async()

January 28, 2020

Avoiding gaps in IOMMU protection at boot

When you save a large file to disk or upload a large texture to your graphics card, you probably don't want your CPU to sit there spending an extended period of time copying data between system memory and the relevant peripheral - it could be doing something more useful instead. As a result, most hardware that deals with large quantities of data is capable of Direct Memory Access (or DMA). DMA-capable devices are able to access system memory directly without the aid of the CPU - the CPU simply tells the device which region of memory to copy and then leaves it to get on with things. However, we also need to get data back to system memory, so DMA is bidirectional. This means that DMA-capable devices are able to read and write directly to system memory.

As long as devices are entirely under the control of the OS, this seems fine. However, this isn't always true - there may be bugs, the device may be passed through to a guest VM (and so no longer under the control of the host OS) or the device may be running firmware that makes it actively malicious. The third is an important point here - while we usually think of DMA as something that has to be set up by the OS, at a technical level the transactions are initiated by the device. A device that's running hostile firmware is entirely capable of choosing what and where to DMA.

Most reasonably recent hardware includes an IOMMU to handle this. The CPU's MMU exists to define which regions of memory a process can read or write - the IOMMU does the same but for external IO devices. An operating system that knows how to use the IOMMU can allocate specific regions of memory that a device can DMA to or from, and any attempt to access memory outside those regions will fail. This was originally intended to handle passing devices through to guests (the host can protect itself by restricting any DMA to memory belonging to the guest - if the guest tries to read or write to memory belonging to the host, the attempt will fail), but is just as relevant to preventing malicious devices from extracting secrets from your OS or even modifying the runtime state of the OS.

But setting things up in the OS isn't sufficient. If an attacker is able to trigger arbitrary DMA before the OS has started then they can tamper with the system firmware or your bootloader and modify the kernel before it even starts running. So ideally you want your firmware to set up the IOMMU before it even enables any external devices, and newer firmware should actually do this automatically. It sounds like the problem is solved.

Except there's a problem. Not all operating systems know how to program the IOMMU, and if a naive OS fails to remove the IOMMU mappings and asks a device to DMA to an address that the IOMMU doesn't grant access to then things are likely to explode messily. EFI has an explicit transition between the boot environment and the runtime environment triggered when the OS or bootloader calls ExitBootServices(). Various EFI components have registered callbacks that are triggered at this point, and the IOMMU driver will (in general) then tear down the IOMMU mappings before passing control to the OS. If the OS is IOMMU aware it'll then program new mappings, but there's a brief window where the IOMMU protection is missing - and a sufficiently malicious device could take advantage of that.

The ideal solution would be a protocol that allowed the OS to indicate to the firmware that it supported this functionality and request that the firmware not remove it, but in the absence of such a protocol we're left with non-ideal solutions. One is to prevent devices from being able to DMA in the first place, which means the absence of any IOMMU restrictions is largely irrelevant. Every PCI device has a busmaster bit - if the busmaster bit is disabled, the device shouldn't start any DMA transactions. Clearing that seems like a straightforward approach. Unfortunately this bit is under the control of the device itself, so a malicious device can just ignore this and do DMA anyway. Fortunately, PCI bridges and PCIe root ports should only forward DMA transactions if their busmaster bit is set. If we clear that then any devices downstream of the bridge or port shouldn't be able to DMA, no matter how malicious they are. Linux will only re-enable the bit after it's done IOMMU setup, so we should then be in a much more secure state - we still need to trust that our motherboard chipset isn't malicious, but we don't need to trust individual third party PCI devices.

This patch just got merged, adding support for this. My original version did nothing other than clear the bits on bridge devices, but this did have the potential for breaking devices that were still carrying out DMA at the moment this code ran. Ard modified it to call the driver shutdown code for each device behind a bridge before disabling DMA on the bridge, which in theory makes this safe but does still depend on the firmware drivers behaving correctly. As a result it's not enabled by default - you can either turn it on in kernel config or pass the efi=disable_early_pci_dma kernel command line argument.

In combination with firmware that does the right thing, this should ensure that Linux systems can be protected against malicious PCI devices throughout the entire boot process.

comment count unavailable comments

libinput's timer offset errors

Let's say you have a friend (this wouldn't happen to you, of course, just that friend) who is staring at their system logs and wonder why it is full of messages similar to this:


libinput error: client bug: timer event5 debounce short: offset negative (-7ms)
And the question is of course - what is going on here and why hasn't this been fixed yet. Now, the libinput documentation explains this already but it's always worthwhile to fire out a blog post into the void in the hope someone reads it.

libinput uses a specific model to communicate with the Wayland compositor (or the X server). There is a single epoll file descriptor and that fd will trigger whenever something happens that's of interest to libinput. When that fd triggers, the compositor is expected to call libinput_dispatch() which is the main "do stuff" function of libinput.

The actual trigger doesn't matter, it could be an event from a device but it could be something else. The caller doesn't have to care. All that matters is that there is code like this:


if (libinput_fd_triggered_in_select)
libinput_dispatch();
And then libinput will do the right thing. Whether you also want events from libinput is almost orthogonal to this.

libinput uses timerfd internally so any timeouts also trigger the epoll fd. Timeouts are scheduled based on the event's time stamp, so if you get an event with timestamp T, a timeout of 180ms will be scheduled for time T + 180ms. So the process looks something like this:


T(0): kernel button event
T(0): libinput_dispatch(): schedule timeout for T(0+180)
...
T(180): epoll fd triggers
T(180): libinput_dispatch(): process timeout
...
This works generally fine. Even with some delays we don't generally need to worry about the timeouts and they still trigger as expected. But some of the timeouts are "short", as in 8ms short. And this is where these warnings may trigger.

Let's say your compositor is busy doing some rendering. The epoll fd triggers with a button event but the compositor is too busy to handle it immediately. Instead, it finishes whatever it's doing and only then calls libinput_dispatch():


T(0): kernel button event
...
T(12): libinput_dispatch(): schedule timeout for T(0+8)

libinput error: client bug: timer event5 debounce short: offset negative (-4ms)
libinput will still use the event's timestamp instead of the wall clock time so the scheduled timeout is no longer in the future. And that is when the error message will be printed. This isn't a libinput bug, it's always a bug in the compositor. Especially gnome-shell is still struggling with these instances and while great strives have been made to make it more responsive, it can still happen.

The error message may seem cryptic, but it provides a bunch of useful information: event5 is your event node, "debounce short" is the timer name so we know where we got stuck. And 4ms gives us an indication how much we got delayed.

And for the record: the other end of this issue - delayed libinput_dispatch() after a timeout should have triggered is handled quietly by libinput. For example, if you have a physical event queued and a timeout expiry, we will process the earlier one first to make sure the sequences are handled correctly.

January 27, 2020

Hunting UEFI Implants

Last week I spent 3 days training on how to detect UEFI firmware implants. The training was run by Alex Matrosov via Hardwear.io and was a comprehensive deep-dive into UEFI firmware internals so that we could hunt for known and unknown implants. I’d 100% recommend this kind of training, it was excelent. Although I understood the general concepts of the protection mechanisms like SMM, HP Sure Start and Intel BIOSGuard before doing the training, it was really good to understand how the technologies really worked, with real world examples of where hardware vendors were getting the implementation wrong – giving the bad guys full control of your hardware. The training was superb, and Alex used lots of hands-on lab sessions to avoid PowerPoint overload. My fellow students were a mixture of security professionals and employees from various government departments from all over the world. We talked, a lot.

My personal conclusion quite simply is that we’re failing as an industry. In the pursuit to reduce S3 resume time from 2s to 0.5s we introduce issues like the S3 bootscript vulnerability. With the goal to boot as quickly as possible, we only check the bare minimum certificate chain allowing additional malicious DXEs to be added to an image. OEMs are choosing inexpensive EC hardware from sketchy vendors that are acting as root of trust and also emulating hardware designed 30 years ago, whilst sharing the system SPI chip. By trying to re-use existing power management primitives like SMM as a security boundary the leaky abstractions fail us. Each layer in the security stack is assuming that the lower below it is implemented correctly, and so all it takes is one driver with SMM or CSME access to not check a memory address in a struct correctly and everything on top (e.g. BootGuard, ALSR, SELinux, etc) is broken. Coreboot isn’t the panacea here either as to get that to run you need to turn off various protections like BootGuard, and some techniques like Sure Start mean that Coreboot just isn’t a viable option. The industry seems invested into EDK2, for better or worse. This shouldn’t just be important to the few people just buying stuff from Purism – 10,000x laptops are being sold on Amazon for every laptop sold by vendors that care about this stuff.

Most of the easy-to-exploit issues are just bugs with IBV or ODM-provided code, some of which can be fixed with a firmware update. Worst still, if you allow your “assumed secure” laptop out of sight then all bets are off with security. About a quarter of people at the UEFI training had their “travel laptop” tampered with at some point – with screws missing after “customs inspections” or with tamper seals broken after leaving a laptop in a hotel room. You really don’t need to remove the screws to image a hard drive these days. But, lets back away from the state-sponsored attacker back to reality for a minute.

The brutal truth is that security costs money. Vendors have to choose between saving 10 cents on a bill-of-materials by sharing a SPI chip (so ~$10K over a single batch), or correctly implementing BIOSGuard. What I think the LVFS now needs to do is provide some easy-to-understand market information to people buying hardware. We already know a huge amount of information about the device from signed reports and from analyzing the firmware binaries. What we’re not doing very well is explaining it to the user in a way they can actually understand. I didn’t understand the nuances between BIOSGuard and BootGuard until a few days ago, and I’ve been doing this stuff for years.

What I propose we do is assign some core protections some weight, and then verify and document how each vendor is configuring each model. For instance, I might say that for my dads laptop any hardware “SEC1” and above is fine as he’s only using it for Facebook or YouTube and it needs to be inexpensive. For my personal laptop I would be happy to restrict my choice of models to anything SEC3 and above. If you’re working as a journalist under some corrupt government, or am a security researcher, only SEC4 and above would be suitable. The reality is that SEC4 is going to be several hundred dollars more expensive than some cheap imported no-name hardware that doesn’t even conform to SEC1.

Of course, we’ll need to expand the tests we do in fwupd to detect implementation errors, and to verify that the model that we’ve purchased does indeed match the SEC level advertised by the LVFS. I’m talking to a few different people on how to do this securely. What I do know is that it will involve a reboot to get some of the data that we can’t even get in kernel mode with SecureBoot turned on. The chipsec report gets us some of the way there, but it’s just too complicated for end users and won’t work with SB turned on.

My proposal would be as follows:

  • SEC1: SecureBoot, BIOS_WE, BLE, SMM_BWP, and updates on the LVFS, with no existing detectable SMM issues (like ThinkPwn for example)
  • SEC2: PRx set correctly, if not using BootGuard or BIOSGuard, with PCR0 attestation data
  • SEC3: BootGuard enabled, with the EC controller requiring signed images
  • SEC4: Intel BIOSGuard or HP SureStart
  • SEC5: Hardware attestation like Apple T2 or Google Titan

SEC1 is a really low bar. Anything not unsetting BIOS_WE can be flashed at runtime trivially. If you’re traveling with a laptop for work you really ought to be specifying at least a SEC3 level of protection.

Of course, some vendors might not care at all about security for some models. A “gaming laptop” with a flashing RGB keyboard backlight is really designed for playing games as fast as possible, and the fact that the BIOS is unlocked might be a good thing as then the user can flash a custom unsigned BIOS with a CounterStrike-themed vendor image. I don’t think we ought to make vendors feel guilty about not even hitting SEC1. Perhaps we could let the consumer vote with their wallet and make the ecosystem more secure. I’m not sold on the SECx name either, it’s not very catchy. I suck at naming stuff. Comments welcome.

A big AppStream status update

It has been a while since the last AppStream-related post (or any post for that matter) on this blog, but of course development didn’t stand still all this time. Quite the opposite – it was just me writing less about it, which actually is a problem as some of the new features are much less visible. People don’t seem to re-read the specification constantly for some reason 😉. As a consequence, we have pretty good adoption of features I blogged about (like fonts support), but much of the new stuff is still not widely used. Also, I had to make a promise to several people to blog about the new changes more often, and I am definitely planning to do so. So, expect posts about AppStream stuff a bit more often now.

What actually was AppStream again? The AppStream Freedesktop Specification describes two XML metadata formats to describe software components: One for software developers to describe their software, and one for distributors and software repositories to describe (possibly curated) collections of software. The format written by upstream projects is called Metainfo and encompasses any data installed in /usr/share/metainfo/, while the distribution format is just called Collection Metadata. A reference implementation of the format and related features written in C/GLib exists as well as Qt bindings for it, so the data can be easily accessed by projects which need it.

The software metadata contains a unique ID for the respective software so it can be identified across software repositories. For example the VLC Mediaplayer is known with the ID org.videolan.vlc in every software repository, no matter whether it’s the package archives of Debian, Fedora, Ubuntu or a Flatpak repository. The metadata also contains translatable names, summaries, descriptions, release information etc. as well as a type for the software. In general, any information about a software component that is in some form relevant to displaying it in software centers is or can be present in AppStream. The newest revisions of the specification also provide a lot of technical data for systems to make the right choices on behalf of the user, e.g. Fwupd uses AppStream data to describe compatible devices for a certain firmware, or the mediatype information in AppStream metadata can be used to install applications for an unknown filetype easier. Information AppStream does not contain is data the software bundling systems are responsible for. So mechanistic data how to build a software component or how exactly to install it is out of scope.

So, now let’s finally get to the new AppStream features since last time I talked about it – which was almost two years ago, so quite a lot of stuff has accumulated!

Specification Changes/Additions

Web Application component type

(Since v0.11.7) A new component type web-application has been introduced to describe web applications. A web application can for example be GMail, YouTube, Twitter, etc. launched by the browser in a special mode with less chrome. Fundamentally though it is a simple web link. Therefore, web apps need a launchable tag of type url to specify an URL used to launch them. Refer to the specification for details. Here is a (shortened) example metainfo file for the Riot Matrix client web app:

<component type="web-application">
  <id>im.riot.webapp</id>
  <metadata_license>FSFAP</metadata_license>
  <project_license>Apache-2.0</project_license>
  <name>Riot.im</name>
  <summary>A glossy Matrix collaboration client for the web</summary>
  <description>
    <p>Communicate with your team[...]</p>
  </description>
  <icon type="stock">im.riot.webapp</icon>
  <categories>
    <category>Network</category>
    <category>Chat</category>
    <category>VideoConference</category>
  </categories>
  <url type="homepage">https://riot.im/</url>
  <launchable type="url">https://riot.im/app</launchable>
</component>

Repository component type

(Since v0.12.1) The repository component type describes a repository of downloadable content (usually other software) to be added to the system. Once a component of this type is installed, the user has access to the new content. In case the repository contains proprietary software, this component type pairs well with the agreements section.

This component type can be used to provide easy installation of e.g. trusted Debian or Fedora repositories, but also can be used for other downloadable content. Refer to the specification entry for more information.

Operating System component type

(Since v0.12.5) It makes sense for the operating system itself to be represented in the AppStream metadata catalog. Information about it can be used by software centers to display information about the current OS release and also to notify about possible system upgrades. It also serves as a component the software center can use to attribute package updates to that do not have AppStream metadata. The operating-system component type was designed for this and you can find more information about it in the specification documentation.

Icon Theme component type

(Since v0.12.8) While styles, themes, desktop widgets etc. are already covered in AppStream via the addon component type as they are specific to the toolkit and desktop environment, there is one exception: Icon themes are described by a Freedesktop specification and (usually) work independent of the desktop environment. Because of that and on request of desktop environment developers, a new icon-theme component type was introduced to describe icon themes specifically. From the data I see in the wild and in Debian specifically, this component type appears to be very underutilized. So if you are an icon theme developer, consider adding a metainfo file to make the theme show up in software centers! You can find a full description of this component type in the specification.

Runtime component type

(Since v0.12.10) A runtime is mainly known in the context of Flatpak bundles, but it actually is a more universal concept. A runtime describes a defined collection of software components used to run other applications. To represent runtimes in the software catalog, the new AppStream component type was introduced in the specification, but it has been used by Flatpak for a while already as a nonstandard extension.

Release types

(Since v0.12.0) Not all software releases are created equal. Some may be for general use, others may be development releases on the way to becoming an actual final release. In order to reflect that, AppStream introduced at type property to the release tag in a releases block, which can be either set to stable or development. Software centers can then decide to hide or show development releases.

End-of-life date for releases

(Since v0.12.5) Some software releases have an end-of-life date from which onward they will no longer be supported by the developers. This is especially true for Linux distributions which are described in a operating-system component. To define an end-of-life date, a release in AppStream can now have a date_eol property using the same syntax as a date property but defining the date when the release will no longer be supported (refer to the releases tag definition).

Details URL for releases

(Since v0.12.5) The release descriptions are short, text-only summaries of a release, usually only consisting of a few bullet points. They are intended to give users a fast, quick to read overview of a new release that can be displayed directly in the software updater. But sometimes you want more than that. Maybe you are an application like Blender or Krita and have prepared an extensive website with an in-depth overview, images and videos describing the new release. For these cases, AppStream now permits an url tag in a release tag pointing to a website that contains more information about a particular release.

Release artifacts

(Since v0.12.6) AppStream limited release descriptions to their version numbers and release notes for a while, without linking the actual released artifacts. This was intentional, as any information how to get or install software should come from the bundling/packaging system that Collection Metadata was generated for.

But the AppStream metadata has outgrown this more narrowly defined purpose and has since been used for a lot more things, like generating HTML download pages for software, making it the canonical source for all the software metadata in some projects. Coming from Richard Hughes awesome Fwupd project was also the need to link to firmware binaries from an AppStream metadata file, as the LVFS/Fwupd use AppStream metadata exclusively to provide metadata for firmware. Therefore, the specification was extended with an artifacts tag for releases, to link to the actual release binaries and tarballs. This replaced the previous makeshift “release location” tag.

Release artifacts always have to link to releases directly, so the releases can be acquired by machines immediately and without human intervention. A release can have a type of source or binary, indicating whether a source tarball or binary artifact is linked. Each binary release can also have an associated platform triplet for Linux systems, an identifier for firmware, or any other identifier for a platform. Furthermore, we permit sha256 and blake2 checksums for the release artifacts, as well as specifying sizes. Take a look at the example below, or read the specification for details.

<releases>
​  <release version="1.2" date="2014-04-12" urgency="high">
​    [...]
​    <artifacts>
​      <artifact type="binary" platform="x86_64-linux-gnu">
​        <location>https://example.com/mytarball.bin.tar.xz</location>
​        <checksum type="blake2">852ed4aff45e1a9437fe4774b8997e4edfd31b7db2e79b8866832c4ba0ac1ebb7ca96cd7f95da92d8299da8b2b96ba480f661c614efd1069cf13a35191a8ebf1</checksum>
​        <size type="download">12345678</size>
​        <size type="installed">42424242</size>
​      </artifact>
​      <artifact type="source">
​        <location>https://example.com/mytarball.tar.xz</location>
​        [...]
​      </artifact>
​    </artifacts>
​  </release>
​</releases>

Issue listings for releases

(Since v0.12.9) Software releases often fix issues, sometimes security relevant ones that have a CVE ID. AppStream provides a machine-readable way to figure out which components on your system are currently vulnerable to which CVE registered issues. Additionally, a release tag can also just contain references to any normal resolved bugs, via bugtracker URLs. Refer to the specification for details. Example for the issues tag in AppStream Metainfo files:

<issues>
​  <issue url="https://example.com/bugzilla/12345">bz#12345</issue>
​  <issue type="cve">CVE-2019-123456</issue>
​</issues>

Requires and Recommends relations

(Since v0.12.0) Sometimes software has certain requirements only justified by some systems, and sometimes it might recommend specific things on the system it will run on in order to run at full performance.

I was against adding relations to AppStream for quite a while, as doing so would add a more “functional” dimension to it, impacting how and when software is installed, as opposed to being only descriptive and not essential to be read in order to install software correctly. However, AppStream has pretty much outgrown its initial narrow scope and adding relation information to Metainfo files was a natural step to take. For Fwupd it was an essential step, as Fwupd firmware might have certain hard requirements on the system in order to be installed properly. And AppStream requirements and recommendations go way beyond what regular package dependencies could do in Linux distributions so far.

Requirements and recommendations can be on other software components via their id, on a modalias, specific kernel version, existing firmware version or for making system memory recommendations. See the specification for details on how to use this. Example:

<requires>
  <id version="1.0" compare="ge">org.example.MySoftware</id>
​  <kernel version="5.6" compare="ge">Linux</kernel>
​</requires>
<recommends>
​  <memory>2048</memory> <!-- recommend at least 2GiB of memory -->
​</recommends>

This means that AppStream currently supported provides, suggests, recommends and requires relations to refer to other software components or system specifications.

Agreements

(Since v0.12.1) The new agreement section in AppStream Metainfo files was added to make it easier for software to be compliant to the EU GDPR. It has since been expanded to be used for EULAs as well, which was a request coming (to no surprise) from people having to deal with corporate and proprietary software components. An agreement consists of individual sections with headers and descriptive texts and should – depending on the type – be shown to the user upon installation or first use of a software component. It can also be very useful in case the software component is a firmware or driver (which often is proprietary – and companies really love their legal documents and EULAs).

Contact URL type

(Since v0.12.4) The contact URL type can be used to simply set a link back to the developer of the software component. This may be an URL to a contact form, their website or even a mailto: link. See the specification for all URL types AppStream supports.

Videos as software screenshots

(Since v0.12.8) This one was quite long in the making – the feature request for videos as screenshots had been filed in early 2018. I was a bit wary about adding video, as that lets you run into a codec and container hell as well as requiring software centers to support video and potentially requiring the appstream-generator to get into video transcoding, which I really wanted to avoid. Alternatively, we would have had to make AppStream add support for multiple likely proprietary video hosting platforms, which certainly would have been a bad idea on every level. Additionally, I didn’t want to have people add really long introductory videos to their applications.

Ultimately, the problem was solved by simplification and reduction: People can add a video as “screenshot” to their software components, as long as it isn’t the first screenshot in the list. We only permit the vp9 and av1 codecs and the webm and matroska container formats. Developers should expect the audio of their videos to be muted, but if audio is present, the opus codec must be used. Videos will be size-limited, for example Debian imposes a 14MiB limit on video filesize. The appstream-generator will check for all of these requirements and reject a video in case it doesn’t pass one of the checks. This should make implementing videos in software centers easy, and also provide the safety guarantees and flexibility we want.

So far we have not seen many videos used for application screenshots. As always, check the specification for details on videos in AppStream. Example use in a screenshots tag:

​<screenshots>
​  <screenshot type="default">
​    <image type="source" width="1600" height="900">https://example.com/foobar/screenshot-1.png</image>
​  </screenshot>
​  <screenshot>
​    <video codec="av1" width="1600" height="900">https://example.com/foobar/screencast.mkv</video>
​  </screenshot>
​ </screenshots>

Emphasis and code markup in descriptions

(Since v0.12.8) It has long been requested to have a little bit more expressive markup in descriptions in AppStream, at least more than just lists and paragraphs. That has not happened for a while, as it would be a breaking change to all existing AppStream parsers. Additionally, I didn’t want to let AppStream descriptions become long, general-purpose “how to use this software” documents. They are intended to give a quick overview of the software, and not comprehensive information. However ultimately we decided to add support for at least two more elements to format text: Inline code elements as well as em emphases. There may be more to come, but that’s it for now. This change was made about half a year ago, and people are currently advised to use the new styling tags sparingly, as otherwise their software descriptions may look odd when parsed with older AppStream implementation versions.

Remove-component merge mode

(Since v0.12.4) This addition is specified for the Collection Metadata only, as it affects curation. Since AppStream metadata is in one big pool for Linux distributions, and distributions like Debian freeze their repositories, it sometimes is required to merge metadata from different sources on the client system instead of generating it in the right format on the server. This can also be used for curation by vendors of software centers. In order to edit preexisting metadata, special merge components are created. These can permit appending data, replacing data etc. in existing components in the metadata pool. The one thing that was missing was a mode that permitted the complete removal of a component. This was added via a special remove-component merge mode. This mode can be used to pull metadata from a software center’s catalog immediately even if the original metadata was frozen in place in a package repository. This can be very useful in case an inappropriate software component is found in the repository of a Linux distribution post-release. Refer to the specification for details.

Custom metadata

(Since v0.12.1) The AppStream specification is extensive, but it can not fit every single special usecase. Sometimes requests come up that can’t be generalized easily, and occasionally it is useful to prototype a feature first to see if it is actually used before adding it to the specification properly. For that purpose, the custom tag exists. The tag defines a simple key-value structure that people can use to inject arbitrary metadata into an AppStream metainfo file. The libappstream library will read this tag by default, providing easy access to the underlying data. Thereby, the data can easily be used by custom applications designed to parse it. It is important to note that the appstream-generator tool will by default strip the custom data from files unless it has been whitelisted explicitly. That way, the creator of a metadata collection for a (package) repository has some control over what data ends up in the resulting Collection Metadata file. See the specification for more details on this tag.

Miscellaneous additions

(Since v0.12.9) Additionally to JPEG and PNG, WebP images are now permitted for screenshots in Metainfo files. These images will – like every image – be converted to PNG images by the tool generating Collection Metadata for a repository though.

(Since v0.12.10) The specification now contains a new name_variant_suffix tag, which is a translatable string that software lists may append to the name of a component in case there are multiple components with the same name. This is intended to be primarily used for firmware in Fwupd, where firmware may have the same name but actually be slightly different (e.g. region-specific). In these cases, the additional name suffix is shown to make it easier to distinguish the different components in case multiple are present.

(Since v0.12.10) AppStream has an URI format to install applications directly from webpages via the appstream: scheme. This URI scheme now permits alternative IDs for the same component, in case it switched its ID in the past. Take a look at the specification for details about the URI format.

(Since v0.12.10) AppStream now supports version 1.1 of the Open Age Rating Service (OARS), so applications (especially games) can voluntarily age-rate themselves. AppStream does not replace parental guidance here, and all data is purely informational.

Library & Implementation Changes

Of course, besides changes to the specification, the reference implementation also received a lot of improvements. There are too many to list them all, but a few are noteworthy to mention here.

No more automatic desktop-entry file loading

(Since v0.12.3) By default, libappstream was loading information from local .desktop files into the metadata pool of installed applications. This was done to ensure installed apps were represented in software centers to allow them to be uninstalled. This generated much more pain than it was useful for though, with metadata appearing two to three times in software centers because people didn’t set the X-AppStream-Ignore=true tag in their desktop-entry files. Also, the generated data was pretty bad. So, newer versions of AppStream will only load data of installed software that doesn’t have an equivalent in the repository metadata if it ships a metainfo file. One more good reason to ship a metainfo file!

Software centers can override this default behavior change by setting the AS_POOL_FLAG_READ_DESKTOP_FILES flag for AsPool instances (which many already did anyway).

LMDB caches and other caching improvements

(Since v0.12.7) One of the biggest pain points in adding new AppStream features was always adjusting the (de)serialization of the new markup: AppStream exists as a YAML version for Debian-based distributions for Collection Metadata, an XML version based on the Metainfo format as default, and a GVariant binary serialization for on-disk caching. The latter was used to drastically reduce memory consumption and increase speed of software centers: Instead of loading all languages, only the one we currently needed was loaded. The expensive icon-finding logic, building of the token cache for searches and other operations were performed and the result was saved as a binary cache on-disk, so it was instantly ready when the software center was loaded next.

Adjusting three serialization formats was pretty laborious and a very boring task. And at one point I benchmarked the (de)serialization performance of the different formats and found out the the XML reading/writing was actually massively outperforming that of the GVariant cache. Since the XML parser received much more attention, that was only natural (but there were also other issues with GVariant deserializing large dictionary structures).

Ultimately, I removed the GVariant serialization and replaced it with a memory-mapped XML-based cache that reuses 99.9% of the existing XML serialization code. The cache uses LMDB, a small embeddable key-value store. This makes maintaining AppStream much easier, and we are using the same well-tested codepaths for caching now that we also use for normal XML reading/writing. With this change, AppStream also uses even less memory, as we only keep the software components in memory that the software center currently displays. Everything that isn’t directly needed also isn’t in memory. But if we do need the data, it can be pulled from the memory-mapped store very quickly.

While refactoring the caching code, I also decided to give people using libappstream in their own projects a lot more control over the caching behavior. Previously, libappstream was magically handling the cache behind the back of the application that was using it, guessing which behavior was best for the given usecase. But actually, the application using libappstream knows best how caching should be handled, especially when it creates more than one AsPool instance to hold and search metadata. Therefore, libappstream will still pick the best defaults it can, but give the application that uses it all control it needs, down to where to place a cache file, to permit more efficient and more explicit management of caches.

Validator improvements

(Since v0.12.8) The AppStream metadata validator, used by running appstreamcli validate <file>, is the tool that each Metainfo file should run through to ensure it is conformant to the AppStream specification and to give some useful hints to improve the metadata quality. It knows four issue severities: Pedantic issues are hidden by default (show them with the --pedantic flag) and affect upcoming features or really “nice to have” things that are completely nonessential. Info issues are not directly a problem, but are hints to improve the metadata and get better overall data. Things the specification recommends but doesn’t mandate also fall into this category. Warnings will result in degraded metadata but don’t make the file invalid in its entirety. Yet, they are severe enough that we fail the validation. Things like that are for example a vanishing screenshot from an URL: Most of the data is still valid, but the result may not look as intended. Invalid email addresses, invalid tag properties etc. fall into that category as well: They will all reduce the amount of metadata systems have available. So the metadata should definitely be warning-free in order to be valid. And finally errors are outright violation of the specification that may likely result in the data being ignored in its entirety or large chunks of it being invalid. Malformed XML or invalid SPDX license expressions would fall into that group.

Previously, the validator would always show very long explanations for all the issues it found, giving detailed information on an issue. While this was nice if there were few issues, it produces very noisy output and makes it harder to quickly spot the actual error. So, the whole validator output was changed to be based on issue tags, a concept that is also known from other lint tools such as Debian’s Lintian: Each error has its own tag string, identifying it. By default, we only show the tag string, line of the issue, severity and component name it affects as well a short repeat of an invalid value (in case that’s applicable to the issue). If people do want to know detailed information, they can get it by passing --explain to the validation command. This solution has many advantages:

  • It makes the output concise and easy to read by humans and is mostly already self-explanatory
  • Machines can parse the tags easily and identify which issue was emitted, which is very helpful for AppStream’s own testsuite but also for any tool wanting to parse the output
  • We can now have translators translate the explanatory texts

Initially, I didn’t want to have the validator return translated output, as that may be less helpful and harder to search the web for. But now, with the untranslated issue tags and much longer and better explanatory texts, it makes sense to trust the translators to translate the technical explanations well.

Of course, this change broke any tool that was parsing the old output. I had an old request by people to have appstreamcli return machine-readable validator output, so they could integrate it better with preexisting CI pipelines and issue reporting software. Therefore, the tool can now return structured, machine-readable output in the YAML format if you pass --format=yaml to it. That output is guaranteed to be stable and can be parsed by any CI machinery that a project already has running. If needed, other output formats could be added in future, but for now YAML is the only one and people generally seem to be happy with it.

Create desktop-entry files from Metainfo

(Since v0.12.9) As you may have noticed, an AppStream Metainfo file contains some information that a desktop-entry file also contains. Yet, the two file formats serve very different purposes: A desktop file is basically launch instructions for an application, with some information about how it is displayed. A Metainfo file is mostly display information and less to none launch instructions. Admittedly though, there is quite a bit of overlap which may make it useful for some projects to simply generate a desktop-entry file from a Metainfo file. This may not work for all projects, most notably ones where multiple desktop-entry files exists for just one AppStream component. But for the simplest and most common of cases, a direct mapping between Metainfo and desktop-entry file, this option is viable.

The appstreamcli tool permits this now, using the appstreamcli make-desktop-file subcommand. It just needs a Metainfo file as first parameter, and a desktop-entry output file as second parameter. If the desktop-entry file already exists, it will be extended with the new data from tbe Metainfo file. For the Exec field in a desktop-entry file, appstreamcli will read the first binary entry in a provides tag, or use an explicitly provided line passed via the --exec parameter.

Please take a look at the appstreamcli(1) manual page for more information on how to use this useful feature.

Convert NEWS files to Metainfo and vice versa

(Since v0.12.9) Writing the XML for release entries in Metainfo files can sometimes be a bit tedious. To make this easier and to integrate better with existing workflows, two new subcommands for appstreamcli are now available: news-to-metainfo and metainfo-to-news. They permit converting a NEWS textfile to Metainfo XML and vice versa, and can be integrated with an application’s build process. Take a look at AppStream itself on how it uses that feature.

In addition to generating the NEWS output or reading it, there is also a second YAML-based option available. Since YAML is a structured format, more of the features of AppStream release metadata are available in the format, such as marking development releases as such. You can use the --format flag to switch the output (or input) format to YAML.

Please take a look at the appstreamcli(1) manual page for a bit more information on how to use this feature in your project.

Support for recent SPDX syntax

(Since v0.12.10) This has been a pain point for quite a while: SPDX is a project supported by the Linux Foundation to (mainly) provide a unified syntax to identify licenses for Open Source projects. They did change the license syntax twice in incompatible ways though, and AppStream already implemented a previous versions, so we could not simply jump to the latest version without supporting the old one.

With the latest release of AppStream though, the software should transparently convert between the different version identifiers and also support the most recent SPDX license expressions, including the WITH operator for license exceptions. Please report any issues if you see them!

Future Plans?

First of all, congratulations for reading this far into the blog post! I hope you liked the new features! In case you skipped here, welcome to one of the most interesting sections of this blog post! 😉

So, what is next for AppStream? The 1.0 release, of course! The project is certainly mature enough to warrant that, and originally I wanted to get the 1.0 release out of the door this February, but it doesn’t look like that date is still realistic. But what does “1.0” actually mean for AppStream? Well, here is a list of the intended changes:

  • Removal of almost all deprecated parts of the specification. Some things will remain supported forever though: For example the desktop component type is technically deprecated for desktop-application but is so widely used that we will support it forever. Things like the old application node will certainly go though, and so will the /usr/share/appdata path as metainfo location, the appcategory node that nobody uses anymore and all other legacy cruft. I will be mindful about this though: If a feature still has a lot of users, it will stay supported, potentially forever. I am closely monitoring what is used mainly via the information available via the Debian archive. As a general rule of thumb though: A file for which appstreamcli validate passes today is guaranteed to work and be fine with AppStream 1.0 as well.
  • Removal of all deprecated API in libappstream. If your application still uses API that is flagged as deprecated, consider migrating to the supported functions and you should be good to go! There are a few bigger refactorings planned for some of the API around releases and data serialization, but in general I don’t expect this to be hard to port.
  • The 1.0 specification will be covered by an extended stability promise. When a feature is deprecated, there will be no risk that it is removed or become unsupported (so the removal of deprecated stuff in the specification should only happen once). What is in the 1.0 specification will quite likely be supported forever.

So, what is holding up the 1.0 release besides the API cleanup work? Well, there are a few more points I want to resolve before releasing the 1.0 release:

  • Resolve hosting release information at a remote location, not in the Metainfo file (#240): This will be a disruptive change that will need API adjustments in libappstream for sure, and certainly will – if it happens – need the 1.0 release. Fetching release data from remote locations as opposed to having it installed with software makes a lot of sense, and I either want to have this implemented and specified properly for the 1.0 release, or have it explicitly dismissed.
  • Mobile friendliness / controls metadata (#192 & #55): We need some way to identify applications as “works well on mobile”. I also work for a company called Purism which happens to make a Linux-based smartphone, so this is obviously important for us. But it also is very relevant for users and other Linux mobile projects. The main issue here is to define what “mobile” actually means and what information makes sense to have in the Metainfo file to be future-proof. At the moment, I think we should definitely have data on supported input controls for a GUI application (touch vs mouse), but for this the discussion is still not done.
  • Resolving addon component type complexity (lots of issue reports): At the moment, an addon component can be created to extend an existing application by $whatever thing This can be a plugin, a theme, a wallpaper, extra content, etc. This is all running in the addon supergroup of components. This makes it difficult for applications and software centers to occasionally group addons into useful groups – a plugin is functionally very different from a theme. Therefore I intend to possibly allow components to name “addon classes” they support and that addons can sort themselves into, allowing easy grouping and sorting of addons. This would of course add extra complexity. So this feature will either go into the 1.0 release, or be rejected.
  • Zero pending feature requests for the specification: Any remaining open feature request for the specification itself in AppStream’s issue tracker should either be accepted & implemented, or explicitly deferred or rejected.

I am not sure yet when the todo list will be completed, but I am certain that the 1.0 release of AppStream will happen this year, most likely before summer. Any input, especially from users of the format, is highly appreciated.

Thanks a lot to everyone who contributed or is contributing to the AppStream implementation or specification, you are great! Also, thanks to you, the reader, for using AppStream in your project 😉. I definitely will give a bit more frequent and certainly shorter updates on the project’s progress from now on. Enjoy your rich software metadata, firmware updates and screenshot videos meanwhile! 😀

January 24, 2020

Starting my YouTube channel

I am going to try to do videos instead of (or maybe in addition to?) blogging this year. Just posted my very first video on my new YouTube channel: https://www.youtube.com/watch?v=br4WolkyMU4, talking about Fedora Silverblue and Flatpaks and OpenArena.

Friends of GNOME Update January 2020

Welcome to 2020 and the Friends of GNOME Update! We’d like to especially welcome all the new Friends of GNOME who joined during the Friends of GNOME drive at the end of 2019.

GNOME on the Road

We spent the end of 2019 at home and on vacation, preparing us for the excitement that 2020 is bringing.

In January we’ll be at Sustain Summit 2020 in Brussels, Belgium. Shortly afterwards, you will be able to find us at FOSDEM on February 1-2!

On Saturday, February 1, we will be having GNOME Beers at Bonnefooi starting at 19:30. It is located at Rue des Pierres 8, 10000 Brussels.

We will also have a booth at FOSDEM, where we’ll have stickers, smiles, and answers to your GNOME related questions. We’re always looking for people to hang out with us at the booth, and if you’re interested in volunteering, please contact kprogri@gnome.org.

Following FOSDEM, many of us will be at CopyLeft Conf, also in Brussels. So come by and say hi!

Molly will also be in Lagos, Nigeria for Open Source Festival, hosted by Open Source Africa, February 20 – 22. At Open Source Festival, Regina Nkemchor will run a workshop on “Improving the Linux User Experience for Aftrican Users” through GNOME.

Hackfest in Brussels

We’ll be hacking away at our first hackfest of 2020 January 28-31 in beautiful Brussels, Belgium! We’ll be working on GTK and accessibility. We’re especially looking for people who with accessibility experience and those who use adaptive technology to provide feedback.

We expanded the team

We’re excited to introduce you to Caroline Henriksen and Melissa Wu! Caroline is our new Brand Manager and Melissa will be overseeing the Coding Education Challenge.

Have you been following the Rothschild Patent Imaging case? Rothschild raised allegations against the GNOME Foundation for patent violation. Our Motion to Dismiss has been confirmed and will be presented to a judge on February 27.

If you want to help support our legal feeds, please donate to our Patent Troll Defense Fund.

2019 Year in Review

Over the final few weeks of 2019, we shared a number of news items and blog entries about our work over the past year. You can read them online:

Thank you!

Thank you so much for supporting GNOME! If you’re not already, consider becoming a Friend of GNOME and help sustain our work.

A forum for Flathub

Flathub is primarily built around GitHub, where applications manifests and infrastructure code live. Unsurprisingly, it turns out that code hosting platform isn’t exactly a go-to place for the community to connect, even if one slaps a discussion label on an issue. Timezones and personal commitments mean that IRC is also not an ideal platform for discussion, and Flathub does not have a mailing list for discussion and announcements.

To remedy this, Flathub now has a forum at discourse.flathub.org! Right now, there are three categories:

  • Requests for requesting new applications. (In the past, GitHub issues requesting new applications would tend to just be closed unless somebody is actively working on the request.)
  • Announcements for news posts from the Flathub team. (In the past, news posts have been scattered around the personal blogs of the Flathub team.)
  • General for other discussion about Flathub.

Please drop by the forum and say hello, even if you don’t have any specific questions yet! You can sign in with your GitHub account, if you already have one; and you can configure Discourse to send you emails if that’s what you prefer. (If you have an issue with a particular application, please open an issue on GitHub as before – this is the best way to get the attention of the maintainers of that application.)

See you there!

January 23, 2020

Navigating Docker for Windows versions

Recently, gitlab-runner got support for running jobs inside windows containers. This simplifies a lot the setup needed to get a windows CI job up and running, and it makes it similar how we do linux builds.

Windows though has a couple of gotchas, the behavior of docker on windows can vastly vary depending on which binary and/or configuration you use.

Containers on windows are dependent on the server version of the Host. For example, your server 2016 (1607) containers can only be executed on a server 2016 host. Currently there are 2 popular base versions that docker supports, Server 2016, and 2019. Gitlab-runner only supports server 2019, so we will go with that.

Microsoft recently introduced a set of  Process isolation apis, where up till now docker had been using a hyper-v backend to launch containers. This results in possibly more light-weight containers as opposed to light VMs with Hyper-V, but also now now docker for windows has 2 isolation backends, which can affect differently the behavior of your containers! Proccess Isolation seems to be the default for Server 2019.

Next you want to go and install docker, so you look up the Microsoft documentation. There’s one option for the servers, but there’s also an option for Docker Desktop for Windows 10, and to add to this Docker Desktop itself has 2 versions depending if you want the Community or Enterprise Edition! Also, you can’t install Docker on windows 10 the same way you would do on the Server, so you are left having to mix different binaries and hope everything works out. Spoiler, it didn’t work for me! There were couple of build/networking failure when using Docker Desktop, where the same Dockerfile built fine on the server host.

Bonus round:

Docker Desktop has 2 modes, one for running linux containers and one for windows containers! We talked about the 2 different isolation backends for windows above. Now it looks like the linux containers mode got a backend based on WSL2 in addition to the existing hyper-v backend it has been using.

Hope you made it to the end without losing count of all the different “containers” in the windows land. :)

Intersecting Intel & AMD Instruction Set Extensions

In some of my projects, I’ve recently had the need to utilize FMA (fused-multiply-add) or AVX instructions. Compiling C/C++ on X86_64 will by default only activate MXX and a few of the early SSE extensions. The utilized instruction set basically predates the core2 which was introduced in 2006. Math…

January 20, 2020

Verifying your system state in a secure and private way

Most modern PCs have a Trusted Platform Module (TPM) and firmware that, together, support something called Trusted Boot. In Trusted Boot, each component in the boot chain generates a series of measurements of next component of the boot process and relevant configuration. These measurements are pushed to the TPM where they're combined with the existing values stored in a series of Platform Configuration Registers (PCRs) in such a way that the final PCR value depends on both the value and the order of the measurements it's given. If any measurements change, the final PCR value changes.

Windows takes advantage of this with its Bitlocker disk encryption technology. The disk encryption key is stored in the TPM along with a policy that tells it to release it only if a specific set of PCR values is correct. By default, the TPM will release the encryption key automatically if the PCR values match and the system will just transparently boot. If someone tampers with the boot process or configuration, the PCR values will no longer match and boot will halt to allow the user to provide the disk key in some other way.

Unfortunately the TPM keeps no record of how it got to a specific state. If the PCR values don't match, that's all we know - the TPM is unable to tell us what changed to result in this breakage. Fortunately, the system firmware maintains an event log as we go along. Each measurement that's pushed to the TPM is accompanied by a new entry in the event log, containing not only the hash that was pushed to the TPM but also metadata that tells us what was measured and why. Since the algorithm the TPM uses to calculate the hash values is known, we can replay the same values from the event log and verify that we end up with the same final value that's in the TPM. We can then examine the event log to see what changed.

Unfortunately, the event log is stored in unprotected system RAM. In order to be able to trust it we need to compare the values in the event log (which can be tampered with) with the values in the TPM (which are much harder to tamper with). Unfortunately if someone has tampered with the event log then they could also have tampered with the bits of the OS that are doing that comparison. Put simply, if the machine is in a potentially untrustworthy state, we can't trust that machine to tell us anything about itself.

This is solved using a procedure called Remote Attestation. The TPM can be asked to provide a digital signature of the PCR values, and this can be passed to a remote system along with the event log. That remote system can then examine the event log, make sure it corresponds to the signed PCR values and make a security decision based on the contents of the event log rather than just on the final PCR values. This makes the system significantly more flexible and aids diagnostics. Unfortunately, it also means you need a remote server and an internet connection and then some way for that remote server to tell you whether it thinks your system is trustworthy and also you need some way to believe that the remote server is trustworthy and all of this is well not ideal if you're not an enterprise.

Last week I gave a talk at linux.conf.au on one way around this. Basically, remote attestation places no constraints on the network protocol in use - while the implementations that exist all do this over IP, there's no requirement for them to do so. So I wrote an implementation that runs over Bluetooth, in theory allowing you to use your phone to serve as the remote agent. If you trust your phone, you can use it as a tool for determining if you should trust your laptop.

I've pushed some code that demos this. The current implementation does nothing other than tell you whether UEFI Secure Boot was enabled or not, and it's also not currently running on a phone. The phone bit of this is pretty straightforward to fix, but the rest is somewhat harder.

The big issue we face is that we frequently don't know what event log values we should be seeing. The first few values are produced by the system firmware and there's no standardised way to publish the expected values. The Linux Vendor Firmware Service has support for publishing these values, so for some systems we can get hold of this. But then you get to measurements of your bootloader and kernel, and those change every time you do an update. Ideally we'd have tooling for Linux distributions to publish known good values for each package version and for that to be common across distributions. This would allow tools to download metadata and verify that measurements correspond to legitimate builds from the distribution in question.

This does still leave the problem of the initramfs. Since initramfs files are usually generated locally, and depend on the locally installed versions of tools at the point they're built, we end up with no good way to precalculate those values. I proposed a possible solution to this a while back, but have done absolutely nothing to help make that happen. I suck. The right way to do this may actually just be to turn initramfs images into pre-built artifacts and figure out the config at runtime (dracut actually supports a bunch of this already), so I'm going to spend a while playing with that.

If we can pull these pieces together then we can get to a place where you can boot your laptop and then, before typing any authentication details, have your phone compare each component in the boot process to expected values. Assistance in all of this extremely gratefully received.

comment count unavailable comments

January 18, 2020

Digitizing a analog water meter

For a University project a spent some time working on a project to digitally track the water consumption in my shared flat. Since nowadays everything is about data collection, I wanted to give this idea a shot. In my flat we have a simple analog water meter in my house.

Sadly, my meter is really dirt under the glass and i couldn’t manage to clean it. This will cause problems down the road.

The initial idea was easy, add a webcam on top of the meter and read the number on the upper half it. But I soon realized that the project won’t be that simple. The number shows only the use of 1m^3 (1000 liters), this means that I would have a change only every couple of days, which is useless and boring.  So, I had to read the analog gauges, which show the fraction in 0.0001, 0.001,  0.01 and 0.1 m^3. This discovery blocked me, and I was like “this is way to complicated”.

I have no idea how I found or what reminded me of OpenCV, but that was the solution. OpenCV is an awesome tool for computer vision, it has many features like Facial recognition, Gesture recognition … and also shape recognition. What’s a analog gauge? It’s just a circle with an triangular arrow indicating the value.

Let’s jump in to the project

I’m using a Raspberry Pi 1, a Logitech webcam, a juice bottle and some leds out of a bicycle light.

You need to find a juice bottle which fits nicely over the water meter. Cut of the top and bottom of the bottle and replace one side with a cardboard or wood with a hole in the middle. Attach the webcam centered over the hole and place a led on each side of the webcam to illuminate the water meter (you my need to cover them with paper to reduce reflection on the plastic of the meter).

First step is to set up the Raspberry Pi 1 (it doesn’t have to be a RPI, any computer running linux should work fine). You have to install a Linux Distro on the device, I used Archlinux. You can find a guide to install it on a Raspberry Pi 1 here.

After the initial setup you need to install git, python3 and opencv:
sudo pacman -S python3 git opencv
Clone the needed code to a know location:
git clone https://github.com/jsparber/water-meter-code.git
You need to create a new git repository to store the data and clone it to /home/alarm/water-meter-data/. If you want to use a different name or location you need to modify the name in measure.sh

On the RPI I have a cronjob which runs a script every minute. The script turns on the the led and then takes a picture then it turns the led off again, to save some energy.
With crontab -e you can modify the cron jobs, add * * * * * /home/alarm/code/take_photo.sh to run the take_photo.sh every minute, you may need to adjust the path depending on where you cloned the git repo.

After the picture is taken it calls a second script which then uses OpenCV to read the gauges and it appends the found values to a file which then is pushed to  git repo. I had a issue with the webcam. After some time my script couldn’t access the webcam anymore, I solved it by rebooting the RPI when it wasn’t possible to take a picture. (I did a quick search on the internet, most people solved this issue by changing the cam)

A nice optional feature is the home made switch connected to the RPI on the above picture. The schematics are really simple it’s only a 1KOhm resistor, a transistor and a USB extension cable. The transistor is switched on via the GPIO pin 18 of the Raspberry PI and gives power to the connected USB device. In this case I used it to connect the Leds.

Inside the USB extension cable there should be 4 different colored cables. We need to cut only the red one and connect it the same way as the schematics above show it, where the red_in goes to the male connector and the red_out to the female side of the cable. The GND needs to be connected to the ground pin of the Raspberry Pi, if you need to power something which requires more then 500mA you should connect the ground directly to the power source the same way as you did with the +5V red cable. You need to use the same power source for the switch and the RPI or it may not work.

And now the OpenCv part

First my code finds the circles of the right size on the image, and uses the two most left ones as gauges for 0.1 m^3 and 0.01 m^3 (Sadly since my meter is so dirty I can’t reliably read the other two values).

The input image.
The found circles of the right size

As the second step I create a mask which filters out everything what’s not red (remember the arrows are read). I take the contour of the mask which encloses the center of the circle I want to read. Then it finds the fairest point from the center of the circle which is the tip of the arrow. The software then creates a virtual line between the center and the tip, which is then used to calculate the angle which is basically the value shown on the gauge. The same thing is repeated for the other gauges.

The mask with only red areas showing.
The arrows found on the source image. This lines are used to calculate the angle.

This system sounds extremely simple, but to make everything work well together it isn’t that easy. OpenCV requires a lot of tuning, e.g selecting the right red color so that it detects it well but stays working even with light changes.

Conclusions

I learned a lot during this project, especially about OpenCV which i never used before. Sadly my water meter was really dirty so a couldn’t read all values and get also some wrong readings. So far I didn’t decide for what i want to use the collected data therefore I didn’t spend much time on finding a solution for read errors and problems I have when the gauges make a full turn. A easy solution would be to just keep an internal count of the water. And when we are unsure about a value we can go back to the memorized value.

The final plot can be found here. All values are saved directly without filter this causes the plot to have quite some noise but it allows to change the function used to filter later and adapt it to future needs.

My code is published on github:

Some sources which helped me a lot, many thanks to them: