It’s been a while since I’ve annoyed you by mentioning drones, but here we are with DJI’s latest creation—the Neo. DJI is a giant, soulless corporation, but they’ve made some clever design decisions with this drone. It’s a flying camera that works without a phone or remote, and for about half the price of a GoPro, you get a self-sufficient, button-operated flying cameraman. Take it out, push a button, and off it goes, capturing footage like it knows what it’s doing. It might also just simply be a response to the actually innovative HoverAir X1, just made extremely affordable.
The camera quality won’t blow you away, but it’s solid enough if you can overlook its overly-sharpened aesthetic. The Neo can even play the part of a lightweight cinewhoop or a poor man’s DJI Mini, though that’s only if you own DJI’s latest goggles and remotes—so there’s a bit of a catch. I bought it mainly for the fun shots you’re watching here, and while I’m not interested in keeping up with DJI’s endless upgrades, I must admit the little drone has charm and utility.
Neo’s Follow mode is a standout, tracking you accurately with just its camera feed and no fancy sensors. It tries to follow your path to avoid hitting anything. But beware the Direction Tracking mode in crowded areas—it has a habit of misjudging where you’re facing and wobbling about. The biggest hiccup I encountered? No gesture to make it return home, which left me standing under it after performing a Rocket shot, until the battery ran out. Luckily the pain lasted only about 10 minutes. Overall, it’s a great toy for hikes, casual outings, and occasional public embarrassment.
Soundtrack for the poorly edited video above comes from my 2024 weekly beats endeavors. Looks like I have enough material for a 2025 album. Stay tuned!
As it's now aproaching mid-September and the autumn release of GNOME, so is there also a new release of Maps approaching for GNOME 47.
Switch to Vector-based Map
The biggest change since last release is that we now use the vector-based map by default and the old raster map has also been retired since we wanted to move forward with things like enabling, and relying on clickable POIs directly in the map view so we could the remove the old tedious “What's here?” context menu doing a reverse geocoding to get details about a place (which is also a bit hit-and-miss with regards to how close to where you point the actual result is).
Apart from this other benefits we get (and this has already been mentioned in earlier posts) localized names (when tagged in OpenStreetMap) and finally a proper dark mode with our new GNOME map style.
Light (default) theme variant of the map in 47
Dark theme variant of the map in 47
Redesigned Search Bar
The “explore” button to open the search for nearby POIs by category menu has now been integrated into the entry to avoid a theme issue when using the linked button style where the rounded corners disappears on the “other side” when the results popover is showing.
Search bar with explore button
This also looks a bit sleeker I think…
Improved Public Transit Routing
Public transit routing is now using the Transitous project (https://transitous.org) to provide transit routing for new regions. And as this is a crowd sourcing initiativ you can also help out by adding additional missing GTFS feeds to the project and it should automatically get supported by Maps.
For the time being, the regions we already supported by third-party APIs (such as Resrobot for Sweden, and OpenData.ch for Switzerland) will still use those providers to avoid possible regressions. It also gives us some leeway to improve MOTIS (the backend used by Transitous). The implementation in Maps also lacks e.g. support for specifying via locations.
Showing some travel itinerary options in Prague
Showing a sample of an itinerary from Lund, Sweden to Hamburg, Germany
Showing a sample of an itinerary in Denver, Colorado
Some changes had to be made to our internal shield rendering library (we couldn't use the OSM Americana implementation directly, as we had to implement ours using Cairo rendering and so on) to support the new convenience shortcut for a “pill” shield shape, and also being able to define hard-coded route references “ref” directly in the shield definition rather than getting from the tile data.
Rochester Inner Loop in Rochester, New York, using a fixed “LOOP” reference label
And on that note, a funny bug I discovered during some testing is that we currently always assume the “pointsUp” attribute in the shield definitions would always default to true when not explicitly set, while the OSM Americana code has different defaults depending on the following shape. So specifically for the “fishhead” shape it should actually be false when not set. I guess this is a bit odd to assume different values depending on following JSON elements, but…
Highway shields in New Zeeland
It was a bit funny I discovered this bug when “browsing around“ in New Zeeland considering Northern Hemisphere-centric jokes about people “Down-under” walking upside-down 😀. But this actually also affects some highways in the US…
I guess this should be fixed before the 47.0 release.
Should your garbage collector be precise or conservative? The
prevailing wisdom is that precise is always better. Conservative GC can
retain more objects than strictly necessary, making GC slow: GC has to
more frequently, and it has to trace a larger heap on each collection.
However the calculus is not as straightforward as most people think, and
indeed there are some reasons to expect that conservative root-finding
can result in faster systems.
(I have made / relayed some of these arguments before but I feel like a
dedicated article can make a contribution here.)
problem precision
Let us assume that by conservative GC we mean conservative
root-finding, in which the collector assumes that any integer on the
stack that happens to be a heap address indicates a reference on the
object containing that address. The address doesn’t have to be at the
start of the object. Assume that objects on the heap are traced
precisely; contrast to BDW-GC which generally traces both the stack and
the heap conservatively. Assume a collector that will pin referents of
conservative roots, but in which objects not referred to by a
conservative root can be moved, as in Conservative
Immix or Whippet’s
stack-conservative-mmc
collector.
With that out of the way, let’s look at some reasons why conservative GC
might be faster than precise GC.
smaller lifetimes
A compiler that does precise root-finding will typically output a
side-table indicating which slots in a stack frame hold references to
heap objects. These lifetimes aren’t always precise, in the sense that
although they precisely enumerate heap references, those heap references
might actually not be used in the continuation of the stack frame. When
GC occurs, it might mark more objects as live than are actually live,
which is the imputed disadvantage of conservative collectors.
This is most obviously the case when you need to explicitly register
roots with some kind of handle API: the handle will typically be kept
live until the scope ends, but that might be an overapproximation of
lifetime. A compiler that can assume conservative stack scanning may
well exhibit more precision than it would if it needed to emit stack
maps.
no run-time overhead
For generated code, stack maps are great. But if a compiler needs to
call out to C++ or something, it needs to precisely track roots in a
run-time data
structure.
This is overhead, and conservative collectors avoid it.
smaller stack frames
A compiler may partition spill space on a stack into a part that
contains pointers to the heap and a part containing numbers or other
unboxed data. This may lead to larger stack sizes than if you could
just re-use a slot for two purposes, if the lifetimes don’t overlap. A
similar concern applies for compilers that partition registers.
no stack maps
The need to emit stack maps is annoying for a compiler and makes
binaries bigger. Of course it’s necessary for precise roots. But then
there is additional overhead when tracing the stack: for each frame on
the stack, you need to look up the stack map for the return
continuation, which takes time. It may be faster to just test if words
on the stack might be pointers to the heap.
unconstrained compiler
Having to make stack maps is a constraint imposed on the compiler.
Maybe if you don’t need them, the compiler could do a better job, or you
could use a different compiler entirely. A conservative compiler can sometimes have better codegen, for example by the use of interior pointers.
anecdotal evidence
The Conservative Immix
paper shows that conservative stack scanning can beat precise scanning
in some cases. I have reproduced these results with
parallel-stack-conservative-mmc compared to
parallel-mmc.
It’s small—maybe a percent—but it was a surprising result to me and I
thought others might want to know.
When it comes to designing a system with GC, don’t count out
conservative stack scanning; the tradeoffs don’t obviously go one way or the other, and conservative scanning might be the right engineering choice for your system.
Many years ago I read one of those Cliff Click “here’s
what I learned” articles in which he was giving advice about garbage
collector design, and one of the recommendations was that at a GC pause,
running mutator threads should cooperate with the collector by identifying roots from
their own stacks. You can read a similar assertion in their VEE2005
paper, The Pauseless GC
Algorithm,
though this wasn’t the source of the information.
One motivation for the idea was locality: a thread’s
stack is already local to a thread. Then specifically in the context of
a pauseless collector, you need to avoid races between the collector and
the mutator for a thread’s stack, and having the thread visit its own
stack neatly handles this problem.
However, I am not so interested any more in (so-called) pauseless collectors;
though I have not measured myself, I am convinced enough by the
arguments in the Distilling the real costs of production garbage
collectors
paper, which finds that state of the art pause-minimizing collectors
actually increase both average and p99 latency, relative to a
well-engineered collector with a pause phase. So, the racing argument
is not so relevant to me, because a pause is happening anyway.
There was one more argument that I thought was interesting, which was
that having threads visit their own stacks is a kind of cheap
parallelism: the mutator threads are already there, they might as well
do some work; it could be that it saves time, if other threads
haven’t seen the safepoint yet. Mutators exhibit a ragged stop, in
the sense that there is no clean cutoff time at which all mutators stop
simultaneously, only a time after which no more mutators are running.
Or so I thought! Let’s try to look at the problem analytically.
Consider that you have a system with N processors, a stop-the-world GC
with N tracing threads, and M mutator threads. Let’s assume that we
want to minimize GC latency, as defined by the time between GC is
triggered and the time that mutators resume. There will be one
triggering thread that causes GC to begin, and then M–1 remote
threads that need to reach a safepoint before the GC pause can begin.
The total amount of work that needs to be done during GC can be broken
down into rootsi, the time needed to visit roots for mutator
i, and then graph, the time to trace the transitive closure of live
objects. We want to know whether it’s better to perform rootsi during
the ragged stop or in the GC pause itself.
Let’s first look to the case where M is 1 (just one mutator thread).
If we visit roots before the pause, we have
latencyragged,M=1 =
roots0 +
graphN
Which is to say, thread 0 triggers GC,
visits its own roots, then enters the pause in which the whole graph is
traced by all workers with maximum parallelism. It may be that graph
tracing doesn’t fully parallelize, for example if the graph has a long
singly-linked list, but the parallelism with be maximal within the pause
as there are N workers tracing the graph.
If instead we visit roots within the pause, we have:
latencypause,M=1=
roots0+graphN
This is strictly better than the ragged-visit latency.
If we have two threads, then we will need to add in some delay,
corresponding to the time it takes for remote threads to reach a
safepoint. Let’s assume that there is a maximum period (in terms of
instructions) at which a mutator will check for
safepoints. In
that case the worst-case delay will be a constant, and we add it on to
the latency. Let us assume also there are more than two threads
available. The marking-roots-during-the-pause case it’s easiest to
analyze:
latencypause,M=2=
delay +
roots0+roots1+graphN
In this case, a ragged visit could win: while the triggering thread is
waiting for the remote thread to stop, it could perform
roots0, moving the work out of the pause, reducing pause
time, and reducing latency, for free.
latencyragged,M=2=
delay + roots1 +
graphN
However, we only have this win if the root-visiting time is smaller than
the safepoint delay; otherwise we are just prolonging the pause. Thing
is, you don’t know in general. If indeed the root-visiting time is
short, relative to the delay, we can assume the roots elements of our
equation are 0, and so the choice to mark during ragged stop doesn’t
matter at all! If we assume instead that root-visiting time is long,
then it is suboptimally parallelised: under-parallelised if we have more
than M cores, oversubscribed if M is greater than N, and
needlessly serializing before the pause while it waits for the last
mutator to finish marking its roots. What’s worse, root-visiting actually slows down delay, because the oversubscribed threads compete with visitation for CPU time.
So in summary, I plan to switch away from doing GC work during the ragged stop. It is complexity that doesn’t pay. Onwards and upwards!
Thank you to the translation teams for all the translation updates which happen in GNOME, particularly just before a release, like just now. It is really appreciated!
A wild new stable release of Cairo appeared! It’s super-effective at fixing build issues and bugs on a variety of platforms and toolchains. Thanks to Federico Mena, Cairo now generates static analysis and coverage reports as part of the CI pipeline; the coverage reports are published, so if you want to contribute to the Cairo project you can now find where your changes can be most effective.
There have been a lot of fixes and refactoring, notable changes:
Credential changes
For SSH repos ssh-agent will now be used by default. This makes it easier to use different keys with or without password protection. You can still configure a predefined ssh key in the settings.
It is now possible to use HTTPS repos with username and password. A userpass dialog will prompt for your credentials which can be stored in the gnome keyring. I was not able to test this feature myself, because I do not have access to a repository which allows HTTPS with password. But it seems to work.
File-manager plugin changes
A huge bottleneck, which caused nautilus to freeze up and worst case to crash has been fixed.
Unfortunately I found another issue with the update_file_info_full function, which I can reproduce on multiple distros. For the time being the turtle plugin uses update_file_info instead. This makes emblem calculation slower, but at least it runs stable again.
There is now a turtle emblem which will be shown for the repo main folder by default. The status emblem can be activated again in the settings. It is also possible to show both or none of them. This change further speeds up emblem calculation in folders with many (100+) repos, especially with the workaround mentioned above, and also makes submodules more visible.
After a long awaited couple of months, Parabolic has been rewritten in C++ and features a redesigned user interface! Users should expect a faster and more reliable downloader.
We encourage all Parabolic users to give this beta a try and iron out all issues before the stable release (targeted for next week).
Here’s the full changelog:
Parabolic has been rewritten in C++ for faster performance
The length of the kept download history can now be changed in the app’s preferences
Cookies can now be fetched from a selected browser in Preferences instead of selecting a TXT cookies file
Parabolic’s Keyring module was rewritten. As a result, all keyrings have been reset and will need to be reconfigured
Fixed validation issues with various sites
Fixed an issue where a specified video password was not being used
Is it hot in the Northern Hemisphere and cold in the Southern Hemisphere?
Weather O’Clock has been ported to GNOME Shell 47! With this new version, it is possible to customize the display of weather information, choosing whether it appears before or after the clock.
New updates for the Day Progress extension - circular indicators and GNOME 47!
Day Progress, the extension that lets you visualise how much time is left of your day has now been ported to support GNOME 47! There is now an (experimental) option to display the time elapsed/remaining as a circular (“pie”) indicator too. I haven’t run into any bugs with the new indicator style in my few days of testing but I’m still hesitant to call it stable, that’s why I have labelled it as ‘experimental’. You can download the extension at https://extensions.gnome.org/extension/7042/day-progress/ and the repository is available at https://github.com/ArcaEge/day-progress where you can also report any issues you find.
That’s all for this week!
See you next week, and be sure to stop by #thisweek:gnome.org with updates on your own projects!
The WebRTC nerds among us will remember the first thing we learn about WebRTC, which is that it is a specification for peer-to-peer communication of media and data, but it does not specify how signalling is done.
Or put more simply, if you want call someone on the web, WebRTC tells you how you can transfer audio, video and data, but it leaves out the bit about how you make the call itself: how do you locate the person you’re calling, let them know you’d like to call them, and a few following steps before you can see and talk to each other.
While this allows services to provide their own mechanisms to manage how WebRTC calls work, the lack of a standard mechanism means that general-purpose applications need to individually integrate each service that they want to support. For example, GStreamer’s webrtcsrc and webrtcsink elements support various signalling protocols, including Janus Video Rooms, LiveKit, and Amazon Kinesis Video Streams.
However, having a standard way for clients to do signalling would help developers focus on their application and worry less about interoperability with different services.
(author’s note: the puns really do write themselves :))
As the names suggest, the specifications provide a way to perform signalling using HTTP. WHIP gives us a way to send media to a server, to ingest into a WebRTC call or live stream, for example.
Conversely, WHEP gives us a way for a client to use HTTP signalling to consume a WebRTC stream – for example to create a simple web-based consumer of a WebRTC call, or tap into a live streaming pipeline.
With this view of the world, WHIP and WHEP can be used both for calling applications, but also as an alternative way to ingest or play back live streams, with lower latency and a near-ubiquitous real-time communication API.
We know GStreamer already provides developers two ways to work with WebRTC streams:
webrtcbin: provides a low-level API, akin to the PeerConnection API that browser-based users of WebRTC will be familiar with
webrtcsrc and webrtcsink: provide high-level elements that can respectively produce/consume media from/to a WebRTC endpoint
At Asymptotic, my colleagues Tarun and Sanchayan have been using these building blocks to implement GStreamer elements for both the WHIP and WHEP specifications. You can find these in the GStreamer Rust plugins repository.
Our initial implementations were based on webrtcbin, but have since been moved over to the higher-level APIs to reuse common functionality (such as automatic encoding/decoding and congestion control). Tarun covered our work in a talk at last year’s GStreamer Conference.
Today, we have 4 elements implementing WHIP and WHEP.
Clients
whipclientsink: This is a webrtcsink-based implementation of a WHIP client, using which you can send media to a WHIP server. For example, streaming your camera to a WHIP server is as simple as:
whepclientsrc: This is work in progress and allows us to build player applications to connect to a WHEP server and consume media from it. The goal is to make playing a WHEP stream as simple as:
The client elements fit quite neatly into how we might imagine GStreamer-based clients could work. You could stream arbitrary stored or live media to a WHIP server, and play back any media a WHEP server provides. Both pipelines implicitly benefit from GStreamer’s ability to use hardware-acceleration capabilities of the platform they are running on.
Servers
whipserversrc: Allows us to create a WHIP server to which clients can connect and provide media, each of which will be exposed as GStreamer pads that can be arbitrarily routed and combined as required. We have an example server that can
play all the streams being sent to it.
whepserversink: Finally we have ongoing work to publish arbitrary streams over WHEP for web-based clients to consume this media.
The two server elements open up a number of interesting possibilities. We can ingest arbitrary media with WHIP, and then decode and process, or forward it, depending on what the application requires. We expect that the server API will grow over time, based on the different kinds of use-cases we wish to support.
This is all pretty exciting, as we have all the pieces to create flexible pipelines for routing media between WebRTC-based endpoints without having to worry about service-specific signalling.
If you’re looking for help realising WHIP/WHEP based endpoints, or other media streaming pipelines, don’t hesitate to reach out to us!
They say you can’t forget how to ride a bicycle. Well I don’t think it applies to FPV racing. Haven’t touched the sticks in a year. But that does not make me not hang out with old buddies at the longest running event in Czechia.
GNOME is interested in participating in the Outreachy December-March cohort, and while we already have a few great projects, we are looking for experienced mentors with a couple more project ideas. Hurry up, we have until September 11 to conclude our list of ideas.
Maps now supports Transitous (https://transitous.org/ ), using a crowd-sourcing approach, backed by the MOTIS search engine for public transit routing (for the time being for areas that isn’t already supported by existing plugins).
GLib
The low-level core library that forms the basis for projects such as GTK and GNOME.
Thanks to work by Evan Welsh, GLib 2.83.0 (the next unstable release, due in a few months) will have support for sync/async/finish introspection annotations when built and run with a suitably new version of gobject-introspection, https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3746
Thanks to Luca Bacci for putting in time to improve debuggability of GLib on Windows CI machines, this should help with keeping GLib running on Windows and Windows-like platforms in the future (more help always needed if anyone is interested)
I’ve informally documented the GNOME Circle review procedure. Existing conventions are laid out and briefly explained, and the ways of which we keep member projects in check are formalized through the concepts of control reviews and group notices. My goal with this is to increase the transparency and consistency of how the Circle Committee operates.
If you’re already a member of GNOME Circle, a developer considering applying, or just a curious bystander, and have questions about this, don’t hesitate to reach out.
This week Tuba was accepted into GNOME Circle. Tuba lets you explore the federated social web. With its extensive support for popular Fediverse platforms like Mastodon, GoToSocial, Akkoma, and more, it makes it easy to stay connected to your favorite communities, family and friends. Congratulations!
Unix file permissions can be hard to understand, especially for new linux users, that is why I wrote Concessio, an app to help users understand and convert between said permissions. You can get Concessio from flathub
The new version of Planify 4.11.0 is here, packed with features and improvements that will make your task management even more efficient and personalized!
What’s New in Planify 4.11.0
1. Collapsible Sections:
You can now collapse sections for a cleaner and more focused view of your priority tasks.
2. Task Counter per Section:
Easily see the number of tasks in each section with the new task counter.
3. Pinned Tasks at the Top:
Pinned tasks are now displayed at the top of each project, keeping them always in sight.
4. Overdue Tasks Indicator:
The “Today” button now shows an indicator for overdue tasks, helping you stay on top of what’s important.
5. New Quick Add Keyboard Shortcuts:
We’ve added new shortcuts to speed up your workflow:
- @ to add tags.
- # to select a project.
- ! to create a reminder.
6. Completed Tasks View:
Check your completed tasks sorted by date and filter them by sections as needed.
7. Task History Log:
Access the new task history log, where you can see when a task was completed or moved between projects or sections.
8. Improved Drag and Drop:
We’ve fixed several bugs with drag and drop, ensuring a smoother experience.
9. Quick Find Task Detail:
Now, when selecting a task in Quick Find, the task detail will automatically open.
10. Markdown Preference:
You can now enable or disable the use of Markdown in task details, based on your preference.
11. Improved Keyboard Navigation:
Keyboard navigation has been optimized to make your experience faster and easier.
12. Create Sections in Quick Add:
If a section doesn’t exist, you can now create it directly from Quick Add.
13. Attachment Number Indicator:
We’ve added a numerical indicator to show the number of attachments on each task.
14. Preference to Keep Task Detail Pane Open:
We’ve added a preference that allows you to keep the task detail pane always open, making navigation faster.
15. Quicker Recurring Tasks:
The option to repeat a task has been moved to the date and time selection widget, making the process of creating recurring tasks faster.
16. Improved Nextcloud Synchronization:
We fixed several bugs related to task synchronization with Nextcloud.
17. Design and Optimization Improvements:
Planify is now faster and consumes fewer resources, thanks to various design and optimization improvements.
A Heartfelt Thank You
We would like to extend our deepest gratitude to everyone who supports the development of Planify by contributing financially. Your generosity helps us continue to improve and expand Planify, making it a better tool for everyone. Thank you for being a vital part of our journey!
Update now and enjoy an even more powerful and tailored task management experience. We’re continuously working to bring you the best in every release!
After a year, there’s a new stable release of JSON-GLib, the JSON parsing and generation library that tries to be well-integrated with GLib data types and best practices. Version 1.10.0 introduces a new, extensive conformance test suite to ensure that the parsing code is capable of handling both valid and invalid JSON. Additionally, JsonParser has a new “strict” option, for when you need to ensure that the JSON data is strictly conforming to the format specification. The parsing code has been simplified and improved, and passes both static analysis and sanitisers. Finally, there are lots of documentation fixes across the API and manual pages of the command line utilities.
Blueprint
A markup language for app developers to create GTK user interfaces.
GNOME Latam 2024 - Universidad de Antioquia, October 25th and 26th. Medellín, Colombia. A day to celebrate and expand the GNOME community in Latin America. Come and share experiences in the creation and use of GNOME technologies in our region.
What is GNOME Latam 2024? GNOME Latam 2024 is more than just an event; it’s a movement that brings together developers, users, educators, and free software enthusiasts to share knowledge, experiences, and collaboratively build a more inclusive technological future. Through a series of in-person and online activities, we aim to spread the latest updates within the GNOME community, encourage active participation, and create a space where everyone can contribute, regardless of their experience level or prior knowledge.
Our mission is clear: to drive the adoption and development of the GNOME environment in Latin America, promoting the values of free software such as freedom, collaboration, and transparency. We believe that access to technology should be a universal right and that everyone, from experts to beginners, has something valuable to contribute.
Note: The talks will be conducted both in-person and online.
As of today, Mutter will style legacy titlebars (i.e. of X11 / Xwayland apps that don’t use client-side decorations) using Adwaita on GNOME.
Shadows match the Adwaita style as well, including shadows of unfocused windows. These titlebars continue to follow the system dark and light mode, even when apps don’t.
Should make using legacy apps a little less unpleasant
We have finally reached the final week of GSoC. It has been an amazing journey! Let’s summarize what was done, the current state of the project and what’s next.
Introduction
This summer, I had the opportunity to work as a student developer under the Google Summer of Code 2024 program with the GNOME Community. I focused on creating a web-based Integrated Development Environment (IDE) specifically designed for writing and executing SPARQL queries within TinySPARQL (formerly Tracker).
This user-friendly interface empowers developers by allowing them to compose and edit multiline SPARQL queries directly in a code editor, eliminating the need for the traditional terminal approach. Once a query is written, it can be easily executed via the HTTP SPARQL endpoint, and the results will be displayed in a visually appealing format, enhancing readability and user experience.
By lowering the barrier to entry for newcomers, boosting developer productivity with visual editing, and fostering collaboration through easier query sharing, this web IDE aims to significantly improve the experience for those using libtracker-sparql to interact with RDF databases.
I would like to express my sincere gratitude to my mentors, Carlos and Sam, for their guidance and support throughout the internship period. Their expertise was invaluable in helping me navigate the project and gain a deeper understanding of the subject matter. I would also like to thank my co-mentee Rachel, for her excellent collaboration and contributions to making this project a reality and fostering a fast-paced development environment.
I’m excited to announce that as the internship concludes, we have a functional web IDE that enables users to run SPARQL queries and view the results directly in their web browser. Here is the working demo of the web IDE that was developed from scratch in this GSoC Project.
What was done
This project was divided into two primary components: the backend C code, which enabled the web IDE to be served and run from the command line, and the frontend JavaScript code, which enhanced the web IDE’s visual appeal and added all user-facing functionalities. I primarily focused on the backend C side of the project, while Rachel worked on the frontend. Therefore, this blog post will delve into the backend aspects of the project. To learn more about the frontend development, please check out Rachel’s blog.
The work done by me, could be divided into three major phases:
Pre-Development Phase
During the pre-development phase, I focused on familiarizing myself with the existing codebase and preparing it for easier development. This involved removing support for older versions of libraries, such as Libsoup.
TinySPARQL previously supported both Libsoup 2 and Libsoup 3 libraries, but these versions had different function names and macros.
This compatibility requirement could significantly impact development time. To streamline the process, we decided to drop support for Libsoup 2.
The following merge requests document the work done in this phase:
In this phase, I extended the HTTP endpoint exposed by the tinysparql endpoint command to also serve the web IDE. The goal was to enable the endpoint to serve HTML, CSS, and JavaScript files, in addition to RDF data. This was a crucial step, as frontend development could only begin once the basic web IDE was ready.
During this phase, the HTTP module became more complex. To aid in debugging and diagnosing errors, we added a debugging functionality. By running TRACKER_DEBUG=http, one can now view logs of all GET and POST methods, providing valuable insights into the HTTP module’s behavior.
The following merge requests document the work done in this phase:
The web IDE added significant size (around 800KB-1MB) to the libtracker-sparql library. Since not all users might need the web IDE functionality, we decided to separate it from libtracker-sparql. This separation improves efficiency for users who won’t be using the web IDE.
To achieve this isolation, we implemented a dedicated subcommand tinysparql webide for the web IDE, allowing it to run independently from the SPARQL endpoint.
Here’s a breakdown of the process:
Isolating HTTP Code: I started by extracting the HTTP code from libtracker-sparql into a new static library named libtracker-http. This library contains the abstraction TrackerHttpServer over the Libsoup server, which can be reused in the tinysparql webide subcommand.
Creating a Subcommand: Following the isolation of the web IDE into its own library and the removal of relevant gresources from libtracker-sparql, we were finally able to create a dedicated subcommand for the web IDE. As a result, the size of libtinysparql.so.0.800.0 has been reduced by approximately 700KB.”
The following merge requests document the work done in this phase:
This is the web IDE we developed during the internship. Check out this demo video to see some of the latest changes in action.
Future Work
Despite having a functional web IDE and completing many of the tasks outlined in the proposal (even exceeding the original scope due to the collaborative efforts of two developers), there are still areas for improvement.
I plan to continue working on the web IDE in the future, focusing on the following enhancements:
Multi-Endpoint Support: Implement a mechanism for querying different SPARQL endpoints. This could involve adding a text box input to the frontend for dynamically entering endpoint URLs or providing a connection string option when creating the web IDE instance from the command line.
Unified HTTP Handling: Implement a consistent HTTP handler for all cases, allowing TrackerEndpointHttp to handle requests both inside and outside the /sparql path.
SPARQL Extraction: Extract the SPARQL query from POST requests in TrackerEndpointHttp or pass the raw POST data in the ::request signal, enabling TrackerEndpointHttp to determine if it contains a SPARQL query.
Avahi Configuration: Move the Avahi code for announcing server availability or assign TrackerEndpointHttp responsibility for managing the content and type of broadcasted data.
CORS Configuration: Make CORS settings configurable at the API level, allowing for more granular control and avoiding the default enforcement of the * wildcard.
GUADEC Experience
One of the highlights of my GSoC journey was the opportunity to present my project at GUADEC, the annual GNOME conference. It was an incredible experience to share my work with a diverse audience of developers and enthusiasts. Be sure to check out our presentation on the TinySPARQL Web IDE, delivered by Rachel and me at GUADEC.
Final Remarks
Thank you for taking the time to read this. Your support means a great deal to me. This internship was a valuable learning experience, as it was my first exposure to professional-level C code and working with numerous libraries solely based on official documentation. I am now more confident in my skills than ever. I gained a deeper understanding of the benefits of collaboration and how it can significantly accelerate development while maintaining high code quality.
Add support for the latest GIR attributes and GI-Docgen formatting to Valadoc.
Overview
GSoC 2024 has come to an end, so it's time to wrap up. I got the opportunity to contribute to the Vala Project which consists of an awesome programming language called Vala, and it gives me immense sense of accomplishment to know that my work will be beneficial for Vala programmers. I spent the 12 weeks working through the codebase of the Vala compiler, adding features and making the necessary changes to achieve the project goals. It was a valuable experience and I have learnt a lot by working with talented mentors and peers. This has undoubtedly shaped my journey as a developer and I plan to continue working on the project.
Project Summary
This project aimed to add support for the latest features of GObject Introspection to the Vala compiler and Valadoc. The plan was to ensure that the Vala compiler (which generates Vala bindings for the GIR files) parses and utilizes the newer GIR attributes from the introspection data of GObject based C libraries, and outputs them in the generation of Vala GIRs. In Valadoc, this was to be implemented by parsing the GI-Docgen documentation format, and rendering working GI-Docgen links in the HTML documentation generated by Valadoc. Another important step in improving Valadoc was to redesign https://valadoc.org and give it a modernized look, making this one of the milestones that was expected to be acheived in the project.
Contributions (Merge requests)
To acheive these objectives, I opened the following merge requests:
Support for sync-func, async-func, and finish-func attributes for method. (Draft) [!393]
Add support for default-value attribute for property. [394]
libvaladoc: Parse backticks in gi-docgen markdown and display the enclosed text as monospaced. [!402]
libvaladoc: Modernize the HTML documentation pages generated by valadoc. [!403]
Redesign https://valadoc.org and make it mobile-responsive. [#419]
Future Plans
Although the coding period of GSoC 2024 is now over, I feel that this is just the beginning of my contributions to GNOME. We still have to implement support for working GI-Docgen links and many other features of GI-Docgen markdown to Valadoc. I will continue working to meet the project objectives, contribute more, and be more involved within the GNOME community. I got to learn a lot over the past 12 weeks and this has certainly made me a better contributor.
Mentor
I extend my heartfelt gratitude to my mentor Lorenz Wildberg for being a constant source of support and motivation throughout the internship. Their expertise and guidance helped me reach this far into the project and I hope to continue working on Vala and other GNOME projects in the coming months.
GUADEC 2024
I got the opportunity to present my project and participate in the Intern Lightning Talks in GUADEC on 20 July 2024. I had a great experience explaining about my project and answering questions, you can watch my presentation here: https://youtu.be/chKVTgUUVpk?si=LE46ezQX6q3ZqcZ4&t=2220
After GSoC
As I look back and reflect on my journey over the last 12 weeks, I am filled with gratitude for this opportunity and excitement for future work on Vala and related GNOME projects. I want to learn more about GTK, Vala and the GNOME development process so that I can make more impactful contributions and be a valuable member of the community. I had many interactions with numerous GNOME contributors and I'm grateful to each and every one of them for always being ready to guide and for their prompt replies to my doubts. I was a Linux user for a long time but never really used it as a power user until I started contributing to GNOME. I'm glad to say that now Linux will always be my preferred choice of an operating system :). My favourite part of working on this project was being part of a community that is diverse, inclusive, and incredibly welcoming to newcomers. I look forward to being a better GNOME contributor and guiding new contributors in GNOME.
TLDR: GSoC is ending soon and I’ve definitely learned a lot from my time here, if you’re interested in the code I’ve written for my GSoC project feel free to go straight to the end where I’ve linked all the MRs I’ve been involved
Hello GNOME community! Time flies and my time with GSoC working on a new Web-IDE for TinySPARQL is coming to a close. You might have seen my intro post about the project, or the lightning talk my colleague Demigod and I recorded together for GUADEC. In any case, I’m excited to show you guys our final product and talk about the next steps, both in terms of this project and my involvement with open source.
First of all, to reiterate the purpose of this project – we’ve been working the last few months to create a web-IDE to be used with TinySPARQL and LocalSearch for query testing in a more user-friendly environment, our main target audience being fellow developers that for any reasons need to interact with LocalSearch or TinySPARQL databases.
My main work during the last few months involved developing a lightweight TypeScript based UI while my colleague Demigod worked mostly on implementing the backend support necessary.
The first big hurdle of the project for me was figuring out how to include the TS code and necessary npm packages to the TinySPARQL codebase without creating bloat for what is supposed to be a very lightweight and low-level package. We ended up using webpack for bundling and then further compressing them into GResources such that only these GResources need to be included in our releases, to be served when a user starts up the web-ide. This quickly addressed my mentors’ concern about having to include npm packages in our releases and ensured we could work comfortably between the TS code and C backend without any troubles.
In terms of actual design and UX work, this went by relatively smoothly, though it did take quite a few feedback cycles to get it to its current state. Click here for a quick demo of the final product.
Just to go over some of the features that have been fully implemented on the web ide frontend:
code editor with full support for SPARQL syntax highlighting and common keyboard shortcuts
error highlighting at corresponding editor positions according to error messages returned by backend
query “bookmarking” via conversion to links
Neat table format for presenting query results, equipped with ontology prefix adaptations and directin linking to relevant documentations
Options to hide/show columns in result tables
Clear error reporting
And here are some features still in progress/waiting to be merged:
Options to query in other rdf formats: trig, turtle and JSON-LD
Examples box for referencing queries that may be useful for certain endpoints
Quick switching between different SPARQL endpoints from the Web IDE interface itself
In terms of future work, some more work needs to be done on our colour scheme as well as the presentation of query results in the other formats offered other than the default cursor format. There were also some discussions in earlier stages of planning about implementing autocomplete and other editor enhancements that we didn’t have enough time for, so there’s still definitely lots of room for improvement. Nonetheless I’m very satisfied and proud of what I’ve achieved in the past few months and will be looking forward to contributing to future improvements of this tool.
Regarding the overall learning experience, one of the most important thing I learned, in my opinoion, was how to keep my git history clean and work with multiple branches of code at the same time without creating conflicts. I feel like this would completely change the experience of whoever I work with in the future as well as myself when I need to go back to some old code. Other than that, I’ve also had little exposure outside web development and working with the GNOME ecosystem was definitely a nice challenge – I’m definitely a lot more confident about dabbling outside my area of expertise now.
Lastly, here’s a list of useful links to the work I’ve been doing over the summer. Thanks for reading this far
As my Outreachy internship comes to a close, I find myself reflecting on the journey with a sense of gratitude. What began with a mix of excitement and fear has turned into a rewarding experience that has shaped my skills, confidence, and passion for open-source contributions.
Overcoming Initial Fears
When I first started, I had some doubts and fears — among them was whether I would fit into the open-source community, I worried that my skills might not translate well to user research, an area I was eager to explore but had limited experience in. However, those fears quickly disappeared as I got myself into the supportive and inclusive GNOME community. I learned that the community values diverse contributions and that there is always room for growth and learning.
Highlights of the Internship
This internship has been a significant period of growth for me. I’ve developed a stronger understanding of user research methodologies, particularly the importance of crafting neutral questions to avoid bias. This was a concept I encountered early in the internship, and it has since become a cornerstone of my research approach. Additionally, I’ve sharpened my ability to analyze and interpret user feedback, which will be invaluable as I continue to pursue UI/UX design.
Beyond technical skills, I’ve also grown in terms of communication. Learning how to ask the right questions, listen actively, and engage with feedback constructively has been crucial. These skills have given me the confidence to interact more effectively within the open-source community.
Mentorship and Project Achievements
My mentors, Allan Day and Aryan Kaushik played a critical role in my development throughout this internship. Their guidance, patience, and willingness to share their expertise made a great difference. They encouraged me to think critically about every aspect of the user research process, helping me to grow not just as a researcher, but as a contributor to the open-source community.
As for my project, I’m proud of the progress I’ve made. I successfully conducted a series of user research exercises and gathered insights that will help improve the usability of some GNOME Apps. However, my work isn’t finished yet — I’m currently in the process of finalizing the usability research report. This report will be a little resource for the GNOME design team, providing detailed findings and recommendations that will guide future improvements.
Throughout this journey, I’ve leaned heavily on the core values I outlined at the start of the internship: Adventure, Contribution, and Optimism. These values have been my compass, guiding me through challenges and reminding me of the importance of giving back to the community. The adventure of stepping into a new field, the joy of making meaningful contributions, and the optimism that every challenge is an opportunity for growth — these principles have been central to my experience.
Reflecting on My Core Values
As I wrap up my time with Outreachy, I feel both proud of what I’ve learned and excited for what lies ahead. I plan to continue my involvement in open-source projects. The skills and confidence I’ve gained during this internship will undoubtedly serve me well in future projects. Additionally, inspired by the mentorship I received, I hope to help mentor others and help them navigate their journeys in open-source contributions.
Finally, this internship has been a transformative experience that has expanded my skill set, deepened my passion for user-focused design, and strengthened my commitment to open-source work. I’m grateful for the opportunity and look forward to staying connected with the GNOME community as I continue to grow and contribute.
Hey everybody, this is another iteration of my previous posts. It’s been a while since I published any updates about my project.
Before I begin with the updates I’d like to thank all of the people who helped me get this far into the project, it wouldn’t have been as engaging and enjoyable of a ride without your support.
For someone reading this blog for the first time, I am Bharat Tyagi. I am a Computer Science major and I have been contributing to the GNOME Project (Workbench in particular) under Google Summer of Code this year.
Since the updates until Week 3 have already been written in greater detail I will only briefly cover them in this report and focus more on the more recent ones.
Project Title
My project is subdivided into three parts:
Port existing demos to Vala
Redesign the Workbench Library and make QoL improvements
Add code search into Workbench
Mentors
Sonny Piers, Andy Holmes
Part 1:
Workbench has a vast library of demos covering every use case for developers or users who would like to learn more about the GTK ecosystem and how each component is connected and works together in unison.
The demos are available in many programming languages including JavaScript, Python, Vala, Rust, and now TypeScript (Thanks to Vixalien for bringing this to Workbench :) ). The first part of my project was to port around 30 demos into Vala. This required me to learn many functions, signals, and how widgets use them to relay information. Since I ported over 30 demos, I’ll mention a few that were fun to port and the list of all the ports that I made, if you’d like a more in-depth review of the process, the Week 3 update is where you should go!
Maps and CSS gradients don’t just look cool, their code was also fun to port. Support for maps is provided by libshumate, sourcing the world view from OSM (Open Street Map), and supports functions like dragging across the map showing the location for latitudes and longitudes entered, and allowing you to put markers at any point on the map.
CSS Gradients allows you to create custom gradients and generate their CSS as the parameters are generated
Session Monitor and Inhibit was another interesting demo to port, as the name goes it allows you to monitor changes and inhibit the desktop from changing state, based on the current state of your application.
You could use the demo for some interesting warnings
After all the ports were done, I moved to make changes to the library
Part 2:
The second part of this project was to redesign the library and bring about quality-of-life improvements.
Sonny prepared a roadmap, including some changes that had already been made, to help break the project down into actionable targets.
Since we wanted to include some filtering based on both language and category, the first step was to move away from the current implementation of the demo rows based on Adw.PreferencesWindow and related widgets, which are easy to use but don’t provide the necessary flexibility.
So I removed their usage with something more universal and that would allow us to reimplement populating the demos. Adw.PreferenceWindow was replaced with Adw.Window,Adw.PreferencesRowwithAdw.ActionRow, and Adw.PreferencesGroup and Page were replaced with simpler Gtk.ScrolledWindow with nested Gtk.Boxand Label
This is how the library looked after these changes
With these out of the way, we could work on making the current search more prominent. Since the search bar was activated by using the search button on the top left of the Library, there were a few people who were unaware of a search being present at all. To resolve this I shifted the search bar inside the library making it directly accessible and quicker to search.
The subsequent code also needed new logic so only the searched demos were visible. I used hash maps to store the currently visible categories and widgets depending on the search term.
//set the widget to be visible if it exists in the map category_map.forEach((category_widget, category_name) => { category_widget.visible = visible_categories.has(category_name); });
Getting the search to function as expected was relieving as it took a few iterations and changes to polish it enough to merge them. I am happy to report the search works just as expected now
With these minor improvements, we were ready to add filtering to the demos based on the language and categories.
The logic for filtering was inspired by Sonny’s previous approach towards adding this feature (here if you want to check it out). We have two dropdowns one for the category and one for languages. The filtering was done based on the input provided in all of the three widgets (Search, Language dropdown, and Category dropdown), if and only if the search term matches all three of these, the result would be displayed.
//filtering logic const is_match = category_match && language_match && (search_term === "" || search_match); //set visibility if the term matches all three entry_row.visible = is_match; if (is_match) { results_found = true; visible_categories.add(category_check[category_index]); //also add it to the visible categories map }
This was super close to how we wanted the filtering to work. Here is the final result :D
These are the commits for this part of the project for anyone curious
Having completed the filtering for our Library, now comes in the third part of my project which was to implement code search. Since we have a bunch of demos, storing and accessing search terms efficiently is a challenge. Sonny, Angelo, and I had a meeting to discuss code search which would then build up the starting point for the feature
Andy and I looked at a few options that could be used to implement this feature, majorly focusing on tools that provide working with large amounts of data. TinySPARQL is one such engine, but it is more preferred for files and directories which is not our goal. We need an API that can interact with the sqlite database and run text searches on it.
There are two major libraries under GNOME, libgomand libgda. libgom is an object-relational mapping library, which allows you to map database tables to GObjects and then run operations on those objects. This is in hindsight simpler than libgda, but it doesn't directly provide text-search functionalities on its own like libgda does.
As of writing this article, I have ported a demo example that makes use of libgom and performs a simple text/ID-based search on a single table. This can be scaled to bigger databases like our Library itself, but it starts showing limitations when it comes to more search functions.
Here is a screengrab of the demo, ported into Modern Gjs (GNOME JavaScript) :)
Now that we’ve seen the demo, let's have a look at the libgom magic that is happening in the background
First, we create a custom class that represents an object with properties id and url that we want to store in our table
We then, initialize the database using Gom.Adapter which also opens an SQLite database (for the simplicity of the demo, we’re only storing the contents in memory). A table is set up and mapped to the ItemClass that we previously created. The id field is set as the Primary key.
Once all the preliminary setup is done, I added the logic for basic text searching using a handy filter function in Gom
I use this to filter out the elements, store them in filtered_items, and display them in the table itself, voila
The PR is approved but yet to be merged, it will take some time before it reaches your Workbench. But if you would like to tinker around and make improvements to it, this is the PR
The plan right now is to implement search into the Library first using libgom and then later moving to libgda which is more versatile and provides full-text search functionalities using SQL queries without having to route them through GObjects.
Acknowledgments and Learnings
I am very thankful for the insights and guidance of my mentors, Andy and Sonny. They were quick to jump in whenever I encountered a blocker. They are awesome people with a strong passion for what they do, it’s been an honor to be able to contribute however little I was able to. I strive to be at their level someday.
This summer has been fruitful and fun for me. The most important thing I learned is to be curious and always ask questions.
A big thank you to Diego and Lorenz for reviewing all of the ports and providing much necessary improvements!
For the readers, I am pleasantly surprised that you were able to reach the end without scrolling away, thank you so much for tuning in and taking out time to read through. I hope this was just as fun for you to read as it was for me to write it :D
I’ll continue to stay in touch with everyone I have met and talked to during these few months because they are simply awesome!
I am pleased to announce a new development release of Cambalache, version 0.91.3, getting us one step closer to the stable release for GNOME 47.
What’s new:
Support 3rd party libraries
Improved Drag&Drop support
Streamline headerbar
Ported treeview to column view
Several bug fixes
3rd party libraries
Cambalache now loads 3rd party catalogs from Load catalogs from GLib.get_system_data_dirs()/cambalache/catalogs and ~/.cambalache/catalogs
These catalog files are generated from Gir data with tools/cambalache-db.py script. Currently this is an internal tool which means it needs some work and documentation to make it easier to use but you can see an example of how its used internally here
So what is a catalog anyway?
A catalog is a XML file with all the necessary data for cambalache to produce UI files with widgets from a particular library, this includes the different GTypes, with their properties, signals and everything else except the actual object implementations.
Runtime objects are created in the workspace by loading the GI namespace specified in the catalog.
So please, feel free to contact me on matrix if you are interested in adding support for a 3rd party library.
Improved Drag&Drop support
After the extensive rework done porting the main widget hierarchy from GtkTreeView to GtkColumnView and implementing several GListModel interfaces to avoid maintaining multiple lists I was able to reimplement and extend Drag&Drop code so now its possible to drop widgets in different parents.
Long version: When UEFI Secure Boot was specified, everyone involved was, well, a touch naive. The basic security model of Secure Boot is that all the code that ends up running in a kernel-level privileged environment should be validated before execution - the firmware verifies the bootloader, the bootloader verifies the kernel, the kernel verifies any additional runtime loaded kernel code, and now we have a trusted environment to impose any other security policy we want. Obviously people might screw up, but the spec included a way to revoke any signed components that turned out not to be trustworthy: simply add the hash of the untrustworthy code to a variable, and then refuse to load anything with that hash even if it's signed with a trusted key.
Unfortunately, as it turns out, scale. Every Linux distribution that works in the Secure Boot ecosystem generates their own bootloader binaries, and each of them has a different hash. If there's a vulnerability identified in the source code for said bootloader, there's a large number of different binaries that need to be revoked. And, well, the storage available to store the variable containing all these hashes is limited. There's simply not enough space to add a new set of hashes every time it turns out that grub (a bootloader initially written for a simpler time when there was no boot security and which has several separate image parsers and also a font parser and look you know where this is going) has another mechanism for a hostile actor to cause it to execute arbitrary code, so another solution was needed.
And that solution is SBAT. The general concept behind SBAT is pretty straightforward. Every important component in the boot chain declares a security generation that's incorporated into the signed binary. When a vulnerability is identified and fixed, that generation is incremented. An update can then be pushed that defines a minimum generation - boot components will look at the next item in the chain, compare its name and generation number to the ones stored in a firmware variable, and decide whether or not to execute it based on that. Instead of having to revoke a large number of individual hashes, it becomes possible to push one update that simply says "Any version of grub with a security generation below this number is considered untrustworthy".
So why is this suddenly relevant? SBAT was developed collaboratively between the Linux community and Microsoft, and Microsoft chose to push a Windows update that told systems not to trust versions of grub with a security generation below a certain level. This was because those versions of grub had genuine security vulnerabilities that would allow an attacker to compromise the Windows secure boot chain, and we've seen real world examples of malware wanting to do that (Black Lotus did so using a vulnerability in the Windows bootloader, but a vulnerability in grub would be just as viable for this). Viewed purely from a security perspective, this was a legitimate thing to want to do.
(An aside: the "Something has gone seriously wrong" message that's associated with people having a bad time as a result of this update? That's a message from shim, not any Microsoft code. Shim pays attention to SBAT updates in order to avoid violating the security assumptions made by other bootloaders on the system, so even though it was Microsoft that pushed the SBAT update, it's the Linux bootloader that refuses to run old versions of grub as a result. This is absolutely working as intended)
The problem we've ended up in is that several Linux distributions had not shipped versions of grub with a newer security generation, and so those versions of grub are assumed to be insecure (it's worth noting that grub is signed by individual distributions, not Microsoft, so there's no externally introduced lag here). Microsoft's stated intention was that Windows Update would only apply the SBAT update to systems that were Windows-only, and any dual-boot setups would instead be left vulnerable to attack until the installed distro updated its grub and shipped an SBAT update itself. Unfortunately, as is now obvious, that didn't work as intended and at least some dual-boot setups applied the update and that distribution's Shim refused to boot that distribution's grub.
What's the summary? Microsoft (understandably) didn't want it to be possible to attack Windows by using a vulnerable version of grub that could be tricked into executing arbitrary code and then introduce a bootkit into the Windows kernel during boot. Microsoft did this by pushing a Windows Update that updated the SBAT variable to indicate that known-vulnerable versions of grub shouldn't be allowed to boot on those systems. The distribution-provided Shim first-stage bootloader read this variable, read the SBAT section from the installed copy of grub, realised these conflicted, and refused to boot grub with the "Something has gone seriously wrong" message. This update was not supposed to apply to dual-boot systems, but did anyway. Basically:
1) Microsoft applied an update to systems where that update shouldn't have been applied 2) Some Linux distros failed to update their grub code and SBAT security generation when exploitable security vulnerabilities were identified in grub
The outcome is that some people can't boot their systems. I think there's plenty of blame here. Microsoft should have done more testing to ensure that dual-boot setups could be identified accurately. But also distributions shipping signed bootloaders should make sure that they're updating those and updating the security generation to match, because otherwise they're shipping a vector that can be used to attack other operating systems and that's kind of a violation of the social contract around all of this.
It's unfortunate that the victims here are largely end users faced with a system that suddenly refuses to boot the OS they want to boot. That should never happen. I don't think asking arbitrary end users whether they want secure boot updates is likely to result in good outcomes, and while I vaguely tend towards UEFI Secure Boot not being something that benefits most end users it's also a thing you really don't want to discover you want after the fact so I have sympathy for it being default on, so I do sympathise with Microsoft's choices here, other than the failed attempt to avoid the update on dual boot systems.
Anyway. I was extremely involved in the implementation of this for Linux back in 2012 and wrote the first prototype of Shim (which is now a massively better bootloader maintained by a wider set of people and that I haven't touched in years), so if you want to blame an individual please do feel free to blame me. This is something that shouldn't have happened, and unless you're either Microsoft or a Linux distribution it's not your fault. I'm sorry.
This blog post summarizes the discussions and action items from the Infrastructure and Release Engineering workshop held at Flock 2024 in Rochester, New York, USA.
This post is also an experiment in using AI generated summaries to provide useful, at-a-glance summaries of key Fedora topics. Parts of this content may display inaccurate info, including about people, so double-check with the source material.
Standards for OpenShift app deployments: There’s a need for consistency in deploying applications to OpenShift. The group discussed creating best practices documentation and addressing deployment methods across various applications.
Infra SIG packages: The workshop reviewed the “infra-sig” package group and identified a need to:
Find owners for orphaned packages.
Onboard new maintainers using Packit.
Remove inactive members from the group.
Release engineering packages: The group agreed to add a list of release engineering packages to the infra-sig for better management.
Proxy network: Discussion about potentially migrating the proxy network from httpd to nginx or gunicorn remained inconclusive. Further discussion is needed.
AWS management with Ansible: The feasibility of managing AWS infrastructure with Ansible is uncertain due to limitations with the main Amazon account.
Onboarding improvements: The group discussed ways to improve the onboarding process for new contributors, including documentation updates, marketing efforts, and “Hello” days after each release.
OpenShift apps deployment info: A tutorial on deploying applications to OpenShift was presented and will be incorporated into the documentation.
Future considerations: The group discussed upcoming challenges like GitLab Forge migration, Bugzilla migration, and a new Matrix server.
Retiring wiki pages: The group needs to decide where to migrate user-facing documentation from the wiki. Additionally, someone needs to review and archive/migrate/delete existing wiki pages in the “Category:Infrastructure” section.
Datagrepper access for CommOps: A solution was proposed to provide CommOps with access to community metrics data by setting up a separate database in AWS RDS and populating it with recent Datagrepper dumps.
ARA in infrastructure: While AWX deployment offers similar reporting features, setting up ARA remains an option if someone has the time and interest.
AWX deployment: Roadblocks related to the public/private Ansible repository structure were identified. A proof of concept using AWX will be pursued to determine if repository restructuring is necessary.
Zabbix integration: The group discussed moving forward with Zabbix to replace Nagios. Action items include setting up a bot channel for alerts, adjusting alerts based on comparison with Nagios, and considering an upgrade to the next LTS version.
Action Items
Create comments in each application playbook explaining its deployment method.
Move all apps using deploymentconfig to deployment with OpenShift 4.16.
Look into deploying Advanced Cluster Security (ACS) for improved visibility into container images.
Create a “best practices” guide for deploying applications in OpenShift clusters.
Find individuals interested in helping with orphaned packages and onboarding new maintainers for the infra-sig package group.
Create a list of release engineering packages for inclusion in the infra-sig.
Continue discussions on migrating the proxy network and managing AWS infrastructure with Ansible.
Update onboarding documentation, implement marketing strategies for attracting contributors, and organize “Hello” days for new members.
Archive/migrate/delete wiki pages in the “Category:Infrastructure” section.
Work on tickets to set up a separate database for CommOps Datagrepper access.
Investigate the feasibility of setting up ARA in infrastructure.
Stand up a proof of concept for AWX deployment and discuss potential repository restructuring.
Set up a Zabbix bot channel for alerts, adjust alerts based on comparisons with Nagios, and consider upgrading to the next LTS version.
Overall, the workshop was a success, with productive discussions and a clear list of action items to move forward.
Note: The workshop lacked remote participation due to network limitations. The source material encourages readers to express interest in helping with the action items.
Meson has had togglable options from almost the very beginning. These split into two camps. The first one is "common options" like optimizations, warning level, language standard version and so on. The second one is "per project" options that are specific to each project, such as which backend to use. For a long time things were quite nice but as people started using subprojects more and more, the need to configure common options on a per-subproject basis became more and more important.
Meson added a limited way of setting some options per subproject, but it was never really felt like a proper integrated solution. Doing it properly turns out to have a lot of requirements because you want to be able to:
Override any shared option for any subproject
Do this at runtime from the command line
You want to unset any override given
Convert existing per-project settings to the new override format
Provide an UI that is readable and sensible
Do all of this without needing to edit subproject build files
The last one of these is important. It means that you can use deps directly (i.e. from WrapDB) without any local patches.
What benefits do you get out of it?
The benefits are most easily seen via examples. Let's say you are developing a program that uses a dependency that does heavy number crunching. You need to build that (and only that) subproject with optimizations enabled, otherwise your development experience is intolerably slow. This is done by defining an augment, like so:
meson configure -Acruncher:optimization=2
A stronger version of this would be to compile all subprojects with optimizations but the top level project without them. This is how you'd do it:
This scheme permits you to do all sorts of useful things, like disable -Werror on specific projects, build some subprojects with a different language version (such as gnu99), compiling LGPL deps as shared libraries and everything else as a static library, and so on.
Implementing
This is a big internal change. How big? Big! This is the largest refactoring operation I have done in my life. It is big enough that it took me over two years of procrastination before I managed to gather enough strength to start work on this. Pretty much all of my Meson work in the last six months or so has been spent on this one issue. The feature is still not done, but the merge request already has 80 commits and 1700+ new lines and even that is an understatement. I have chopped off bits of the change and merged them on their own. All in all this meant that the schedule for most days of my summer vacation went like this:
Wake up
Work on Meson refactoring branch until fed up
Work on my next book until fed up
Maybe do something else
Sleep
FTR I don't recommend this style of working for anyone else. Or even to myself. But sometimes you just gotta.
The main reason this change is so complex lies in the architecture. In existing code each built target "knew" the option settings needed for it (options could and can be overridden in build files on a per-target basis). This does not work any more. Instead the code needs one place that encapsulates all option data and provides methods like "what is the value of option X when building target Y in subproject Z". Option code was everywhere, so changing this meant touching the entire code base and that the huge change blob must be landed in master atomically.
The only thing that made this change even remotely feasible was that Meson has an extensive test suite. The main code changes were done months ago, and all work since then has gone into making existing unit tests pass. They still don't pass, so work continues. Without this test suite there would have been hundreds of regressing projects, people would be angry and everyone would pin their Meson to an old version and refuse to update. These are the sorts of breakages that kill projects dead. So, write tests, even if it does not seem fun. Without them every project will eventually end up in a fork in the road where the choice is between "death by stagnation" and "death by breaking end users". Most projects are not Python 3. They probably won't survive a similar level of breakage.
Refactoring, types and Python
Python is, at the same time, my favourite programming language and very much not my favourite programming language. Python in the small is nice, readable, wonderful and productive. As the project size grows, the lack of static types becomes aggravating and eventually you end up debugging cases like "why does this argument that should be a dict is an array one out of 500 times at random". Types make these problems go away and make refactoring easy.
But not always.
For this very specific case the complete lack of types actually made the refactoring easier. Meson currently supports more than one hundred different compilers. I needed to change the way compiler classes work, but I did not know how. Thus I started by just using the GNU C compiler. I could change that (and its base class) as much as I wanted without having to care about any other compiler class. As long as I did not use any other compiler their code was not called and it did not matter that their method signatures were completely different. In a static language all type changed would need to be done up front just to make the dang thing compile.
Still, you can have my types when you drag them from my cold, dead fingers. But maybe this is something for language designers of the future to consider. It would be kind of cool to have a strictly typed language where you could add a compiler flag to say "convert all variables into Python style variant dictionaries and make all type checks, method invocations etc work at runtime". Yes, people would abuse the crap out of this feature, but the same can be said about every new feature.
When will this land?
It is not done yet, so we don't know. At the earliest this will be in the next release, but more likely in the one after that.
If you like trying out new things and living dangerously, you can try the code from this MR. Be sure to post comments on that page if you do.
This blog post has been floating around as a draft for several years. It eventually split off into a presentation at GUADEC 2022, titled Offline learning with GNOME and Kolibri (YouTube). In that presentation, Manuel Quiñones and I explained how Endless OS reaches a unique audience by providing Internet-optional learning resources, and we provided an overview of our work with Kolibri. This post goes into more detail about the technical implementation of the Kolibri desktop app for GNOME, and in particular how it integrates with Endless OS.
Integrating a flatpak app with an immutable OS
In Endless OS, way back with Endless OS 4 in 2021, we added Kolibri, an app created by Learning Equality, as a new way to discover educational content. Kolibri has a rich library of video lessons, games, documents, e-books, and more; as well as tools for guided learning – both for classrooms, and for families learning at home. The curation means it is safe and comfortable to freely explore. And all of this works offline, with everything stored on your device.
Making this all come together was an interesting challenge, but looking back on it with Endless OS 6 alive and well, I can say that it worked out nicely.
The Kolibri app for GNOME
Learning Equality designed Kolibri with offline, distributed learning in mind. While an organization can run a single large Kolibri instance that everyone reaches with a web browser, it is equally possible for a group of people to use many small instances of Kolibri, where those instances connect with each other intermittently to exchange information. The developers are deeply interested in sneaker net-style use cases, and indeed Kolibri’s resilience has allowed it to thrive in many challenging situations.
Despite using Django and CherryPy at its heart, Kolibri often presents itself as a desktop app which expects to run on end user devices. Behind the scenes, the existing Windows and MacOS apps each bundle a Kolibri server, running it in the background for as long as the desktop app is running.
We worked with Learning Equality to create a new Kolibri app for GNOME. It uses modern GTK with WebKitGTK to show Kolibri itself. It also includes a desktop search provider, so you can search for Kolibri content from anywhere.
The Kolibri GNOME app is distributed as a flatpak, so its dependencies are neatly organized, it runs in a well-defined sandbox, and it is easy to install it from Flathub. For Endless OS, using flatpak means it is trivial to update Kolibri independent from Endless OS’s immutable base system.
Kolibri Daemon
But Endless OS doesn’t just include Kolibri. One of my favourite parts of Endless OS is it provides useful content out of the box, which is great for people with limited internet access. So in addition to Kolibri itself, we want a rich library of Kolibri content pre-installed. And with so much already there, ready to be used, we want it to be easy for people to search for that content right away and start Kolibri for the first time.
This becomes both a technical challenge and a philosophical challenge. Normally, each desktop user has their own instance of Kolibri, with its own hidden directory full of content. Because it is a flatpak, it normally doesn’t see the rest of the system unless we explicitly give it permission to, and every time we do that we need to think carefully about what it means. Should we really grant a WebView the ability to read and write /run/media? We try to avoid it.
At the same time, we want a way to create new apps which use content from Kolibri, so that library of pre-installed content is visible up front, from the apps grid. But it would beexpensive if each of these apps ran its own instance of Kolibri. And whatever solution we employ, we don’t want to diverge significantly from the Kolibri people are using outside of Endless OS.
To solve these problems, we split the code which starts and stops the Kolibri service into a separate component, kolibri-daemon. The desktop app (kolibri-gnome) and the search provider each communicate with kolibri-daemon using D-Bus.
This design is exactly what happens when you start the Kolibri app from Flathub. And with the components neatly separated, on Endless OS we add eos-kolibri, which takes it a step further: it adds a kolibri system user and a service which runs kolibri-daemon on the D-Bus system bus. The resulting changes turn out to be straightforward, because D-Bus provides most of what we need for free.
With this in place, every user on the system shares the same Kolibri content, and it is installed to a single well-known location: /var/lib/kolibri. Now, pre-installing Kolibri content is a problem we can solve at the system level, and in the Endless OS image builder. Independent from the app itself.
Channel apps
Now that we have solved the problem of Kolibri content being duplicated, we can come back to having multiple apps share the same Kolibri service. In Endless OS, we want users to easily see the content they have installed, and we do this by adding launchers to the apps grid.
First, we need to create those apps. If someone has installed content from a Kolibri channel like TED-Ed Lessons or Blockly Games, we want Kolibri to generate a launcher for that channel.
But remember, Kolibri on Endless OS is an unprivileged system service. It can’t talk to the DynamicLauncher portal. That belongs to the user’s session, and we want these launchers to be visible before a user ever starts Kolibri in their own session. Kolibri also can’t be creating files in /usr/share/applications. That would be far too much responsibility.
Instead, we add a Kolibri plugin to generate desktop entries for channels. The desktop entries refer to the Kolibri app using a custom URI scheme, a layer of indirection because Kolibri (potentially inside a flatpak) is unaware of how the host system launches it. The URI scheme provides enough information to start in a channel-specific app mode, instead of in its default configuration.
Finally, instead of placing the desktop entry files in one of the usual places, we place them in a well-known location inside Kolibri’s data directory. That way the channel apps are available, but not visible by default.
In Endless OS, the channel launchers end up in /var/lib/kolibri/data/content/xdg, so in our system configuration we add that directory to XDG_DATA_DIRS. This turns out to be a good choice, because it is trivial to start generating search providers for those apps, as well.
Search providers
To make sure people can find everything we’ve included in Endless OS, we add as many desktop search providers as we can think of, and we encourage users to explore them. The search bar in Endless OS is not just for apps.
That means we need a search provider for Kolibri. It’s a simple enough problem. We extended kolibri-daemon‘s D-Bus interface with its own equivalents for the GNOME Shell search provider interface. It is capable of reading directly from Kolibri’s database, so we can avoid starting an HTTP server. But we also want to avoid dealing with kolibri-daemon as much as possible. It is a Python process, heavy with web server stuff and complicated multiprocessing code. And, besides, the daemon could be connecting to the D-Bus system bus, and the shell only talks to search providers on the session bus. That’s why the search provider itself is a separate proxy application, written in C.
But in Endless OS, we don’t just need one search provider, either. We want one for each of those channel apps we generated. So, I mentioned that our Kolibri plugin generates a search provider to go with each desktop file. Of course, loading and searching through Kolibri’s sqlite database is already expensive once, so it would be absurd to do it for every channel that is installed. That’s a lot of processes!
Fortunately, those search providers are all the same D-Bus service, with a different object path for each Kolibri channel. That one D-Bus service receives a lot of identical search queries for a lot of different object paths, but at least the system is only starting one process for it all. In the search provider code, I added a bespoke task multiplexer, which allows the service to run a single search in kolibri-daemon for a given query, then group the results and return them to different invocations from the shell.
It is a complicated workaround, but it means search results appear in distinct buckets with meaningful names and icons. For our purpose in Endless OS, it was definitely worth the trouble.
User accounts
There was one last wrinkle here: Kolibri kept asking people to set it up, make a user account (with a password!), and sign in. It is, after all, a standalone learning app with a big complicated database that keeps track of learning progress and understands how to sync content between devices. But this isn’t a great experience if you’re just here to watch that lecture about cats.
What we want is for Kolibri to already know who is accessing it. They’re already signed in as a desktop user. And most of the time, we want to blaze right through that initial “set up your device” step, or at least make it as smooth as possible.
To do that, we added an interface in kolibri-daemon so the desktop app can get an authentication token to use over HTTP. On the other side, kolibri-daemon privately communicates with Kolibri itself to verify an authentication token, and it communicates with logind to build a profile for the authenticating user.
It was ugly at first, with a custom kolibri-desktop-auth-plugin which sat on top of Kolibri’s authentication system. But after some iteration, upstream Kolibri now has its own understanding of desktop users. On the surface, it uses Kolibri’s app interface plugin for platform integration. With the newest version of Kolibri we have been able to solve authentication in a way that I am properly happy with.
My favourite part of the feature has been seeing it come together with Kolibri’s first run wizard. Given a working authentication token, Kolibri knows to skip creating an initial user account, leaving only some simple questions about how the user is planning to use Kolibri; independently or connecting to an existing classroom.
That’s it!
It has been great to work on the Kolibri desktop app, and I expect to take some of the approaches and lessons here over to other projects. It is the first big new Python desktop app I have worked with, and it was interesting using some modern Python tools in tandem with the GNOME ways of doing things. The resulting codebase has some fun details:
The source repository includes a Flatpak manifest, so it builds and runs out of the box in GNOME Builder. As soon as that was working, I used Builder for everything.
Meson is truly indispensable for this kind of thing. We’re sharing build configuration between a bunch of Python modules, all sorts of configuration and data files, and a pair of C projects – one of which is imported by a Python module using GObject introspection. This all works (in a mere 577 lines of meson.build, if you’re counting) because the build system is language-agnostic, and I love it for that. I know that isn’t a lot to ask, but the go-to for Python is decidedly not language-agnostic, and I do not love it.
We added pre-commit to automatically clean up source files and run quick tests against them. It doesn’t actually require you have a Python codebase, but it is written in Python and I think people are afraid of how Pythony it looks? It’s really convenient, and it does a good job taking care of the usual nightmare of setting up a virtual environment to run all its tools. I often don’t bother with the actual git hook part, and instead I remember to run the thing manually, and we use the pre-commit github action to be sure.
At some point, I added Python type hinting to every part of the project. This tremendously improved the development experience with Builder, and it allowed me to add a mypy pre-commit hook to catch mistakes.
I got annoyed at the problem of needing to write release notes in the appdata file before knowing what the next release is called, so I devised a fun scheme where we add notes under "{current_version}+next", and then bump-my-version (another tool that looks very Pythony but everyone should use it) knows to mark that release entry as released, setting the date and version appropriately. I wish it didn’t involve regex, but as a concept it has been nice to use. I was tempted to write a pre-commit hook which actually insists on an up to date “next release” entry in appdata, but I should find another project to try it with.
With that said, a better workflow probably involves appstream-util news-to-appdata.
Managing history in WebKit can be tricky because the BackForwardList is read-only. That was an issue with the Kolibri app because we (with our UI consisting almost entirely of a WebView) need to communicate about Kolibri’s state before its HTTP server is running. Kolibri upstream provides a static HTML loading screen for this purpose, which is fine, but now we have this file in our WebView’s back / forward list. I solved it by swapping between different WebViews, and later showing one in a dialog just for Kolibri’s setup wizard. At first, that was all to keep the history stack organized, but at the same time I found it made the app feel a little less like a web browser in a trench coat. We can switch from the loading WebView to the real thing with a nice crossfade, and only when the UI is actually for real finished loading.
This whole project uses a lot of GObject throughout. At some point I finally read the pygobject manual and found myself happily doing property binding, signals and async functions and all those good things from Python. It was a much better experience than earlier in the project’s life where there was a type of angry mishmash between vanilla Python and GObject. (The thing that really freed this up was when I moved a lot of D-Bus code over to a C helper library with gdbus-codegen, which allowed me to delete the equivalent duplicative Python code, and also introduced a bunch more GObject). It’s easy to see why GObject works best with a language that doesn’t carry its own big standard library, but I was happy with how productive I could be in Python once I started actively preferring GObject, especially with the various magic helpers provided by PyGObject. In a future starting-from-scratch project, I would be tempted to make that a rule when adding imports and writing new classes.
I have to admit I got carried away with certain aspects of this. In the end there is a certain discontent to be had spending creative energy on what is, from many angles, a glorified web browser. It’s frustrating when the web stack leads us to treat an application as a black box behind an HTTP interface, which makes integration difficult: boot it up (in its own complex runtime environment which is heroically not a Docker container); wait until it is ready (Kolibri is good at this, but sometimes you’re just watching a file or polling some well-known port); authenticate; ask it (over HTTP) some trivial question that amounts to a single SQL command; return None. But look at that nice framework we’re using!
At the same time, it isn’t lost on me that a software stack like Kolibri’s simply is a popular choice for a cross-platform app. It’s worth understanding how to work with it in a way that still does the best we can to be useful, efficient, and comfortable to use.
Beyond all the tech stuff, I want to emphasize that Kolibri is an exceptionally cool project. I truly admire what Learning Equality are doing with it, and if you’re interested in offline-first content, data sovereignty, or just open source learning in general, I highly recommend checking it out – either our app on Flathub, or at learningequality.org/kolibri.
Most end-user platforms have something they call an intent system or something
approximating the idea. Implementations vary somewhat, but these often amount
to a high-level desktop or application action coupled to a URI or mime-type.
There examples of fancy URIs like sms:555-1234?body=on%20my%20way that can do
intent-like things, but intents are higher-level, more purposeful and certainly
not restricted to metadata shoehorned into a URI.
I'm going to approach this like the original proposal by David Faure and the
discussions that followed, by contrasting it with mime-types and then
demonstrating what the files for some real-world use cases might look like.
Let's start with the mime-apps Specification. For desktop environments
mime-types are, most of all, useful for associating content with applications
that can consume it. Once you can do that, the very next thing you want is
defaults and fallback priorities. Now can you double-click stuff to have your
favourite application open it, or right-click to open it with another of your
choice. Hooray.
We've also done something kind of clever, by supporting URI handlers with the
special x-scheme-handler/* mime-type. It is clever, it does work and it was
good enough for a long time. It's not very impressive when you see what other
platforms are doing with URIs, though.
Moving on to the Implements key in the Desktop Entry Specification, where
applications can define "interfaces" they support. A .desktop file for an
application that supports a search interface might look like this:
The last line is a list of interfaces, which in this case is the D-Bus interface
used for the overview search in GNOME Shell. In the case of the
org.freedesktop.FileManager1 interface we could infer a default from the
preferred inode/directory mime-type handler, but there is no support for
defining a default or fallback priority for these interfaces.
While researching URI handlers as part of the work funded by the STF, Sonny
reached out to a number developers, including Sebastian Wick, who has been
helping to push forward sandboxing thumbnailers. The proposed intent-apps
Specification turns out to be a sensible way to frame URI handlers, and other
interfaces have requirements that make it an even better choice.
In community-driven software, we've operated on a scratch-an-itch priority
model for a very long time. At this point we have several, arguably critical,
use cases for an intent system. Some known use cases include:
Default Terminal
This one should be pretty well known and a good example of when you might
need an intent system. Terminals aren't really associated with anything, let
alone a mime-type or URI scheme, so we've all been hard-coding defaults for
decades now. See the proposed terminal-intent Specification for details.
Thumbnailers
If C/C++ are the languages responsible for most vulnerabilities, thumbnailers
have to be high on the list of application code to blame. Intents will allow
using or providing thumbnailing services from a sandboxed application.
URI Handler
This intent is probably of interest to the widest range of developers, since
it allows a lot freedom for independent applications and provides assurances
relied on by everything from authentication flows to personal banking apps.
Below is a hypothetical example of how an application might declare it can
handle particular URIs:
While the Desktop Entry specification states that interfaces can have a named
group like above, there are no standardized keys shared by all interfaces. The
Supports key proposed by Sebastian is important for both thumbnailers and URI
handlers. Unlike a Terminal which lacks any association with data, these need
the ability to express additional constraints.
So the proposal is to have the existing Implements key work in tandem with
the intentapps.list (similar to the MimeType key and mimeapps.list), while
the Supports key allows interfaces to define their own criteria for defaults
and fallbacks. Below is a hypothetical example of a thumbnailer's .desktop
file:
The Supports key will always be a list of strings, but the values themselves
are entirely up to the interface to define. To the intent system, these are
simply opaque tags with no implicit ordering. In the URI handler we may want
this to be a top-level domain to prevent things like link hijacking, while
thumbnailers want to advertise which mime-types they can process.
In the intentapps.list below, we're demonstrating how one could insist that a
particular format, like sketchy SVGs, are handled by Loupe:
We're in a time when Linux users need to do things like pass an untrusted file
attachment, from an unknown contact, to a thumbnailer maintained by an
indepedent developer. So while the intent-apps Specification itself is
superficially quite simple, if we get this right it can open up a lot of
possibilities and plug a lot of security holes.
First a bit of context for the GLib project, which is comprised of three main
parts: GLib, GObject and GIO. GLib contains things you'd generally get from a
standard library, GObject defines the OOP semantics (methods/properties/signals,
inheritance, etc), and GIO provides reasonably high-level APIs for everything
from sockets and files to D-Bus and Gio.DesktopAppInfo.
The GLib project as a whole contains a substantial amount of the XDG
implementations for the GLib/GTK-lineage of desktop environments. It also
happens to be the layer we implement a lot of our cross-platform support, from
OS-level facilities like process spawning on Windows to desktop subsystems like
sending notifications on macOS.
The merge request I drafted for the initial implementation received what
might look like Push Back, but this should really be interpreted as a Speed
Bump. GLib goes a lot of places, including Windows and macOS, thus we need
maintainers to make prudent decisions that allow us to take calculated risks
higher in the stack. It may also be a sign that GLib is no longer the first
place we should be looking to carry XDG implementations.
Something that you may be able to help with, is impedance-matching our
implementation of the intent-apps Specification with its counterparts in the
Apple and Microsoft platforms. Documentation is available (in varying quality),
but hands-on experience would be a great benefit.
Last year, I was invited by Sonny Piers to co-mentor for both Google Summer
of Code and Outreachy, which was really one the best times I've had in the
community. He also invited a couple of us Workbenchers from that period to
the kick-off meeting for this year's projects.
Recently, he asked if I could step in and help out with this year's programs.
This is a very unfortunate set of circumstances to arise during an internship
program, but regardless, I'm both honored and thrilled.
I think there's good chance you've run into one of our mentees this year,
Shem Angelo Verlain (aka vixalien). He's been actively engaging in the GJS
community for some time and contributing to better support for TypeScript,
including his application Decibels which is in incubation to become a part of
GNOME Core. His project to bootstrap TypeScript support in Workbench is going
to play an important role in its adoption by our community.
Our other mentee, Bharat Atbrat, has a familiar origin story. It started as an
innocent attempt to fix a GNOME Shell extension, turned into a merge request
for GNOME Settings, rolled over into porting Workbench demos to Vala and it's
at this point one admits to oneself they've been nerd-sniped. Since then, Bharat
has been porting more demos to Vala and working on an indexed code search for
the demos. As a bonus, we will get a GOM demo that's being used to prototype
and test searching capabilities.
The release notes are not yet finalized for GNOME 47, but there are few
highlights worth mentioning.
There have been several improvements to the periodic credential checks, fixing
several false positives and now notifying when an account needs to be
re-authenticated. The notification policy in GNOME 47.beta turned out overly
aggressive, so it has been amended to ensure you are notified at most once per
account, per session.
For Kerberos users, there is rarely any exciting news, however after
resurrecting a merge request by Rishi (a previous maintainer) and some help,
we now support Linux's general notification mechanism as a very efficient
alternative to the default credential polling. If you're using your Kerberos or
Fedora account on a laptop or GNOME Mobile, this may improve your battery life
noticeably.
The support for Mail Autoconfig and improved handling of app passwords for
WebDAV accounts will ship in GNOME 47. The DAV discovery and Mail Autoconfig
will form the base of the collection provider, but this won't ship until
GNOME 48. Aside from time constraints, this will allow a cycle to shake out
bugs while the existing pieces are stitched together.
The Microsoft 365 provider has enabled support for email, calendar and
contacts, thanks to more work by Jan Michael-Brummer and Milan Crha. This
is available in GNOME OS Nightly now, so it's great time to get in some
early testing. We've made progress on verifying our application to supports more
organizational accounts and, although this is not constrained by our release
schedule, I expect it to be resolved by GNOME 47.
Many thanks again to the Sovereign Tech Fund and everyone who helped make it
possible. I would also like to express my appreciation to everyone who helps me
catch up on the historical context of the various XDG and GLib facilities. Even
when documentation exists, it can be extremely arduous to put the picture
together by yourself.
(The issues described in this post have been fixed, I have not exhaustively researched whether any other issues exist)
Feeld is a dating app aimed largely at alternative relationship communities (think "classier Fetlife" for the most part), so unsurprisingly it's fairly popular in San Francisco. Their website makes the claim:
Can people see what or who I'm looking for? No. You're the only person who can see which genders or sexualities you're looking for. Your curiosity and privacy are always protected.
which is based on you being able to restrict searches to people of specific genders, sexualities, or relationship situations. This sort of claim is one of those things that just sits in the back of my head worrying me, so I checked it out.
First step was to grab a copy of the Android APK (there are multiple sites that scrape them from the Play Store) and run it through apk-mitm - Android apps by default don't trust any additional certificates in the device certificate store, and also frequently implement certificate pinning. apk-mitm pulls apart the apk, looks for known http libraries, disables pinning, and sets the appropriate manifest options for the app to trust additional certificates. Then I set up mitmproxy, installed the cert on a test phone, and installed the app. Now I was ready to start.
What became immediately clear was that the app was using graphql to query. What was a little more surprising is that it appears to have been implemented such that there's no server state - when browsing profiles, the client requests a batch of profiles along with a list of profiles that the client has already seen. This has the advantage that the server doesn't need to keep track of a session, but also means that queries just keep getting larger and larger the more you swipe. I'm not a web developer, I have absolutely no idea what the tradeoffs are here, so I point this out as a point of interest rather than anything else.
Anyway. For people unfamiliar with graphql, it's basically a way to query a database and define the set of fields you want returned. Let's take the example of requesting a user's profile. You'd provide the profile ID in question, and request their bio, age, rough distance, status, photos, and other bits of data that the client should show. So far so good. But what happens if we request other data?
graphql supports introspection to request a copy of the database schema, but this feature is optional and was disabled in this case. Could I find this data anywhere else? Pulling apart the apk revealed that it's a React Native app, so effectively a framework for allowing writing of native apps in Javascript. Sometimes you'll be lucky and find the actual Javascript source there, but these days it's more common to find Hermes blobs. Fortunately hermes-dec exists and does a decent job of recovering something that approximates the original input, and from this I was able to find various lists of database fields.
So, remember that original FAQ statement, that your desires would never be shown to anyone else? One of the fields mentioned in the app was "lookingFor", a field that wasn't present in the default profile query. What happens if we perform the incredibly complicated hack of exporting a profile query as a curl statement, add "lookingFor" into the set of requested fields, and run it?
Oops.
So, point 1 is that you can't simply protect data by having your client not ask for it - private data must never be released. But there was a whole separate class of issue that was an even more obvious issue.
Looking more closely at the profile data returned, I noticed that there were fields there that weren't being displayed in the UI. Those included things like "ageRange", the range of ages that the profile owner was interested in, and also whether the profile owner had already "liked" or "disliked" your profile (which means a bunch of the profiles you see may already have turned you down, but the app simply didn't show that). This isn't ideal, but what was more concerning was that profiles that were flagged as hidden were still being sent to the app and then just not displayed to the user. Another example of this is that the app supports associating your profile with profiles belonging to partners - if one of those profiles was then hidden, the app would stop showing the partnership, but was still providing the profile ID in the query response and querying that ID would still show the hidden profile contents.
Reporting this was inconvenient. There was no security contact listed on the website or in the app. I ended up finding Feeld's head of trust and safety on Linkedin, paying for a month of Linkedin Pro, and messaging them that way. I was then directed towards a HackerOne program with a link to terms and conditions that 404ed, and it took a while to convince them I was uninterested in signing up to a program without explicit terms and conditions. Finally I was just asked to email security@, and successfully got in touch. I heard nothing back, but after prompting was told that the issues were fixed - I then looked some more, found another example of the same sort of issue, and eventually that was fixed as well. I've now been informed that work has been done to ensure that this entire class of issue has been dealt with, but I haven't done any significant amount of work to ensure that that's the case.
You can't trust clients. You can't give them information and assume they'll never show it to anyone. You can't put private data in a database with no additional acls and just rely on nobody ever asking for it. You also can't find a single instance of this sort of issue and fix it without verifying that there aren't other examples of the same class. I'm glad that Feeld engaged with me earnestly and fixed these issues, and I really do hope that this has altered their development model such that it's not something that comes up again in future.
(Edit to add: as far as I can tell, pictures tagged as "private" which are only supposed to be visible if there's a match were appropriately protected, and while there is a "location" field that contains latitude and longitude this appears to only return 0 rather than leaking precise location. I also saw no evidence that email addresses, real names, or any billing data was leaked in any way)
After many months, I finally found the time to finish the GNOME desktop/application settings migration in the Linux Desktop Migration Tool and made another release. It basically involves exporting the dconf keys on the source machine and importing writable keys on the destination machine. I’ve also added some extra code to handle the desktop background. If the dconf key points to a picture that is not present on the destination machine, the picture is copied as well. Be it a custom background, or a system provided one that is no longer shipped (in case you’re doing the migration between OSes of different versions).
The list of migrations the tool can do is already fairly long:
Migrate data in XDG directories (Documents, Pictures, Downloads…) and other arbitrary directories in home.
WordPress.com has recently allowed blogs to Enter the Fediverse, so you can now follow my writings on Mastodon and other places. Click the new ‘Follow’ button in the sidebar, or paste this succinct Fediverse address:
This blog is more serious writing about technology, you can also follow @vladimirchicken@mastodon.art which is mostly music, art and needless complaints over things I can’t change.
Reflections on GUADEC 2024
I was lucky enough to attend GNOME’s 2024 conference in Denver. I had a great time, saw many people I haven’t seen this decade, and it was also my first trip into the USA beyond the easternmost parts of the east coast.
I have a more thoughts about this year’s event which are complex and contradictory and better discussed face to face. Sometimes you have to hold multiple conflicting thoughts in your head at once. I’ve written some of these thoughts down below.
The world is huge … like really big. Denver is a full EIGHT timezones away from Berlin, right? If you get up at 9AM in Denver then it’s already 5PM in Berlin, and it’ll be 6PM by the time you’ve brushed your teeth. How can you unite two groups of people across such a big time gap?
Having tried it, my feeling that a “federated” event split across different locations at the same time is not a replacement for a single, global in-person event. The community splits according to the geographical distribution of its members, roughly following Conway’s Law.
Travelling back from Denver to Santiago de Compostela took me about 50 hours in the end. It was supposed to be less but there was a 5 hour delay in Denver due to a shortage of air traffic controllers in Newark, which led to missing a connection to Madrid, the next day some bad weather added two more hours of delay, and then the trains from Madrid were largely full so I couldn’t get back home until early evening.
I kind of enjoy travel chaos (and my feeling is that as a society, we’ll only reduce our dependence on air travel if and when it becomes frustratingly inconvenient to use).
I feel lucky that I don’t have to do this kind of trip very frequently. I have more appreciation now for the significant effort people go through to get to events in Europe.
Some benefits to holding a GUADEC in the USA. Inspirational keynotes by local speakers, both Ryan Sipes’ story of Thunderbird, and Stephanie Taylor who is the force behind Google Summer of Code. Meeting contributors who don’t travel to Europe. Media coverage of the GNOME Foundation AGM, in a good way.
I think we missed an opportunity to make more of the event. Involving “the local community” in a summer event is always tricky, as during the summer holidays, “casual” attendees such as students and hobbyists are mostly on holiday. They aren’t usually looking for new software conferences they might attend. That said, GNOME would definitely would benefit from more hands focused on communication outside the project, what we often call “marketing” and “engagement”. It’s a difficult and often thankless task and we have to pay someone to do it. (With all the endless money we have, of course).
I did mention the thing about multiple conflicting ideas at once, right?
Let’s not pretend that a video conference or a hybrid BOF is the same as an in-person meetup. Once you’ve sung karaoke with someone, or explored Meow Wolf, or camped in the desert in Utah together, your relationship is richer than when you only interacted via Gitlab pull requests and BigBlueButton. You have more empathy and you can resolve conflicts better.
Let’s keep exploring new ways to collaborate. Regional events and hybrid events. And accept that these will form bubbles. If you live near members of the GNOME design team and meet in-person with them, you’re going to be able to influence GNOME’s design, more easily than if you live on a continent such as Africa where (to my knowledge) you can’t meet any existing design team members without leaving your continent. If you have a friend who maintains Nautilus it’s going to be easier to get up to speed with Nautilus development yourself, compared to if you’re starting from scratch and you live in a timezone that’s 10 hours offset from Europe.
We want to rethink technical governance in GNOME, which currently somewhat resembles the 15th century Feudal system. (See also, Tobi’s less flippant explanation of how decisions are made). Let’s keep geography in mind when we discuss this. And let’s also think how we can balance out the implicit advantage you get for being based in a certain place, and how we can grow local communities in places that don’t currently have them. I suspect this effort will need to be larger than just the GNOME project, and we can’t be the only community thinking about this stuff.
The USA is just as crazy as you imagine from its TV output. I was ready for beautiful scenery, long car journeys, no pavements, icy air conditioning, unhealthy food and franchises inside of franchises inside of franchises. I was genuinely surprised how hot it gets everywhere — even during my unwanted stopover in Newark the weather was 35° humid heat. And I wasn’t ready for disposable plates, cups and cutlery at every hotel breakfast. I’ve stayed in very cheap and very expensive hotels in many places, and all of them manage to wash and reuse the plates.
I want to see the single location, in-person GUADEC continuing while it’s still possible to do it. Count how many plane tickets were bought this year to attend GNOME events, and compare it to the number of flights taken just by Elon Musk’s private jet. It’s great that we avoided a few dozen plane tickets compared to last year but I’m yet to see a noticeable impact on the airline industry; while the impact on the GNOME project of splitting the community into two physical locations was very noticeable indeed.
We should alternate in-person GUADEC with more radically decentralized events, under a different name. We need to continue developing those as well. Like it or not there are big changes coming in society as the world gets increasingly hot. Tobi outlined all this very well in 2022. Nobody knows exactly what will happen of course, but you can expect that building resilience to change will be worthwhile, to put it verymildly. The key is for this to be something positive rather than a negative. The creation of something new rather than the loss of something we fondly remember. I mean, you can’t claim “GUADEC” is a particularly great name
It’s easy to write about this stuff in a blog post of course, harder to put into practice, and actually it wasn’t even easy to write…. this has taken me three hours.
Fairphone 5
I was going to write more stuff here but it turns out I had a lot to say about GNOME this month. At FOSDEM 2024, which I could easily attend in-person because I’m European, I saw a great talk by Luca from Fairphone, and decided my next phone would be a Fairphone 5. And I just got one. More on that next month, I guess.
I attended GUADEC 2024 last month in Denver, Colorado. I thought I’d write about some of the highlights for me.
It was definitely the smallest GUADEC I’ve been to, and it was unusual in some other ways too, such as having several hybrid presentations, with remote and in-person presenters sharing the stage. That took some adjusting, but it worked well, even if I missed some of the energy of past events. (I shared some thoughts about hybrid GUADEC on a Discourse thread).
I felt this GUADEC was really defined by the keynotes. They were great!
First, we had Ryan Stipes from Thunderbird telling us all about Thunderbird’s journey from a somewhat neglected but well-loved side project and on to a thriving self-funded community project: Thunderbird, The Death and Rebirth of an OSS Project (YouTube). He had a lot to say about the value of metrics to measure the impact of certain features and target platforms, which really resonated with people. (It is interesting to note, for instance, there appear to be more Thunderbird users using Windows 8.1 than Linux). He also had a lot to say about the success Thunderbird had just being direct and asking users for money.
Much of this success comes from Thunderbird doing a good job telling its own story. People clearly understand what Thunderbird is doing for them. And there was plenty of talk for the next few days: what does it mean for GNOME to own its story?
I also really enjoyed Stephanie Taylor’s keynote, all about Google Summer of Code (which started 20 years ago now!): Google Summer of Code 20 years of OSS Mentorship (YouTube). It just made me super happy as a GSoC alumni (one of thousands!) to see that program continuing to do so much good, and how much mentorship in open source has grown over the years.
Scott Jenson’s presentation, How can GNOME explore bigger concepts? (YouTube), is another really important watch. Scott’s advice about breaking free from traps like constraint thinking really resonated with me, especially his suggestion to, at first, treat the software like it is magic and see where that leads.
That approach reminds me of how software improves in implementation, as well. It is natural for a codebase to start off with a whole bunch of “do magic” stub functions, then slowly morph into a chaotic mess until finally it turns into something that actually might just work. And getting to that last step usually involves deleting a lot of code, after it turns out you never needed all that much magic. But you have to be patient with the chaos to get there. You have to believe in it.
Speaking of magic, there is so much around GNOME that is exciting right now, so I spent some time just being excited about things.
Eitan Isaacson talked about Spiel, a modern speech synthesis system: The Whole Spiel – A New Speech Synthesis API (YouTube). I loved his examples showing how it is important to satisfy several very different use cases for speech synthesis. While one user may value the precision of eSpeak at chipmunk speed, another user would prefer their computer talks like a human. And if we can get speech synthesis working well for non-accessibility reasons, there’s a real curb cut effect that should benefit everyone, including people who are just starting to use accessibility tools.
I went to the newest edition of Jonathan Blandford and Federico Mena Quintero’s presentation about Crosswords, GNOME Crosswords, Year Three (YouTube). It was abridged due to the format, but I especially enjoyed learning about the MVC-like data model for the application. It would be neat to see more GNOME apps using the same pattern.
There was a lot to learn about GNOME OS and OpenQA testing. The process for a new developer to get into hacking on a GNOME system component tends to be really awkward – particularly if that developer doesn’t want to mess up their host system. So You’re always breaking GNOME (YouTube) got me pretty excited about what’s coming with GNOME OS and sysext, as well as for testing in general. The OpenQA workshop on Monday was also well attended. Some people were unclear what openqa.gnome.org was doing, or what it can do for them. Just stepping through some conveniently broken tests and fixing them together was an excellent way to demystify the thing.
Much of this work is being helped along with the Sovereign Tech Fund. This is the GUADEC where a lot of that is up for show, and I think it’s amazing to see so many quiet but high impact projects finally getting the attention (and funding) they deserve.
Outside of the event, it was great hanging out around Denver with all sorts of GNOME folks. I loved how many restaurants were perfectly happy to accommodate giant mobs of people. We saw huge mountains, the Colorado Rockies winning a baseball game, surprisingly good karaoke, and some very unique bars. On the last day, a large contingent of us headed to Meow Wolf, which was just a ridiculously fun way to spend a few hours. It reminded me of a point and click adventure game in the style of Myst and Riven, in all the best ways.
I was also suitably impressed by the 35 minute walk from where I was staying, around Empower Field, over the South Platte River, under some giant highway … which was actually entirely pleasant, for North America. This part of Denver has plenty of pedestrian bridges, which are both nice to walk along and really helpful to guide pedestrians through certain areas, so for me the obvious walking routes were completely different from (and as efficient as) the obvious driving routes.
The GUADEC dinner was, for me, the ideal GUADEC dinner. It was right there at the venue, at the same brewery people had been going to every day – but this time with free tacos! I truly appreciated the consistency there, for Denver has good beer and good tacos. I also appreciated that we were set up both inside and outside, at nice big tables with plenty of room for people to sit. It helped me to feel comfortable, and it was great for people who were there with families (which meant I got to meet said families!). It reminded me of the GUADEC 2022 taco party. An event like this really shines when people are moving around, and there was a lot of it here.
It turns out I didn’t take many pictures this year, but the official ones are better anyway. I did, however, take far too many pictures from the train ride home: I rode Amtrak, mostly for fun, on California Zephyr from Denver to Sacramento; then Coast Starlight from Sacramento to Seattle; and the smaller Cascades train from Seattle to Vancouver. It was beautiful, and seriously I think everyone should have the opportunity to try an overnight roomette on the Zephyr. My favourite part was sitting in the spacious observation car watching the world go by, getting only the tiniest amount of work done. I found tons of fun people to talk to, which I don’t usually do, but something about that space made it oddly comfortable. Everyone there was happy and sociable and relaxed. And I guess I was still in conference mode.
I returned home refreshed and excited for where GNOME is heading, especially with new progress around accessibility and developer tools. And with plenty of ideas for little projects I can work on this year.
Thanks to all the awesome people who make GUADEC happen, as well as my employer, Endless OS Foundation, for giving me the opportunity to spend several work days meeting people from around the GNOME community and wandering around Denver.
It's been a busy year, and our platform and developer community-building efforts are paying off. Let's take a look at what we've been up to over the last six months, and measure its effect.
We're back with some new milestones thanks to the continued growth of Flathub as an app store and the incredible work of both our largely volunteer team and our growing app developer community:
Over 1,000 apps have been verified by their developers on Flathub, including 70% of the top 30 most popular apps. Developers of verified apps are ultimately in charge of their own app listings, and their updates are delivered directly to Flathub users while passing our automated testing and human review of things like permission changes.
Over 100 apps are now passing our quality guidelines that include checks like icon contrast on both light and dark backgrounds, quality screenshots, and consistent app naming and descriptions so users get a better experience browsing Flathub. These guidelines are what enable us to curate and display visually appealing and consistent banners on the new home page, for example.
This means that between late February and July, the developers of over 100 apps went out of their way to improve—and sometimes make significant changes to—their apps' metadata to get ready for these new guidelines and features on Flathub. We're proud of these developers who have gone above and beyond, and we look forward to even more apps opting in over time.
Developers, if you'd like to see your app featured on the home page, please ensure you are following these guidelines! We've heard from app developers that getting your app featured not only gives a bump in downloads, but can also bring an increase in contributions to your project if it's open source.
Six months ago we passed one million active users based on a simple if conservative estimate of updates to a common runtime version we had served. Using that same methodology, we now estimate we have over 4 million active users!
As a reminder, this data is publicly available and anyone can check our work. In fact, I personally would love if we could work with a volunteer from the community to automate this statistic so we don't have to do manual collation each time. If you're interested, check out this GitHub issue.
Those users have been busy, too: to date we have served over two billion downloads of different apps to people using different Linux flavors all around the world. This is a huge community of people trusting Flathub as their source of apps for Linux.
Thank you to our download-happy community of users who have put their trust in Flathub as their source of apps on Linux. Thank you to all of the developers of those apps, and in particular those developers who have chosen to follow the quality guidelines to help make Flathub a more consistent and engaging space. And thank you to every contributor to Flathub itself whether you are someone who fixed a typo in the developer documentation, helped translate the store, contributed mockups and design work, or spent countless hours keeping everything running smoothly.
As a grassroots effort, we wouldn't have become the Linux app store without each and every one of you. ❤️
Everything is better in color. Even better if it is HDR.
Seven-color and twelve-color color circles from 1708, attributed to Claude Boutet
In this post, we’ll provide an overview of what is happening with color in GTK, without diving too deeply into the weeds of colorimetry and color science.
The high-level goals of this effort are to enable proper handling of HDR content and color-managed workflows.
In the beginning, sRGB
Up until now, colors in GTK were always assumed to be in the sRGB color space. The omnipresent GdkRGBA struct is defined as specifying an sRGB color.
sRGB was a great thing 20 years ago, but there world is moving on to other color spaces that include a wider range of hues (such as Display-P3) or a bigger dynamic range (such as BT.2100-PQ).
After having this on our agenda for quite a while (initial investigations into this topic happened in 2021), we’re finally moving ahead with landing the foundations for a more colorful future.
Coming soon: GdkColorState
Earlier this year, we’ve merged some great work by Alice to support modern color syntax and color spaces in our CSS engine. Having expressive colors in CSS is great, but it can’t really shine if all our rendering machinery still requires colors to be specified in sRGB.
So over the past month or two, we’ve added support for doing our rendering in well-defined color spaces, and introduced an object representing these. It is called GdkColorState (don’t ask me why the name changed from space to state).
As of now, GdkColorState can represent sRGB and BT.2100-PQ, as well as their linearized variants. You cannot yet do much with these objects, they are mainly used internally by our renderers.
One thing you can already do though, is trying out how rendering in a linear color space will look, by setting the
GDK_DEBUG=linear
environment variable.
This is a change that we need to do eventually, to produce correct and understandable results, in particular when working with HDR content.
Doing all our compositing in a linear color state does look subtly different though, so want to delay this change until all of the surrounding work is done, and do it at beginning of a development cycle to give everybody time to adjust.
New protocols
The Wayland color management protocol has been a long time in the making, but it is hopefully close to leaving the experimental phase — kwin already has support for it, and there is a mutter branch as well.
We have added support for the xx-color-management-v4 protocol, so we get the preferred color state from the compositor (if it is supports that protocol), and we can tell it about the color state of the frames that we produce.
It is unlikely that your compositor will prefer an HDR color state like BT.2100-PQ today (unless you find the hidden switch to turn on experimental HDR support), but you can try how things look when GTK is rendering in that color state by setting the
GDK_DEBUG=hdr
environment variable.
Note that this doesn’t make HDR content appear on your screen — we do our rendering in HDR and translate the final frame back to sRGB as the last step. Making HDR content appear on your screen requires a compositor that accepts such content.
In the future: More color states and linear compositing
We still have a long todo list to fully develop color support in GTK.
The highlights include
More color states (including OKLCH for better gradients and YUV for video content)
Color state aware rendering api (GdkColor, and new GtkSnapshot APIs)
Passing CSS color state information down to the renderer
Propagating color state information from gstreamer (for HDR, among other things)
Switching to linear compositing
A few of these will hopefully make it in time for the GTK 4.16 release.
When doing a major refactoring in Meson, I came up with a interesting refactoring technique, which I have not seen before. Some search engineing did not find suitable hits. Obviously it is entirely possible that this is a known refactoring but I don't know its name. In any case, here's my version of it.
The problem
Suppose you have a Python class with the following snippet
class Something:
def __init__(self):
self.store = {}
Basically you have a dictionary as a member variable. This is then used all around the class that grows and grows. Then you either find a bug in how the dict is used or you want to add some functionality like, to pick an arbitrary requirement, all keys for this object that are strings, must begin with "s_".
Now you have a problem because you need to do arbitrary changes all around the code. You can't easily debug this. You can't add a breakpoint inside this specific dictionary's setter function (or maybe Python's debugger can do that but I don't know how to do that). Reading code that massages dictionaries directly is tricky, because it's all brackets and open code rather than calling named methods like do_operation_x.
The solution, step one
Create a Python class that looks like this:
class MeaningfulName:
def __init__(self, *args, **kwargs):
self.d = dict(*args, **kwargs)
def contains(self, key):
return key in self.d
def __getitem__(self, key):
return self.d[key]
def __setitem__(self, key, value):
self.d[key] = value
...
Basically you implement all the special methods that do nothing else than forward to the underlying dictionary. Then replace the self.store dictionary with this object. Nothing should have changed. Run tests to make sure. Then commit this to main. Let it sit in the code base for a while in case there are untested code paths that use functionality that you did not write.
Just doing this gives an advantage: it is easy to add breakpoints to methods that mutate the objects's state.
Step two
Pick any of the special dunder methods and rename it to a more meaningful name. Add validation code if you need. Run tests. Fix all errors by rewriting the calling code to use the new named method. Some methods might need to be replaced with multiple new methods that do slightly different things. For example you might want to add methods like set_value and update_if_changed.
Step three
Repeat step two until all dunder methods are gone.
Last I mentioned I was doing an ABI cleanup of libspelling as it was really just a couple hour hack that got used by people.
Part of that was to make way for performing spellchecking off the GTK thread. That just landed in today’s libspelling 0.3.0 release. In one contrived benchmark, spellchecking a 10,000 line document was 8x faster. YMMV.
One of the reasons it’s faster is we don’t need to use the GtkTextIter API. Pango provides all the things we need to do that without the PangoLayout overhead. So we just skip right past that and implement iterators off-thread.
I also extracted the GtkTextBuffer adapter so that we may add a GtkEditable adapter in the future. Internally, the adapters provide a series of callbacks to the SpellingEngine.
You might wonder what happens if you make an edit that collides with a region being spellchecked off thread? The answer is quite simple, it checks for collisions and either adapts by adjusting offsets or discards irreconcilable collisions.
Both Text Editor and Builder have been migrated to using libspelling instead of its ancestor code. If you’re running nightly Flatpak of those, take a crack at it and report bugs.
We all think we’re smart enough to not be tricked by a phishing attempt, right? Unfortunately, I know for certain that I’m not, because I entered my GitHub password into a lookalike phishing website a year or two ago. Oops! Fortunately, I noticed right away, so I simply changed my unique, never-reused password and moved on. But if the attacker were smarter, I might have never noticed. (This particular attack website was relatively unsophisticated and proxied only an unauthenticated view of GitHub, a big clue that something was wrong. Update: I want to be clear that it would have been very easy for the attacker to simply redirect me to the real github.com after stealing my credentials, in which case I would not have noticed the attack and would not have known to change my password.)
You might think multifactor authentication is the best defense against phishing. Nope. Although multifactor authentication is a major security improvement over passwords alone, and the particular attack that tricked me did not attempt to subvert multifactor authentication, it’s actually unfortunately pretty easy for phishers to defeat most multifactor authentication if they wish to do so:
Multifactor authentication based on phone calls is also insecure (because SIM swapping isn’t going away; determined attackers will steal your phone number if it’s an obstacle to them)
Multifactor authentication based on authenticator apps (using TOTP or HOTP) is much better in general, but still fails against phishing. When you paste your one-time access code into a phishing website, the phishing website can simply “proxy” the access code you kindly provided to them by submitting it to the real website. This only allows authenticating once, but once is usually enough.
Fortunately, there is a solution: passkeys. Based on FIDO2 and WebAuthn, passkeys resist phishing because the authentication process depends on the domain of the service that you’re actually connecting to. If you think you’re visiting https://example.com, but you’re actually visiting a copycat website with a Cyrillic а instead of Latin a, then no worries: the authentication will fail, and the frustrated attacker will have achieved nothing.
The most popular form of passkey is local biometric authentication running on your phone, but any hardware security key (e.g. YubiKey) is also a good bet.
target.com Is More Secure than Your Bank!
I am not joking when I say that target.com is more secure than your bank (which is probably still relying on SMS or phone calls, and maybe even allows you to authenticate using easily-guessable security questions):
target.com is introducing passkeys!
Good job for supporting passkeys, Target.
It’s probably perfectly fine for Target to support passkeys alongside passwords indefinitely. Higher-security websites that want to resist phishing (e.g. your employer’s SSO service) should consider eventually allowing only passkeys.
No Passkeys in WebKitGTK
Unfortunately for GNOME users, WebKitGTK does not yet support WebAuthn, so passkeys will not work in GNOME Web (Epiphany). That’s my browser of choice, so I’ve never touched a passkey before and don’t actually know how well they work in practice. Maybe do as I say and not as I do? If you require high security, you will unfortunately need to use Firefox or Chrome instead, at least for the time being.
Why Was Michael Visiting a Fake github.com?
The fake github.com appeared higher than the real github.com in the DuckDuckGo search results for whatever I was looking for at the time. :(
As part of my effort to reduce Mutter dependencies, I finally found some time to focus on removing Cairo now that we can have a Wayland only build. Since the majority of the remaining usages are related to fonts, we would need to move CoglPango to be part of GNOME Shell's libst.
Merging Cally
Moving CoglPango also necessitates moving ClutterText and its corresponding accessibility implementation object, CallyText. The accessibility implementation is an object that inherits from AtkObject and can implement various interfaces to describe the type it corresponds to.
Once you initialize accessibility inside Mutter, the first step is to set up the AtkUtil implementation, which overrides the get_root and returns a CallyRoot instance. The remaining types register an AtkObjectFactory using the provided macros.
Allowing Atk to know which type to instantiate in case it encounters a CLUTTER_TYPE_CLONE, for example. That usually happens when you call atk_gobject_accessible_for_object(actor), which creates an instance of CallyActor using cally_actor_new. Although, the factories are nice, they don't allow out-of-tree AtkObject implementations. But wait, how is GNOME Shell accessible then?
Inside GNOME Shell StWidget, the widget abstraction, overrides the ClutterActorClass.get_accessible virtual function and replaces the default behavior that would call atk_gobject_accessible_for_object to:
Getting the corresponding accessibility type GType from another StWidgetClass.get_accessible_type virtual function
Create an instance of that type using g_object_new (T->get_accessible_type())
Call atk_object_initialize on it
Keep a weak reference to it
After "upstreaming" both StWidgetClass.get_accessible_type and their override of ClutterActorClass.get_accessible to become the default behavior, I managed to completely get rid of the factories. The remainder of the migration was mostly renaming things and trying to emit accessibility state changes when the Clutter type updated its state where possible.
Now that my branches of Mutter and GNOME Shell are working, I decided to test whether the reshuffling I did has caused any regressions. Unfortunately, Accerciser from the Fedora Rawhide repositories doesn't start due to a Python forward compatibility issue.
I remembered that Georges Basile Stavracas Neto started a re-implementation in C, called Elevado, so between fixing Accerciser or adding some missing features I wanted in Elevado, the choice was easy.
Current accessibility tree
And few hours later, I had managed to add some meaningful accessible names to various objects.
Accessibility tree from local development
Now that I have confirmed that my changes didn't break any obvious thing, my weekend fun is over.
The Freedesktop.org Specifications directory contains a list of common specifications that have accumulated over the decades and define how common desktop environment functionality works. The specifications are designed to increase interoperability between desktops. Common specifications make the life of both desktop-environment developers and especially application developers (who will almost always want to maximize the amount of Linux DEs their app can run on and behave as expected, to increase their apps target audience) a lot easier.
Unfortunately, building the HTML specifications and maintaining the directory of available specs has become a bit of a difficult chore, as the pipeline for building the site has become fairly old and unmaintained (parts of it still depended on Python 2). In order to make my life of maintaining this part of Freedesktop easier, I aimed to carefully modernize the website. I do have bigger plans to maybe eventually restructure the site to make it easier to navigate and not just a plain alphabetical list of specifications, and to integrate it with the Wiki, but in the interest of backwards compatibility and to get anything done in time (rather than taking on a mega-project that can’t be finished), I decided to just do the minimum modernization first to get a viable website, and do the rest later.
So, long story short: Most Freedesktop specs are written in DocBook XML. Some were plain HTML documents, some were DocBook SGML, a few were plaintext files. To make things easier to maintain, almost every specification is written in DocBook now. This also simplifies the review process and we may be able to switch to something else like AsciiDoc later if we want to. Of course, one could have switched to something else than DocBook, but that would have been a much bigger chore with a lot more broken links, and I did not want this to become an even bigger project than it already was and keep its scope somewhat narrow.
DocBook is a markup language for documentation which has been around for a very long time, and therefore has older tooling around it. But fortunately our friends at openSUSE created DAPS (DocBook Authoring and Publishing Suite) as a modern way to render DocBook documents to HTML and other file formats. DAPS is now used to generate all Freedesktop specifications on our website. The website index and the specification revisions are also now defined in structured TOML files, to make them easier to read and to extend. A bunch of specifications that had been missing from the original website are also added to the index and rendered on the website now.
Originally, I wanted to put the website live in a temporary location and solicit feedback, especially since some links have changed and not everything may have redirects. However, due to how GitLab Pages worked (and due to me not knowing GitLab CI well enough…) the changes went live before their MR was actually merged. Rather than reverting the change, I decided to keep it (as the old website did not build properly anymore) and to see if anything breaks. So far, no dead links or bad side effects have been observed, but:
If you notice any broken link to specifications.fd.o or anything else weird, please file a bug so that we can fix it!
Thank you, and I hope you enjoy reading the specifications in better rendering and more coherent look!
Ptyxis has arrived in Fedora 40 thanks to my Red Hat colleague Nieves Montero Fernandez. You can sudo dnf install ptyxis to get it.
Discussions are ongoing to make Ptyxis the default terminal for Fedora Workstation (and Silverblue) though it has already been approved by FESCo. One benefit of this is that we can finally remove the downstream patches to gnome-terminal and return it to a more upstream experience.
Some distributions are configuring it with -Dgeneric=terminal which means it will look like a regular ol’ terminal without the Ptyxis name or branding. In fact it uses the previous “prompt” iconography.
If you ship GNOME on your distribution and want help switching to Ptyxis, feel free to reach out.
One of the most recent features I worked on in this GSoC project was to add error highlighting onto the editor if the sparql server returns an error. Parser errors are designed to be returned alongside a byte number where the error originates, so surely that wouldn’t be too difficult right? Well it turned out to involve a bit more engineering than expected.
To start off, the editor we built using CodeMirror cannot be styled directly using CSS, as the styling will not persist over changes in the editor state (e.g. when you start typing or change the position of the cursor). The way this is intended to be done with this package is to use a Decoration object instead. There are different types of decorations including lines and marks, and using the range() method on the Decoration object I would be able to introduce a persisting decoration for a certain byte range in the editor text.
For example, if there is an error on the line starting from byte 16, the following code will produce the decoration I need to highlight the entire line in red:
const errorLine = new Decoration.line({
class: "redBackground"
}).range(16);
However, this is not enough as the decoration needs to be dispatched to the existing editor view, and the documentations weren’t very clear on how to do this. Luckily, after taking some time to scrutinise this page, I managed to pick out what I need.
There were 2 more key objects I needed from the CodeMirror library: a StateEffect to be dispatched as trigger and a StateField to monitor the effects, producing the error highlights when necessary. The complete code ended up looking something like this:
const errorLine = Decoration.mark({
class: "error-line", // this class gives a red underline
inclusiveEnd: true
});
const errorLineEffect = StateEffect.define<{ pos: number }>();
const errorLineField = StateField.define<DecorationSet>({
create() {
return Decoration.none;
},
update(field, tr) {
field = field.map(tr.changes);
let errorLineTriggered = false;
for (let e of tr.effects) if (e.is(errorLineEffect)) {
const charAtErrorLine = view.state.doc.toString()[e.value.from - 1];
// the code below adds the errorLine decoration to the correct positions
if (charAtErrorLine == "\n") {
field = field.update({
add: [errorLine.range(realErrorPos - 1, realErrorPos)]
});
} else {
field = field.update({
add: [errorLine.range(realErrorPos, realErrorPos + 1)]
});
}
errorLineTriggered = true;
}
// remove the decoration once editor text changed as error may no longer exist/may now be in a different position
if (tr.docChanged && !errorLineTriggered)
{
field = field.update({
filter: (f, t, value) => !value.eq(errorLine)
})
}
return field;
},
provide: f => EditorView.decorations.from(f)
})
// function to trigger errorLine application
export function setErrorLine (pos: number) {
let effects: StateEffect<unknown>[] = [ errorLineEffect.of({ pos }) ];
if (!view.state.field(errorLineField, false))
effects.push(StateEffect.appendConfig.of([errorLineField]))
view.dispatch({ effects });
}
And this works as intended, producing the following in the the interface:
But there was one final problem, this does not work for strings of all encoding, e.g. a query like this: select ('👩🏿🦱👩🏽🦳' AS ?str) { asdf } will trigger an error response at the wrong position. Luckily, this was a relatively simple fix using TextEncoder/Decoder in JS, as demonstrated with our example query below:
Good news: exploiting memory safety vulnerabilities is becoming more difficult. Traditional security vulnerabilities will remain a serious threat, but attackers prefer to take the path of least resistance, and nowadays that is to attack developers rather than the software itself. Once the attackers control your computer, they can attempt to perform a supply chain attack and insert backdoors into your software, compromising all of your users at once.
If you’re a software developer, it’s time to start focusing on the possibility that attackers will target you personally. Yes, you. If you use Linux, macOS, or Windows, take a moment to check your home directory for a hidden .n2 folder. If it exists, alas! You have been hacked by the North Koreans. (Future malware campaigns will presumably be more stealthy than this.)
Attackers who target developers are currently employing two common strategies:
Fake job interview: you’re applying to job postings and the recruiter asks you to solve a programming problem of some sort, which involves installing software from NPM (or PyPI, or another language ecosystem’s package manager).
Fake debugging request: you receive a bug report and the reporter helpfully provides a script for reproducing the bug. The script may have dependencies on packages in NPM (or PyPI, or another language ecosystem’s package manager) to make it harder to notice that it’s malware. I saw a hopefully innocent bug report that was indistinguishable from such an attack just last week.
But of course you would never execute such a script without auditing the dependencies thoroughly! (Insert eyeroll emoji here.)
By now, most of us are hopefully already aware of typosquatting attacks, and the high risk that untrustworthy programming language packages may be malicious. But you might not have considered that attackers might target you personally. Exercise caution whenever somebody asks you to install packages from these sources. Take the time to start a virtual machine before running the code. (An isolated podman container probably works too?) Remember, attackers will target the path of least resistance. Virtualization or containerization raises the bar considerably, and the attacker will probably move along to an easier target.
In a previous post I gave the context for my pet project ieee1275-rs, it is a framework to build bootable ELF payloads on Open Firmware (IEEE 1275). OF is a standard developed by Sun for SPARC and aimed to provide a standardized firmware interface that was rich and nice to work with, it was later adopted by IBM, Apple for POWER and even the OLPC XO.
The crate is intended to provide a similar set of facilities as uefi-rs, that is, an abstraction over the entry point and the interfaces. I started the ieee1275-rs crate specifically for IBM’s POWER platforms, although if people want to provide support for SPARC, G3/4/5s and the OLPC XO I would welcome contributions.
There are several ways the firmware takes a payload to boot, in Fedora we use a PReP partition type, which is a ~4MB partition labeld with the 41h type in MBR or 9E1A2D38-C612-4316-AA26-8B49521E5A8B as the GUID in the GPT table. The ELF is written as raw data in the partition.
Another alternative is a so called CHRP script in “ppc/bootinfo.txt”, this script can load an ELF located in the same filesystem, this is what the bootable CD/DVD installer uses. I have yet to test whether this is something that can be used across Open Firmware implementations.
To avoid compatibility issues, the ELF payload has to be compiled as a 32bit big-endian binary as the firmware interface would often assume that endianness and address size.
The entry point
As I entered this problem I had some experience writing UEFI binaries, the entry point in UEFI looks like this:
#![no_main]
#![no_std]
use uefi::prelude::*;
#[entry]
fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
uefi::helpers::init(&mut system_table).unwrap();
system_table.boot_services().stall(10_000_000);
Status::SUCCESS
}
Basically you get a pointer to a table of functions, and that’s how you ask the firmware to perform system functions for you. I thought that maybe Open Firmware did something similar, so I had a look at how GRUB does this and it used a ppc assembler snippet that jumps to grub_ieee1275_entry_fn(), yaboot does a similar thing. I was already grumbling of having to look into how to embed an asm binary to my Rust project. But turns out this snippet conforms to the PPC function calling convention, and since those snippets mostly take care of zeroing the BSS segment but turns out the ELF Rust outputs does not generate one (although I am not sure this means there isn’t a runtime one, I need to investigate this further), I decided to just create a small ppc32be ELF binary with the start function into the top of the .text section at address 0x10000.
I have created a repository with the most basic setup that you can run. With some cargo configuration to get the right linking options, and a script to create the disk image with the ELF payload on the PReP partition and run qemu, we can get this source code being run by Open Firmware:
Provided we have already created the disk image (check the run_qemu.sh script for more details), we can run our code by executing the following commands:
$ cargo +nightly build --release --target powerpc-unknown-linux-gnu
$ dd if=target/powerpc-unknown-linux-gnu/release/openfirmware-basic-entry of=disk.img bs=512 seek=2048 conv=notrunc
$ qemu-system-ppc64 -M pseries -m 512 --drive file=disk.img
[...]
Welcome to Open Firmware
Copyright (c) 2004, 2017 IBM Corporation All rights reserved.
This program and the accompanying materials are made available
under the terms of the BSD License available at
http://www.opensource.org/licenses/bsd-license.php
Trying to load: from: /vdevice/v-scsi@71000003/disk@8000000000000000 ... Successfully loaded
Ta da! The wonders of getting your firmware to run an infinite loop. Here’s where the fun begins.
Doing something actually useful
Now, to complete the hello world, we need to do something useful. Remeber our _entry argument in the _start() function? That’s our gateway to the firmware functionality. Let’s look at how the IEEE1275 spec tells us how we can work with it.
This function is a universal entry point that takes a structure as an argument that tells the firmware what to run, depending on the function it expects some extra arguments attached. Let’s look at how we can at least print “Hello World!” on the firmware console.
The basic structure looks like this:
#[repr(C)]
pub struct Args {
pub service: *const u8, // null terminated ascii string representing the name of the service call
pub nargs: usize, // number of arguments
pub nret: usize, // number of return values
}
This is just the header of every possible call, nargs and nret determine the size of the memory of the entire argument payload. Let’s look at an an example to just exit the program:
#[no_mangle]
#[link_section = ".text"]
extern "C" fn _start(_r3: usize, _r4: usize, entry: extern "C" fn(*mut Args) -> usize) -> isize {
let mut args = Args {
service: "exit\0".as_ptr(),
nargs: 0,
nret: 0
};
entry (&mut args as *mut Args);
0 // The program will exit in the line before, we return 0 to satisfy the compiler
}
When we run it in qemu we get the following output:
To summarize, we’ve learned that we don’t really need assembly code to produce an entry point to our OF bootloader (tho we need to zero our bss segment if we have one), we’ve learned how to build a valid OF ELF for the PPC architecture and how to call a basic firmware service.
In a follow up post I intend to show a hello world text output and how the ieee1275 crate helps to abstract away most of the grunt to access common firmware services. Stay tuned!
I have been doing random coding experiments with my spare time that I never got to publicize much outside of my inner circles. I thought I would undust my blog a bit to talk about what I did in case it is useful for others.
For some background, I used to manage the bootloader team at Red Hat a few years ago alongside Peter Jones and Javier Martinez. I learned a great deal from them and I fell in love with this particular problem space and I have come to enjoy tinkering with experiments in this space.
There many open challenges in this space that we could use to have a more robust bootpath across Linux distros, from boot attestation for initramfs and cmdline, A/B rollbacks, TPM LUKS decryption (ala BitLocker)…
One that particularly interests me is unifying the firmware-kernel boot interface across implementations in the hypothetical absence of GRUB.
Context: the issue with GRUB
The priority of the team was to support RHEL boot path on all the architectures we supported. Namely x86_64 (legacy BIOS & UEFI), aarch64 (UEFI), s390x and ppc64le (Open Power and PowerVM).
These are extremely heterogeneous firmware interfaces, some are on their way to extinction (legacy PC BIOS) and some will remain weird for a while.
GRUB, (GRand Unified Bootloader) as it names stands, intends to be a unified bootloader for all platforms. GRUB has to support a supersetq of firmware interfaces, some of those, like legacy BIOS do not support much other than some rudimentary support disk or network access and basic graphics handling.
To get to load a kernel and its initramfs, this means that GRUB has to implement basic drivers for storage, networking, TCP/IP, filesystems, volume management… every time there is a new device storage technology, we need to implement a driver twice, once in the kernel and once in GRUB itself. GRUB is, for all intent and purposes, an entire operating system that has to be maintained.
The maintenance burden is actually quite big, and recently it has been a target for the InfoSec community after the Boot Hole vulnerability. GRUB is implemented in C and it is an extremely complex code base and not as well staffed as it should. It implements its own scripting language (parser et al) and it is clear there are quite a few CVEs lurking in there.
So, we are basically maintaining code we already have to write, test and maintain in the Linux kernel in a different OS whose whole purposes (in the context of RHEL, CentOS and Fedora) its main job is to boot a Linux kernel.
This realization led to the initiative that these days are taking shape in the discussions around nmbl (no more boot loader). You can read more about that in that blog post, I am not actively participating in that effort but I encourage you to read about it. I do want to focus on something else and very specific, which is what you do before you load the nmble kernel.
Booting from disk
I want to focus on the code that goes from the firmware interface to loading the kernel (nmbl or otherwise) from disk. We want some sort of A/B boot protocol that is somewhat normalized across the platforms we support, we need to pick the kernel from the disk.
The systemd community has led some of the boot modernization initiatives, vocally supporting the adoption of UKI and signed pre-built initarmfs images, developing the Boot Loader Spec, and other efforts.
At some point I heard Lennart making the point that we should standardize on using the EFI System Partition as /boot to place the kernel as most firmware implementations know how to talk to a FAT partition.
This proposal caught my attention and I have been pondering if we could have a relatively small codebase written in a safe language (you know which) that could support a well define protocol for A/B booting a kernel in Legacy BIOS, S390 and OpenFirmware (UEFI and Open Power already support BLS snippets so we are covered there).
My modest inroad into testing this hypothesis so far has been the development of ieee1275-rs, a Rust module to write programs for the Open Firmware interface, so far I have not been able to load a kernel by myself but I still think the lessons learned and some of the code could be useful to others. Please note this is a personal experiment and nothing Red Hat is officially working on.
I will be writing more about the technical details of this crate in a follow up blog post where I get into some of the details of writing Rust code for a firmware interface, this post is long enough already. Stay tuned.
When you stop and think about it, user interfaces are almost entirely designed around sight: they display graphical elements that are mostly interacted with by pointing and clicking (or touching). However, as we know, not everyone has “perfect” vision: some are color blind, some are short sighted, some are long sighted, etc. In many cases, these people can still interact with the same user interfaces as everyone else, but those with more severe visual impairments need a different method to use their computers.
That’s when the Screen Reader comes in! The Screen Reader is a software that adapts computers to the needs of users with low vision or blindness, by reading descriptions of user interfaces out loud and facilitating keyboard navigation.
Today, we will explore the web with Orca, the Screen Reader of the Linux Desktop! After that, we will contribute towards improving the experience of Orca users.
Mind you that Screen Readers have tons of advanced features to empower users with as much efficiency as possible. Because of that, it can be challenging to use this kind of software if you are not used to it. I invite you to embrace this challenge as an opportunity to experience the desktop from a different perspective, and to appreciate the way other people user their computers.
Without further ado, let’s get going!
Part I - Exploring the Screen Reader
Enabling and Disabling Orca
You can enable and disable Orca by pressing Super + Alt + s. This can also be configured via GNOME Settings, under “Accessibility” > “Seeing” > “Screen Reader”.
Turn up and the volume and make sure you hear a robotic voice saying “Screen Reader on” and “Screen Reader off”. Then, open the Firefox web browser and check if Orca describes the current open tab. If it is quiet, try closing and re-opening Firefox again or disabling and re-enabling Orca.
Controlling Orca
Orca is controlled entirely from the keyboard, having dozens of shortcuts. Besides, these keyboard shortcuts are slightly different if you have a Number Pad (NumPad) or not. I have laid out the most important ones below, but an exhaustive list can be found in the quite excellent Orca documentation.
Orca Modifier
Key
Without a NumPad (Laptop)
With a NumPad (Desktop)
Orca Modifier
Caps Lock
NumPad Ins
Important shortcuts
Action
Without a NumPad
With a NumPad
Toggle screen reader
Super + Alt + s
Super + Alt + s
Interrupt screen reading
Orca Modifier (press)
Orca Modifier (press)
Toggle caret mode in Firefox
F7
F7
Where am I
NumPad Enter
Orca Modifier + Enter
Display page structure
Alt + Shift + h
Alt + Shift + h
Display page buttons
Alt + Shift + b
Alt + Shift + b
Read current line
NumPad 8
Orca Modifier + i
Read current word
NumPad 5
Orca Modifier + k
Read onward
NumPad +
Orca Modifier + ;
Next link
k
k
Previous link
Shift + k
Shift + k
Next paragraph
p
p
Previous paragraph
Shift + p
Shift + p
Next button
p
p
Previous paragraph
Shift + p
Shift + p
Activity: Exploring the web with Orca
Open the Firefox web browser and head to https://flathub.org. Find an app that looks interesting to you and explore its page using Orca. Try to navigate the entire page!
Feel free to use the questions below as a guide to your exploration:
Can you read the app description? Try to do it twice.
Can you click the “Install” button? What happens when you do?
Can you figure out if the app is verified or not? How so?
Can you guess what the screenshots of the app are showing?
Can you find the link to the source code of the app?
Part II - Captioning Screenshots in Flathub
Flatpak and Flathub
Flatpak is a modern packaging technology, which aims to work on all Linux distributions. In turn, Flathub is an app store for software that was packaged using Flatpak. Flathub has been embraced as the main channel for publishing applications by many projects, most notably GNOME!
App listings
Each app that is published on Flathub has a page for itself, which is called “app listing”. You will find lots of important information about an app on its listing, such as:
Name
Description
Author
License
Permissions
Screenshots
All this information is sourced from a special file called “MetaInfo File”, that is typically hosted on the app’s repository. Its filename usually ends with .metainfo.xml.in.in. You can click here to see an example of a MetaInfo File. You can read more about this file format on Flathub’s MetaInfo Guidelines.
Screenshot captions
To promote high-quality listings, Flathub publishes its own Quality Guidelines.. These guidelines encourage maintainer’s to add captions to their images, which, as we learned in the first activity, helps people who use Screen Readers understand the content of the screenshots of the app. To quote the Quality Guidelines:
Every screenshot should have a caption briefly describing it. Captions should only be one sentence and not end with a full stop. Don’t start them with a number.
To be more precise, good captions should clearly convey the functionality demonstrated in the screenshot, as well as give a general idea of the user interface on display. I have cataloged dozens of Flathub listings on this Google Sheet:
There you find listings with Caption status set “Exemplary”, which are great models of how captions should be written.
Activity: Contributing with captions
Now, we will improve the accessibility of the Linux Desktop (albeit slightly), by contributing to Flathub with screenshot captioning. To do that, follow the instructions laid out below:
Find the repository of the app (usually under “Browser the source code” on “Links”)
Log in or create an account on the platform that hosts the repository of the app (GitHub, GitLab, etc)
Go back to the repository of the app, and find its MetaInfo File. It is typically placed in a data/ directory, at the root of the repository.
On the MetaInfo File, look for the screenshot tags and find the files referenced in it
Inside each screenshot element, there is an image element. For each of those, create a new line with caption tags. The caption goes in between them, like so:
<screenshot>
<image>https://raw.githubusercontent.com/SeaDve/Kooha/main/data/screenshots/screenshot2.png</image>
+ <caption>In-progress recording duration and button to stop recording</caption>
</screenshot>
When you are done writing your captions, proof read them and run them through a spellchecker. I haven’t found any great ones online, but Language Tool’s is passable.
Commit your changes, following the commit message conventions of the project you are contributing to. If this convention is not documented anywhere, try to infer it looking at the commit history.
Open a Pull Request (PR) / Merge Request (MR) with your contribution, explaining what you did and why it is important. You can use my explanation as a model.
Put a link to your PR/MR on the sheet.
Wait for the maintainer’s feedback
Part III: Farewell
Thank you for attending this workshop. I hope you had a good time!