July 26, 2024

Booting with Rust: Chapter 2

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:

#![no_std]
#![no_main]

use core::{panic::PanicInfo, ffi::c_void};

#[panic_handler]
fn _handler (_info: &PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
#[link_section = ".text"]
extern "C" fn _start(_r3: usize, _r4: usize, _entry: extern "C" fn(*mut c_void) -> usize) -> isize {
    loop {}
}

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:

Trying to load:  from: /vdevice/v-scsi@71000003/disk@8000000000000000 ...   Successfully loaded
W3411: Client application returned.

Aha! We successfully called firmware code!

To be continued…

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!

#158 Mini Edition

Update on what happened across the GNOME project in the week from July 19 to July 26.

GNOME Core Apps and Libraries

Calendar

A simple calendar application.

Hari Rana | TheEvilSkeleton reports

GNOME Calendar users, rejoice! After 7 months of pain and suffering, we finally reworked the event details popover, which will be available in GNOME 47!

The new event details popover builds on top of the existing UI/code, while adding a few improvements and behavioral changes:

  • The popover displays the changes-prevent (lock) icon when the event is read-only.
  • Each section is properly separated with separators, with equal amount of margins.
  • Detected video meeting links no longer appear in both the location and meeting sections, but only in the meeting section.
  • When an event has no event note, the popover will always explicitly display that there’s no event description.
  • The action button adapts its icon and tooltip text depending on the event permission.

GNOME Circle Apps and Libraries

Railway

Travel with all your train information in one place.

schmiddi reports

Railway version 2.7.0 was released. This includes an improved dialog listing the providers, which now highlights the currently selected provider and furthermore separates the providers by region. One can now furthermore search for journeys by arrival time (took only 9 months for it to get merged). This release also contains minor fixes with some providers.

Third Party Projects

pieterdd says

Rclone Shuttle is a libadwaita GUI for rclone to interact with a wide variety of cloud storage providers. The latest update (0.1.4) adds support for downloading a local copy of certain files/folders.

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!

July 25, 2024

Booting with Rust: Chapter 1

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.

July 24, 2024

whippet progress update: funding, features, future

Greets greets! Today, an update on recent progress in Whippet, including sponsorship, a new collector, and a new feature.

the lob, the pitch

But first, a reminder of what the haps: Whippet is a garbage collector library. The target audience is language run-time authors, particularly “small” run-times: wasm2c, Guile, OCaml, and so on; to a first approximation, the kinds of projects that currently use the Boehm-Demers-Weiser collector.

The pitch is that if you use Whippet, you get a low-fuss small dependency to vendor into your source tree that offers you access to a choice of advanced garbage collectors: not just the conservative mark-sweep collector from BDW-GC, but also copying collectors, an Immix-derived collector, generational collectors, and so on. You can choose the GC that fits your problem domain, like Java people have done for many years. The Whippet API is designed to be a no-overhead abstraction that decouples your language run-time from the specific choice of GC.

I co-maintain Guile and will be working on integrating Whippet in the next months, and have high hopes for success.

bridgeroos!

I’m delighted to share that Whippet was granted financial support from the European Union via the NGI zero core fund, administered by the Dutch non-profit, NLnet foundation. See the NLnet project page for the overview.

This funding allows me to devote time to Whippet to bring it from proof-of-concept to production. I’ll finish the missing features, spend some time adding tracing support, measuring performance, and sanding off any rough edges, then work on integrating Whippet into Guile.

This bloggery is a first update of the progress of the funded NLnet project.

a new collector!

I landed a new collector a couple weeks ago, a parallel copying collector (PCC). It’s like a semi-space collector, in that it always evacuates objects (except large objects, which are managed in their own space). However instead of having a single global bump-pointer allocation region, it breaks the heap into 64-kB blocks. In this way it supports multiple mutator threads: mutators do local bump-pointer allocation into their own block, and when their block is full, they fetch another from the global store.

The block size is 64 kB, but really it’s 128 kB, because each block has two halves: the active region and the copy reserve. It’s a copying collector, after all. Dividing each block in two allows me to easily grow and shrink the heap while ensuring there is always enough reserve space.

Blocks are allocated in 64-MB aligned slabs, so you get 512 blocks in a slab. The first block in a slab is used by the collector itself, to keep metadata for the rest of the blocks, for example a chain pointer allowing blocks to be collected in lists, a saved allocation pointer for partially-filled blocks, whether the block is paged in or out, and so on.

The PCC not only supports parallel mutators, it can also trace in parallel. This mechanism works somewhat like allocation, in which multiple trace workers compete to evacuate objects into their local allocation buffers; when an allocation buffer is full, the trace worker grabs another, just like mutators do.

However, unlike the simple semi-space collector which uses a Cheney grey worklist, the PCC uses the fine-grained work-stealing parallel tracer originally developed for Whippet’s Immix-like collector. Each trace worker maintains a local queue of objects that need tracing, which currently has 1024 entries. If the local queue becomes full, the worker will publish 3/4 of those entries to the worker’s shared worklist. When a worker runs out of local work, it will first try to remove work from its own shared worklist, then will try to steal from other workers.

Of course, because threads compete to evacuate objects, we have to use atomic compare-and-swap instead of simple forwarding pointer updates; if you only have one mutator thread and are OK with just one tracing thread, you can avoid the ~30% performance penalty that atomic operations impose. The PCC generally starts to win over a semi-space collector when it can trace with 2 threads, and gets better with each thread you add.

I sometimes wonder whether the worklist should contain grey edges or grey objects. MMTk seems to do the former, and bundles edges into work packets, which are the unit of work sharing. I don’t know yet what is best and look forward to experimenting once I have better benchmarks.

Anyway, maintaining an external worklist is cheating in a way: unlike the Cheney worklist, this memory is not accounted for as part of the heap size. If you are targetting a microcontroller or something, probably you need to choose a different kind of collector. Fortunately, Whippet enables this kind of choice, as it contains a number of collector implementations.

What about benchmarks? Well, I’ll be doing those soon in a more rigorous way. For now I will just say that it seems to behave as expected and I am satisfied; it is useful to have a performance oracle against which to compare other collectors.

finalizers!

This week I landed support for finalizers!

Finalizers work in all the collectors: semi, pcc, whippet, and the BDW collector that is a shim to use BDW-GC behind the Whippet API. They have a well-defined relationship with ephemerons and are multi-priority, allowing embedders to build guardians or phantom references on top.

In the future I should do some more work to make finalizers support generations, if the collector is generational, allowing a minor collection to avoid visiting finalizers for old objects. But this is a straightforward extension that will happen at some point.

future!

And that’s the end of this update. Next up, I am finally going to tackle heap resizing, using the membalancer approach. Then basic Windows and Mac support, then I move on to the tracing and performance measurement phase. Onwards and upwards!

July 23, 2024

Accessibility Hackathon

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”.

&ldquo;Seeing&rdquo; panel within GNOME Settings

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:

  1. Can you read the app description? Try to do it twice.
  2. Can you click the “Install” button? What happens when you do?
  3. Can you figure out if the app is verified or not? How so?
  4. Can you guess what the screenshots of the app are showing?
  5. 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:

GUADEC 2024 - Accessibility Hackathon

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:

  1. Open the Google Sheet on this link
  2. Choose an app with Caption status set “Absent”
  3. Assign “In-progress” to its Caption status
  4. Put your name(s) on its Group column
  5. Head to the Flathub listing link of the app
  6. Find the repository of the app (usually under “Browser the source code” on “Links”)

    Flathub listing links tab

  7. Log in or create an account on the platform that hosts the repository of the app (GitHub, GitLab, etc)
  8. 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.
  9. On the MetaInfo File, look for the screenshot tags and find the files referenced in it
  10. 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>
  1. 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.
  2. 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.
  3. 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.
  4. Put a link to your PR/MR on the sheet.
  5. Wait for the maintainer’s feedback

Part III: Farewell

Thank you for attending this workshop. I hope you had a good time!

Until the next one!

July 22, 2024

Renaming multiple files

After World War II, Jack Kirby and his partner Joe Simon began their first foray into the genre of crime comics. (Kirby would return to the topic briefly in 1954 and 1971.) Beginning in 1947 and tailing into 1951, the stories appeared largely in Headline Comics and Justice Traps the Guilty for Crestwood’s Prize Publications. In 2011, Titan Books published a selection of these stories in hardcover, but sixty percent of the stories from this time period aren’t included in the book, and at least 20 stories have never been reprinted. Unlike Simon & Kirby’s much more prolific romance offerings, all of the comics in question are in the public domain and available on Digital Comic Museum and Comic Book Plus sites, thanks to multiple volunteers. I set about creating my own collection of scanned pages.

When the downloaded .cbz files are extracted into a folder, the resulting image files have names like scan00.jpg, scan01.jpg, etc. In GNOME Files, selecting all the files for a given issue and pressing F2 brings up the batch rename dialogue.

Selecting the Find and replace text option, I replace “scan” with the book title and issue number, with “p” as a separator for the page number.

When all the stories have been added, the pages will be sorted by title and issue number. To enable sorting chronologically, a four- or six-digit prefix can be used to specify the cover date, in this case “4706” for June 1947. To add this I press F2 on the same selected files and use the Rename using a template option.

Using the Jack Kirby Checklist as a guideline, and discarding (very few) stories for insufficient Kirby content, my project yielded a folder containing 633 pages including covers.


Jack Kirby (1917-1994) was a pioneer in American comic books. He grew up on the Lower East Side of New York City (where he encountered real and aspiring gangsters first hand) and fought in northeastern France in 1944 as an infantryman in Patton’s Third Army. As a partner in Simon and Kirby and the manager of the S&K studio, Kirby defined the word cartoonist—he generally wrote, penciled, inked, and occasionally coloured his own stories.
Jack Kirby photo property of the Kirby Estate. Used with permission.

finalizers, guardians, phantom references, et cetera

Consider guardians. Compared to finalizers, in which the cleanup procedures are run concurrently with the mutator, by the garbage collector, guardians allow the mutator to control concurrency. See Guile’s documentation for more notes. Java’s PhantomReference / ReferenceQueue seems to be similar in spirit, though the details differ.

questions

If we want guardians, how should we implement them in Whippet? How do they relate to ephemerons and finalizers?

It would be a shame if guardians were primitive, as they are a relatively niche language feature. Guile has them, yes, but really what Guile has is bugs: because Guile implements guardians on top of BDW-GC’s finalizers (without topological ordering), all the other things that finalizers might do in Guile (e.g. closing file ports) might run at the same time as the objects protected by guardians. For the specific object being guarded, this isn’t so much of a problem, because when you put an object in the guardian, it arranges to prepend the guardian finalizer before any existing finalizer. But when a whole clique of objects becomes unreachable, objects referred to by the guarded object may be finalized. So the object you get back from the guardian might refer to, say, already-closed file ports.

The guardians-are-primitive solution is to add a special guardian pass to the collector that will identify unreachable guarded objects. In this way, the transitive closure of all guarded objects will be already visited by the time finalizables are computed, protecting them from finalization. This would be sufficient, but is it necessary?

answers?

Thinking more abstractly, perhaps we can solve this issue and others with a set of finalizer priorities: a collector could have, say, 10 finalizer priorities, and run the finalizer fixpoint once per priority, in order. If no finalizer is registered at a given priority, there is no overhead. A given embedder (Guile, say) could use priority 0 for guardians, priority 1 for “normal” finalizers, and ignore the other priorities. I think such a facility could also support other constructs, including Java’s phantom references, weak references, and reference queues, especially when combined with ephemerons.

Anyway, all this is a note for posterity. Are there other interesting mutator-exposed GC constructs that can’t be implemented with a combination of ephemerons and multi-priority finalizers? Do let me know!

July 19, 2024

Installing Nvidia Driver Will Be Easy Again in Fedora Workstation

The feature my team worked on – Nvidia Driver Installation with Secure Boot Supportwas approved by FESCo earlier this week and its upstream implementation was also approved several days ago, so it’s on its way to Fedora 41 and I decided to write a blog post with more context and our motivations behind it.

Installing the Nvidia drivers in Fedora Linux was not easy in the past. You had to add 3rd party repos and then install specific packages. Not very intuitive for beginners. That’s why we teamed up with the RPMFusion community which created a separate repository with the Nvidia driver that was enabled in Fedora Workstation if you agreed to enable third-party software sources. It also shipped AppStream metadata to integrate with app catalogs like GNOME Software. So all the user had to do was open GNOME Software, look up “nvidia”, and click to install it. Simple enough.

It only had one problem: it didn’t work with Secure Boot enabled. The next boot would simply fail if Secure Boot was enabled and the reason was not obvious for many users. It was not that significant when we came up with the solution, but it grew in significance as more and more machines had Secure Boot enabled.

The Fedora Workstation Working Group decided earlier this year that it would be better to remove the driver from GNOME Software given the fact that the current solution doesn’t work with Secure Boot. The repository remained among the approved third-party sources, but the user experience of installing the Nvidia driver was significantly degraded.

It’s really not something Fedora Workstation can afford because the Nvidia driver is more popular than ever in the AI craze. So we started thinking about a solution that would meet the criteria and work with Secure Boot. The most seamless solution would be to sign the module with the Fedora key, but that’s pretty much out of the question. Fedora wouldn’t sign a piece of closed source software from a third party repo.

So basically the only solution left is self-signing. It’s not ideal from the UX perspective. The user has to create a password for the machine owner key. The next time they boot, they have to go through several screens in terminal user interface of mokutil and enter the password. At such an early stage of the boot process the charset is pretty much limited to ASCII, so you can’t let the user use any other characters when creating the password in GNOME Software. But I think Milan Crha (devel) and Jakub Steiner (UX design), who worked on it, handled the problems pretty well.

The password is generated for the user.

When I was submitting the change, I was not expecting a lot of resistance. And if any, then questions about why we’re making proprietary software easily installable. But the biggest resistance was related to security. By enrolling a MOK, you allow all modules installed in the future to be signed by it as well.

I understand the security implications of it, but you’re already trusting any software from the package repository, you’ve enabled, with the root privileges anyway and the only other alternative is to disable Secure Boot completely which removes that security measure entirely. In addition, the solution with disabled Secure Boot has other problems: it is done differently on different computers, there is no single set of step-by-step instructions which we could give to all users. And they may not be able to disable Secure Boot at all.

On the other hand, we didn’t do a good job of informing users about the security implications in the original implementation and feedback from the community helped us come up with a better implementation with a reworked dialog. We’ve also added information about the security implications and an instruction how to remove the MOK when it’s no longer needed to the docs.

The approved version of the dialog.

So in Fedora Workstation 41, installing the Nvidia driver will be as easy as it can be within the constraints of Fedora policies. We still see this as a temporary solution for older Nvidia cards and until Nvidia rolls out its open source kernel module. Then, hopefully, this perennial pain for Linux users will finally be over.

#157 GUADEC 2024

Update on what happened across the GNOME project in the week from July 12 to July 19.

GUADEC Banner

GUADEC is already happening! You can find the complete timetable here.

Register on https://guadec.org to get all the details on how to join the event remotely.

GNOME Circle Apps and Libraries

Workbench

A sandbox to learn and prototype with GNOME technologies.

Sonny announces

After adding TypeScript support, Angelo made Workbench able to use TypeScript for JavaScript diagnostics and completions.

Authenticator

Simple application for generating Two-Factor Authentication Codes.

Bilal Elmoussaoui reports

I have released Authenticator 4.5.0. The release consists of the following:

  • backup: Add import of Raivo OTP files
  • qr scanner: Use back camera by default
  • search provider: Withdraw notification once the code is no longer valid
  • accounts: Refresh completion model when scanning from qr code
  • use latest libadwaita widgets

Without forgetting all the improvements brought to the camera widget through aperture library

Third Party Projects

Ronnie Nissan announces

This week I released Embellish, a simple nerd fonts manager, the features include:

  • A beautiful Libadwaita design
  • Download and install nerd fonts from a list
  • Update installed fonts
  • Uninstall fonts
  • Fuzzy search
  • A CLI/TUI companion app (embellish-tui) for scripting and/or people who prefer the terminal

This is my first ever gnome app (and desktop app) and I am happy to be joining the community. You can get Embellish on flathub

JumpLink reports

In preparation for GUADEC, we have released version 4.0.0-beta.7 of the TypeScript type definition generator ts-for-gir this friday evening.

With this version, it is now possible to generate types locally without package.json support. This was a highly requested feature from the GNOME community to avoid the need for NPM. You can now easily generate this types, for example, with:

npx @ts-for-gir/cli generate Adw-1.0

Additionally, we have released a preview version of the upcoming GNOME Shell 47 types here and on NPM. This should hopefully make it easier for GNOME Shell extension projects to prepare their extensions for the upcoming version.

Keypunch

Practice your typing skills

Brage Fuglseth reports

This week I released Keypunch 3.0. If you appreciate accuracy and legibility, you are going to love this update!

  • Monospaced font in the main text view
  • Text generation support for Bangla and Hebrew
  • Guillemet quotation marks for the French text generation
  • User interface translations for Hebrew and Italian, making Keypunch available in a total of 14 languages

You can get Keypunch on Flathub.

Fractal

Matrix messaging app for GNOME written in Rust.

Kévin Commaille reports

Did you miss us? Only two weeks after our beta release, we are ready to release Fractal 8.rc.

Here are the main goodies since Fractal 8.beta:

  • Draft messages are persisted in the database, allowing to restore them after a restart
  • A banner appears when synchronization with the homeserver fails too many times in a row
  • Links in room descriptions are detected in most places
  • Collapsed categories in the sidebar are remembered between restarts, with the “Historical” category collapsed by default
  • The history media viewers show properly when they are loading or empty

As usual, this release includes other improvements, fixes and new translations thanks to all our contributors, and our upstream projects.

It is available to install via Flathub Beta, see the instructions in our README.

As the version implies, it should be mostly stable and we expect to only include minor improvements until the release of Fractal 8.

If you want to spend your summer or winter coding, solving our issues is a great place to start!

GNOME Foundation

Rosanna reports

Today is the first official day at GUADEC, but for our Board of Directors, Our Interim Executive Director, and myself, it’s day three of GUADEC-related events. Traditionally, the board meets in person two days before GUADEC, followed the next day with a meeting with our Advisory Board. This year, with some directors unable to be here in person in Denver, we have had some scheduling challenges and the Board meeting has spread out across three mornings. It has been satisfying helping the new Directors as they work together as a Board for the first time.

At the meetings I presented financials, gave input on Foundation procedures, and explained proposals on behalf of the staff. While all-day meetings are always tiring, it was good to be able to work through the backlog of issues that usually are decided at the GUADEC Board meetings.

In addition, I have had some fruitful discussions with Richard, our Interim ED, on the priorities and logistics of Foundation work.

Last night was the pre-registration party here in Denver, and it was wonderful seeing so many folks again! It was truly lovely to see familiar faces that have not been able to come to GUADEC in a while due to the distance.

While GUADEC has taken up most of my time and mental energies, regular Foundation work doesn’t stop. I have also been in contact with our accountants to make sure our finalized financial review is signed, worked on paperwork for other tax forms, made sure invoices are being paid, along with other minutiae.

If you’re in Denver please drop by and say hi! I am excited GUADEC is about to begin and looking forward to the coming days of talks, BoFs, and social events!

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!

July 17, 2024

Why refactoring is harder than you think, a pictorial representation

Suppose you are working on a legacy code base. It looks like this:

Within it you find a piece of functionality that does a single thing, but is implemented in a hideously complicated way. Like so.

You look at that and think: "That's awful. I know how to do it better. Faster. Easier. More maintainable." Then you set out to do just that. And you succeed in producing this thing.

Wow. Look at that slick and smooth niceness. Time to excise the bad implementation from your code base.

The new one is put in:

And now you are don ... oh, my!



July 15, 2024

2024-07-15 Monday

  • Up early, mail chew, catch up with Miklos, then Lily - back from vacation.
  • Niels from OpenProject over to catch up - great to talk in the garden; had lunch & drove him back to Cambridge; good.
  • Suffered a terrible case of range-anxiety: having got used to the electric car being 100% fully charged whenever I leave to go out; I was amazed to notice the hybrid car having only 15 miles of range, and have to nurse it to an ugly smelling petrol station; hey ho.
  • Back to admin, caught up with Andras; build bits, finished mali chew, and more.

Status update, 15/07/2024

Was a great weekend for me with a trip to Ovar, Portugal to watch amazing Bixiga 70 and eat delicious Portuguese food. Not such a great weekend for the England men’s football team or Donald trump.

Work has been hard this month, my client work transitioned into something of a support role for a system which we are supposed to be decommissioning, and gave me little head space to focus on other work.

Looking back at the last year, I’ve done a lot of interesting open source work, and I’m heading to the GNOME conference in Denver next week to talk about some of it. I’ll talk about QA on Friday during “You’re Always Breaking GNOME” and then on Monday: “Workshop: openQA testing for your GNOME app, module or service” which will cover how to write tests with openQA and how to use the in-development support for testing unmerged branches in modules.

Hybrid events

This is an interesting year for the GNOME project as it’s the first GUADEC split between two locations – one group in Denver and one in Berlin, plus many other folk at home. It’s great that we are trying this approach, especially this year where the choice of host country makes it very hard for many people to get a visa.

There’s a few reasons I opted to go to Denver.

Partly I was hoping to meet the Outreachy interns from the openQA internship we did in Q1, but due to minimal transport sponsorship this year they aren’t able to go. Partly I’ve always said that if I could visit one place in the US, it would be Colorado. But the main reason is a conversation I had two years ago with an American contributor, who said something like … “Of course its always the Americans who have to travel to Europe, you guys never make the trip to the US.”

I stopped and thought a while over that. I grew up in 1990’s Britain, and there was a very clear heirarchy of who is most important in the world.

  1. The USA
  2. England
  3. Everyone else

All the news, music, politics, technology, TV and cinema in the UK reflected this, and i imagine it’s the same today. The idea stayed with me that the USA is the dominant force in all things. And I never questioned that in open source software it was also the case.

Until I thought about it a bit. All the conferences I’ve been to have been in Europe. I’ve told a lot of people “You should come to FOSDEM” or “You should come to GUADEC” but nobody’s ever told me the inverse, “You should come to $X event that happens in the US.” Every friend I have in open source is someone who either lives in Europe or has travelled to Europe so we could meet. Maybe it’s true that for once, things are a bit Euro-centric?

Most of my free software contributions now are talking to people and making connections. I haven’t done any substantial development work in a long time, nor would I have capacity to start anything. (Which I hate, but at least it’s not chatting to chatbots).

I find it much easier to communicate in person, for better or worse. This year it’s time for the openQA testing work to meet the wider community, and to “sink or swim” based on how useful everyone finds it. There are some interesting things to announce in the world of desktop search as well, such as the death of Tracker.

I hope that this Denver trip will be an opportunity to share what I know with people who don’t or can’t travel to Europe, who I’m otherwise never going to meet in person.

I’ve been to a few hybrid events in the past which had only minimal connection between the remote and local participants, hopefully this year we will learn some ways to build better connections. Lets see how it goes… I’ll report back in a couple of weeks.

July 14, 2024

Goodbye Tracker, hello TinySPARQL and LocalSearch

It is our pleasure (Sam’s and mine) to announce the following project renames:

– The Tracker SPARQL library will now be known as TinySPARQL
– The Tracker Miners indexer will now be known as LocalSearch, or GNOME LocalSearch if you prefer.

This on one hand will meet the increasing popular demand to have the terms “Tracker” and “Miner” avoided (In case the subtleties of language evade you, these have gained some bad connotations over the years), and on the other hand fixes the blunder done when Tracker 1.x conceded its name to the SPARQL library when library and indexer were split for 2.x. This has been a source of confusion wrt the extent and intent of each component, even among GNOME peers.

I am personally hopeful for the rename of TinySPARQL, as there has been a great amount of effort spent in making it a most credible implementation of the SPARQL/RDF/RDFS sets of standards, which feels underutilized with the library being mostly typecast as a gateway to the indexer data. Perhaps the new name will help reach to wider audiences that will be appreciative of a lightweight alternative to the available enterprise-level SPARQL engines.

But I shouldn’t diminish the importance of the rename of LocalSearch, especially at a time when other operating systems are approaching search in orwellian ways in the name of convenience, it becomes important to take a stand. We think the new name is a simple enough guide of our goals and non-goals.

This is not just a superficial rename, however we the maintainers have grown wary of approaches that would require us to bump version to 4.x, and updating all the projects depending on TinySPARQL/LocalSearch in a synchronized manner. So we have gone for a soft approach that will be backwards compatible for the most part, and allow a transitional path. Here’s what you can do to “update”:

In C projects

You are encouraged to use the tinysparql-3.0.pc file to detect the TinySPARQL library. The tracker-sparql-3.0.pc file is still provided for backwards compatibility. In code, use #include <tinysparql.h> to include the SPARQL library headers.

In the future we might consider an API migration scheme that deprecates all API under the old namespace and replaces with with objects in the new namespace. Meanwhile, all API objects and symbols keep the “Tracker” namespace.

From gobject-introspection bindings

You can get a “sneak peek” by using the new namespaces right away, e.g. from python code:

#!/usr/bin/python3

import gi, sys
gi.require_version('Tsparql', '3.0')
from gi.repository import GLib, Gio, Tsparql

try:
    connection = Tsparql.SparqlConnection.new(
        Tsparql.SparqlConnectionFlags.NONE,
        None, # Database location, None creates it in-memory
        Tsparql.sparql_get_ontology_nepomuk(), # Ontology location
        None)

    # Create a resource containing RDF data
    resource = Tsparql.Resource.new(None)
    resource.set_uri('rdf:type', 'nmm:MusicPiece')

    # Create a batch, and add the resource to it
    batch = connection.create_batch()
    batch.add_resource(None, resource)

    # Execute the batch to insert the data
    batch.execute()

    connection.close()

except Exception as e:
    print('Error: {0}'.format(e))
    sys.exit(-1)

Remember to make your project depend on tinysparql-3.0.pc to ensure an up-to-date enough version to have these new binding definitions. Of course backwards compatibility is preserved, and there will be GIR/typelib files for the old namespaces.

In Vala code

There is also a new VAPI file, to describe the TinySPARQL API under the Tsparql namespace:

// valac --pkg tsparql-3.0 tsparql.vala
using Tsparql;

int main (string[] argv) {
  try {
    var conn = Tsparql.Connection.new (Tsparql.ConnectionFlags.NONE, null, Tsparql.get_ontology_nepomuk(), null);

    // Create a resource containing RDF data
    var resource = new Tsparql.Resource(null);
    resource.set_uri ("rdf:type", "nmm:MusicPiece");

    // Create a batch, and add the resource to it
    var batch = conn.create_batch ();
    batch.add_resource (null, resource);

    // Execute the batch to insert the data
    batch.execute ();

    conn.close ();
  } catch (GLib.Error e) {
    stderr.printf ("Error: %s\n", e.message);
  }

  return 0;
}

And same as before, there will be also a VAPI file with the old namespace.

Accessing LocalSearch data

The LocalSearch indexer data will now alternatively be offered via the org.freedesktop.LocalSearch3 D-Bus name, as well as through the org.freedesktop.Tracker3.Miner.Files D-Bus name for backwards compatibility. This can be used as usual with the TinySPARQL D-Bus based connection, e.g. from Python:

#!/usr/bin/python3

import gi, sys
gi.require_version('Tsparql', '3.0')
from gi.repository import Tsparql

conn = Tsparql.SparqlConnection.bus_new(
    'org.freedesktop.LocalSearch3', None)
cursor = conn.query('select ("Hello World" as ?str) {}', None)
cursor.next()
print(cursor.get_string(0))

Command line utilities

The command line utilities for both projects have parted ways. Instead of a single command line utility with extensible subcommands, the will be distinct tinysparql and localsearch command line tools, each providing their own sensible set of subcommands. We trust it will be reasonably low effort to adapt to these changes, and reasonably intuitive.

Test utilities

In order to test search-related code, some projects were using the tracker-testutils-3.0.pc file to locate the tracker-sandbox helper script to help them implement a test harness for search features. This has now become more of a first class citizen as a localsearch test-sandbox subcommand. There is no longer a pkgconf file, we recommend to detect the presence of the localsearch command in build systems.

A note to distributors

The repositories for these projects are https://gitlab.gnome.org/GNOME/tinysparql and https://gitlab.gnome.org/GNOME/localsearch, and the tarballs distributed will have tinysparql-x.y.z and localsearch-x.y.z filenames (starting with tinysparql-3.8.alpha and localsearch-3.8.alpha which are already out of the door). We are understanding that a project rename will involve a more changes on your end(s) than ours, but we will greatly appreciate if you help us establish the new identities and follow up on the rename.

2024-07-14 Sunday

  • Up earlyish; played at All Saints, home - most people gone - except for Sade. Bits of left-overs for lunch, slugged enthusiastically - watched some Travellers, babes seem to have got into football somehow. Bed early.

July 13, 2024

TinySPARQL Web IDE update

Hi GNOME community! It’s been a while 🙂 I caught COVID a while back so it took some time to catch back up on work but we’ve been making progress on the GSoC project. We now have a fully functional IDE with a smooth interface and syntax highlighting – take a look!

It is also equipped with basic error reporting functionalities.

With the basic structure and functionalities complete, our next steps will be to extend the error response to also reflect the line from which the error is originating from, as well as implementing a bookmarking feature to allow users to “save” their queries using links.

There have also been suggestions to create an RDF browsing interface on the project. This will be a challenge to implement as it would require quite a lot of flexibility in the display, but given that we still have quite a bit of time until the end of GSoC, it is a challenge I hope I would be ale to take on.

July 12, 2024

GNOME Foundation Announces Transition of Executive Director

Today, the GNOME Foundation announces that Holly Million will be departing from her role as Executive Director of the GNOME Foundation. An Interim Executive Director, Richard Littauer, has joined the Foundation this week. This arrangement ensures a smooth transition and continuity as we prepare for a new Executive Director search. Holly’s last day will be July 31, and she and Richard will be able to coordinate their efforts during this period. 

“I’m very proud of what I have accomplished in my time with GNOME,” Holly said, “and I am dedicated to working with Richard through a transition to ensure that everything I have been working on is handed off smoothly. I will be pursuing a PhD in Psychology and dedicating myself to my own private practice. I want to thank all the wonderful people I have had the pleasure of connecting with here at GNOME, including the staff, board, advisors, founders, and volunteers. You have created something of real value in the world, and I look forward to seeing what you do next.”

On behalf of the whole GNOME community, the Board of Directors would like to give our utmost thanks to Holly for her achievements during the past 10 months, including drafting a bold five-year strategic plan for the Foundation, securing two important fiscal sponsorship agreements with GIMP and Black Python Devs, writing our first funding proposal that will now enable the Foundation to apply for more grants, vastly improving our financial operations, and implementing a break-even budget to preserve our financial reserves. 

The Foundation’s Interim Executive Director, Richard Littauer, brings years of open source leadership as part of his work as an organizer of SustainOSS and CURIOSS, as a sustainability coordinator at the Open Source Initiative, and as a community development manager at Open Source Collective, and through open source contributions to many projects, such as Node.js and IPFS. The Board appointed Richard in June and is confident in his ability to guide the Foundation during this transitional period.

The Board will be meeting next week to set up a search plan for a permanent Executive Director, which will be announced after GUADEC. The Foundation looks forward to talking with GNOME members and the community and answering any questions next week at the Annual General Meeting (AGM) GUADEC in Denver, as well as asynchronously with those in Berlin and around the globe.

Profiling a web engine

One topic that interests me endlessly is profiling. I’ve covered this topic many times in this blog, but not enough to risk sounding like a broken record yet. So here we are again!

Not everyone may know this but GNOME has its own browser, Web (a.k.a. Epiphany, or Ephy for the intimates). It’s a fairly old project, descendant of Galeon. It uses the GTK port of WebKit as its web engine.

The recent announcement that WebKit on Linux (both WebKitGTK and WPE WebKit) switched to Skia for rendering brought with it a renewed interest in measuring the performance of WebKit.

And that was only natural; prior to that, WebKit on Linux was using Cairo, which is entirely CPU-based, whereas Skia had both CPU and GPU-based rendering easily available. The CPU renderer mostly matches Cairo in terms of performance and resource usage. Thus one of the big promises of switching to Skia was better hardware utilization and better overall performance by switching to the GPU renderer.

A Note About Cairo

Even though nowadays we often talk about Cairo as a legacy piece of software, there’s no denying that Cairo is really good at what it does. Cairo can and often is extremely fast at 2D rendering on the CPU, specially for small images with simple rendering. Cairo has received optimizations and improvements for this specific use case for almost 20 years, and it is definitely not a low bar to beat.

I think it’s important to keep this in mind because, as tempting as it may sound, simply switching to use GPU rendering doesn’t necessarily imply better performance.

Guesswork is a No-No

Optimizations should always be a byproduct of excellent profiling. Categorically speaking, meaningful optimizations are a consequence of instrumenting the code so much that the bottlenecks become obvious.

I think the most important and practical lesson I’ve learned is: when I’m guessing what are the performance issues of my code, I will be wrong pretty much 100% of the time. The only reliable way to optimize anything is to have hard data about the behavior of the app.

I mean, so many people – myself included – were convinced that GNOME Software was slow due to Flatpak that nobody thought about looking at app icons loading.

Enter the Profiler

Thanks to the fantastic work of Søren Sandmann, Christian Hergert, et al, we have a fantastic modern system profiler: Sysprof.

Sysprof offers a variety of instruments to profile the system. The most basic one uses perf to gather stack traces of the processes that are running. Sysprof also supports time marks, which allow plotting specific events and timings in a timeline. Sysprof also offers extra instrumentation for more specific metrics, such as network usage, graphics, storage, and more.

  • Screenshot of Sysprof's callgraph view
  • Screenshot of Sysprof's flamegraphs view
  • Screenshot of Sysprof's mark chart view
  • Screenshot of Sysprof's waterfall view

All these metrics are super valuable when profiling any app, but they’re particularly useful for profiling WebKit.

One challenging aspect of WebKit is that, well, it’s not exactly a small project. A WebKit build can easily take 30~50min. You need a fairly beefy machine to even be able to build a debug build of WebKit. The debug symbols can take hundreds of megabytes. This makes WebKit particularly challenging to profile.

Another problem is that Sysprof marks require integration code. Apps have to purposefully link against, and use, libsysprof-capture to send these marks to Sysprof.

Integrating with Sysprof

As a first step, Adrian brought the libsysprof-capture code into the WebKit tree. As libsysprof-capture is a static library with minimal dependencies, this was relatively easy. We’re probably going to eventually remove the in-tree copy and switch to host system libsysprof-capture, but having it in-tree was enough to kickstart the whole process.

Originally I started sprinkling Sysprof code all around the WebKit codebase, and to some degree, it worked. But eventually I learned that WebKit has its own macro-based tracing mechanism that is only ever implemented for Apple builds.

Looking at it, it didn’t seem impossible to implement these macros using Sysprof, and that’s what I’ve been doing for the past few weeks. The review was lengthy but behold, WebKit now reports Sysprof marks!

Screenshot of Sysprof with WebKit marks highlighted

Right now these marks cover a variety of JavaScript events, layout and rendering events, and web page resources. This all came for free from integrating with the preexisting tracing mechanism!

This gives us a decent understanding of how the Web process behaves. It’s not yet complete enough, but it’s a good start. I think the most interesting data to me is correlating frame timings across the whole stack, from the kernel driver to the compositor to GTK to WebKit’s UI process to WebKit’s Web process, and back:

Screenshot of Sysprof with lots of compositor and GTK and WebKit marks

But as interesting as it may be, oftentimes the fun part of profiling is being surprised by the data you collect.

For example, in WebKit, one specific, seemingly innocuous, completely bland method is in the top 3 of the callgraph chart:

Screenshot of Sysprof showing the callgraph view with an interesting result highlighted

Why is WebCore::FloatRect::contains so high in the profiling? That’s what I’m investigating right now. Who guessed this specific method would be there? Nobody, as far as I know.

Once this is out in a stable release, anyone will be able to grab a copy of GNOME Web, and run it with Sysprof, and help find out any performance issues that only reproduce in particular combinations of hardware.

Next Plans

To me this already is a game changer for WebKit, but of course we can do more. Besides the rectangular surprise, and one particular slowdown that comes from GTK loading Vulkan on startup, no other big obvious data point popped up. Specially in the marks, I think their coverage is still fairly small compared to what it could have been.

We need more data.

Some ideas that are floating right now:

  • Track individual frames and correlate them with Sysprof marks
  • Measure top-to-bottom-to-top latency
  • Measure input delay
  • Integrate with multimedia frames

Perhaps this will allow us to make WebKit the prime web engine for Linux, with top-tier performance, excellent system integration, and more. Maybe we can even redesign the whole rendering architecture of WebKit on Linux to be more GPU friendly now. I can dream high, can’t I? 🙂

In any case, I think we have a promising and exciting time ahead for WebKit on Linux!

Graphics offload continued

We first introduced support for dmabufs and  graphics offload last fall, and it is included in GTK 4.14. Since we last talked about, more improvements have happened, so it is time for another update.

Transformations

When you rotate your monitor, it changes its aspect from landscape (say, 1920 x 1200 to portrait (1200 x 1920). But the framebuffer that the compositor deals with stays 1920 x 1200.

There are some other situations where the content may need some transformation before it can be displayed. For example when showing the video stream from a front-facing camera.

D4 cycle graph

So far, GTK has treated flipped or rotated textures as not offloadable, but after some recent improvements, we will now pass such transformations along to the compositor when offloading content.

Quick detour

This is a good opportunity to explain that the GTK transformation API relies on you to provide classification into useful categories such as 2D translations, scales, rotations or arbitrary 3D transforms.

Therefore, it is much better to use specific methods such as gsk_transform_rotate or gsk_transform_translate instead of computing the transformation matrix yourself and using  gsk_transform_matrix, which does not give us any information about the kind of transformation we are dealing with. And without this information, we miss out on optimizations like the transformed offloading described above.

Benjamin gave a quick overview of our approach to transformations here.

Textures

The initial goal for the offload work was to enable passthrough of dmabuf content. Often, that is not what we get though. It is still far more common that we receive content in GL textures (e.g. from gstreamer). Thankfully, mesa has GL extensions that let us export textures as dmabufs. We’ve recently used this to enable offloading for GL textures.

Note that these extensions might not be available everywhere, and individual drivers can have problems with dmabuf support.

Summary

In the coming GTK 4.16 release, graphics offloading should work much more often.

You can try these improvements today, e.g. in the nightly build of the new Showtime app:

Showtime

July 11, 2024

Summer Maps

 I usually write a “Summer Maps” blog post around midsummer. It's a bit later this year. But here it comes!

 


 Since the last time there has been some visual updates.

Search Bar Tweak

Since version 45 we had an “Explore POIs” interface that is accessed via a menu button next to the main search entry. This button used a linked style to get shared rounded corner. This has had some visual glitches when showing the search results popover (resutling in the rounded corner getting „straightened” giving jarred look).

After playing with putting the menu button at the side without being linked, but that gives a little „unbalanced” look with the popover. So I decided to try out something different.

Using the secondary icon of the entry, indicating the „explore” action when no search term is entered.




More Adwaita Dialogs

Contributor “DaPigGuy” has converted the export image dialog to use the new dialog widget from libadwaita. Also the save file UI has been streamlined, and is now handled by the file select dialog when clicking „Save”.

One more Icon

One additional POI icon for playgrounds has been contributed by Jakub Steiner.

Playgrounds

Transitous Support

I've written about the Transitous project before.

There is now a work-in-progress branch implementing public transit routing using MOTIS: https://gitlab.gnome.org/GNOME/gnome-maps/-/merge_requests/440

Currently it works best when using the service at europe.motis-project.de.

This can be tested by setting the following environement variables when running the branch (from GNOME Builder it can be set either by entering the „execution terminal”, or by creating a custom run command).

TRANSITPLUGIN=Motis

MOTIS_BASE_URL=https://europe.motis-project.de


 
A sample of itineraries going from the  Louvre to the Eiffel tower in Paris.


And I would like to re-iterate that you can also help contributing public transit feeds to the Transitous project for your locality by creating pull requests at https://github.com/public-transport/transitous

Hill Shading

Another project James Westman has been working on is hillshading. This would make the map show terrain topology, which not only gives the map a nicer, more live appearance. It is also beneficial for example when hiking.


Currently it awailable in the web preview for the map style at https://maps.jwestman.net/

In the screenshot above showing the Sylarna mountain range: Wikipedia

Contrasting with how it looks currently when rendered by libshumate in Maps

 

Adding this in libshumate will require some additional work, as it will need support for transparent „overlay sources” to render the „TopoRGB” tiles (which will basically be an alpha channel rendering „on top” of the vector rendering (I hope I explained that somewhat correctly).

 

And that's going to be it for this time!

And stay tuned 😀 



July 10, 2024

Forcibly Set Array Size in Vala

Vala programmers probably already know that a Vala array is equivalent to a C array plus an integer size. (Unfortunately, the size is gint rather than size_t, which seems likely to be a source of serious bugs.) When you create an array in Vala, the size is set for you automatically by Vala. But what happens when receiving an array that’s created by a library?

Here is an example. I recently wanted to use the GTK function gdk_texture_new_for_surface(), which converts from a cairo_surface_t to a GdkTexture, but unfortunately this function is internal to GTK (presumably to avoid exposing more cairo stuff in GTK’s public API). I decided to copy it into my application, which is written in Vala. I could have put it in a separate C source file, but it’s nicer to avoid a new file and rewrite it in Vala. Alas, I hit an array size roadblock along the way.

Now, gdk_texture_new_for_surface() calls cairo_image_surface_get_data() to return an array of data to be used for creating a GBytes object to pass to gdk_memory_texture_new(). The size of the array is cairo_image_surface_get_height (surface) * cairo_image_surface_get_stride (surface). This is GTK’s code to create the GBytes object:

bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
                                    cairo_image_surface_get_height (surface)
                                    * cairo_image_surface_get_stride (surface),
                                    (GDestroyNotify) cairo_surface_destroy,
                                    cairo_surface_reference (surface));

The C function declaration of g_bytes_new_with_free_func() looks like this:

GBytes*
g_bytes_new_with_free_func (
  gconstpointer data,
  gsize size,
  GDestroyNotify free_func,
  gpointer user_data
)

Notice it takes both the array data and its size size. But because Vala arrays already know their size, Vala bindings do not contain a separate parameter for the size of the array. The corresponding Vala API looks like this:

public Bytes.with_free_func (owned uint8[]? data, DestroyNotify? free_func, void* user_data)

Notice there is no way to pass the size of the data array, because the array is expected to know its own size.

I actually couldn’t figure out how to pass a DestroyNotify in Vala. There’s probably some way to do it (please let me know in the comments!), but I don’t know how. Anyway, I compromised by creating a GBytes that copies its data instead, using this simpler constructor:

public Bytes (uint8[]? data)

My code looked something like this:

unowned uchar[] data = surface.get_data ();
return new Gdk.MemoryTexture (surface.get_width (),
                              surface.get_height (),
                              BYTE_ORDER == ByteOrder.LITTLE_ENDIAN ? Gdk.MemoryFormat.B8G8R8A8_PREMULTIPLIED : Gdk.MemoryFormat.A8R8G8B8_PREMULTIPLIED,
                              new Bytes (data),
                              surface.get_stride ());

But this code crashes because the size of data is unset. Without any clear way to pass the size of the array to the Bytes constructor, I was initially stumped.

My first solution was to create my own array on the stack, use Posix.memcpy to copy the data to my new array, then pass the new array to the Bytes constructor. And that actually worked! But now the data is being copied twice, when the C version of the code doesn’t need to copy the data at all. I knew I had to copy the data at least once (because I didn’t know how else to manually call cairo_surface_destroy() and cairo_surface_ref() in Vala, at least not without resorting to custom language bindings), but copying it twice seemed slightly egregious.

Eventually I discovered there is an easier way:

data.length = surface.get_height () * surface.get_stride ();

It’s that simple. Vala arrays have a length property, and you can assign to it. In this case, the bindings didn’t know how to set the length, so it defaulted to the maximum possible value, leading to the crash. After setting the length manually, the code worked properly. Here it is.

Stepping down as GNOME Internships organizer

For the past nine years, I’ve been actively involved in our internship initiatives with Google Summer of Code and Outreachy. As an alumnus of these programs, I firmly believe they are great ways to onboard new contributors to GNOME and help students start a career in open-source software.

However, as my work responsibilities have grown and I’ve started some personal (non-software) projects, I’ve found myself with less time and energy for this type of contribution.

I’ve been discussing this with key people over the past year, and I plan to stay around to hand over responsibilities to other members of the Internship Committee. I will continue helping manage things for 2024, but I won’t be directly involved beyond that.

If you’re interested in helping with these activities, please reach out to the GNOME Internship Committee.

July 09, 2024

Outreachy Internship Blog Series: Understanding GNOME

When I first encountered GNOME, I struggled a lot with understanding what it was. This was during the Outreachy contribution phase, and its description included the term “desktop environment”.

At the time I had no idea what a desktop environment was. While developing skills in user experience design, I learned that a digital product could be an application, a website, or something in between: a web app. However, I didn’t realize it could also be a desktop environment, because I was oblivious to the notion of a desktop environment being a distinct component of an operating system.

Nonetheless, I braced myself for the journey ahead and took the first steps towards contributing to this design-driven project that I knew nothing about.

And So It begins!

It didn’t take long into my search for me to realize that I was a complete novice in the world of computers as understanding GNOME required me to be familiar with it.

I had begun my quest for understanding with a quick Google search for “desktop environment”, this led me to answers that intensified my confusion, prompting further questions that developed as follows:

What is an operating system? What is a computer? What are the core computer software? What are the functions of an operating system? What is Linux? Why do Linux distributions exist instead of a single Linux operating system? And so on…

By the end of my search, I knew a lot more than I did at the start, but I still lacked a satisfying understanding of what GNOME was. To make things worse, I also couldn’t tell what I was missing.

My research had revealed that GNOME, in some ways, transcended the definitions I’d found for a desktop environment, but it was also not an operating system. And yet, you could run GNOME OS independently in a virtual machine. So, what was it? A family of products? A desktop environment? Or a quasi-operating system?

Finding Answers

The entire time, I was reluctant to seek help from the mentors. I thought the subject of my confusion was too basic to bring to their attention. This, and a fear of appearing inadequate. Instead, I buried myself in more research hoping to somehow figure things out on my own, only returning to the channel intermittently to check for instructions and updates.

Yet, it was during one of these brief returns to the matrix channel that something unexpected caught my attention.

Upon close observation, some of the questions and comments on the channel revealed that I wasn’t the only one unsure about what GNOME was, and the mentors, having picked up on it, were responding insightfully to the questions behind the questions being asked.

I found my missing piece in one of these responses, and just like that, this comment flipped a switch and connected the dots in a manner that put things in the right perspective for me.

Armed with this new-found clarity, I was able to revisit my notes from earlier research and properly answer my discovery questions about GNOME.

The Lessons

I came out of this experience with a few solid realizations that continue to shape my perspective on embracing new challenges.

Firstly, I recognized that I was getting in my own way. Feeling insecure about my struggles was keeping me stuck, to the extent that I might have given up on making contributions to this project, save for the courage of a peer who could ask for help when they needed it.

Secondly, I learned that struggling is a natural part of growth and something that everyone experiences. It really isn’t something to be insecure about.

Thirdly, I realized that I was in an enabling community. The mentors anticipated that we would need help in different areas and their focus was on helping us get to a place where we could make the contributions that we wanted to. Watching their interactions with my peers dissipated my fears, and I’ve never hesitated to ask for help since then.

My Understanding of GNOME

My understanding of GNOME has evolved considerably since that first encounter and continues to deepen.

I now understand GNOME as an ecosystem that provides an inclusive, free, user-friendly computing experience for computers and devices powered by the Linux kernel.

From a technical point of view, this means that it is both the GUI and its interactions (i.e. how the operating system looks and feels), the programs and applications that constitute the computing experience, and the stacks that make if possible for distros to integrate and customize this computing experience.

I sadly cannot fit all that I’ve learned about GNOME in this blog post. However, you can visit the official GNOME website to learn more about the software and discover its powerful features and intuitive desktop experience.

It’s a gratifying thing to witness one’s own growth. And I am deeply grateful for the guidance of the mentors, the platforms (Outreachy and GNOME), and the cultural factors within these platforms that have enabled this particular one.

Thank you for reading!

 

July 07, 2024

GSoC 2024: Week 3-4 Report

GSoC 2024: Week 3-4 Report 


Project

Add support for the latest GIR attributes and GI-Docgen formatting to Valadoc

Mentor

Lorenz Wildberg


Week 3 : default-value attribute for property

Parsing the "default-value" attribute into a member access: In continuation of the work done previously, we tried to set the prop.initializer of the corresponding Vala.Property from the default-value attribute by parsing it as an enumerator member. We parsed the default-value into a member access in GirParser.process(), but trying to regenerate the bindings resulted in errors like this:


We got these errors because the default-value attribute in the vala GIRs actually contains the "c:identifier" of the enum member. However the function   parse_enumeration_member() in the GirParser parses it into an EnumValue using its "name" attribute and not the "c:identifier" attribute. Thus when we try to parse the default-value attribute into a member access, the parser cannot find any enum member corresponding to the value in the default-value attribute:

  The enum member with c:identifier="GTK_FILE_CHOOSER_ACTION_OPEN" is parsed with the name "open"

A temporary workaround is to parse enum members using their "c:identifier" attribute so that the value of the "default-value" attribute is the same as the symbol name of the EnumValue (the name of the symbol would now be the "c:identifier" of the enum member). This would fix the above error but it would also change the names of all enum members in the bindings to the value of their corresponding "c:identifier". This might be risky, so we are working on finding a viable solution that does not cause any breaking changes. Furthermore, we are also working on cases where the "default-value" attribute has two enum members separated by a binary OR - an example of this is in line 64623 in Gtk-4.0.gir :

default-value="GTK_FONT_CHOOSER_LEVEL_STYLE | GTK_FONT_CHOOSER_LEVEL_SIZE"

Here, the default-value cannot be parsed into a single enum member because it contains two members of type Gtk.FontChooserLevel.With the resolution of the above error and some minor tweaks, we will be able to successfully parse the default-value attribute into the bindings.

Week 4: glib:finish-func attribute for method

In the GirParser: In the previous week, we added a new property, finish_func in Vala.Method and set this property to the value of glib:finish-func. To further refine the support for this attribute, we used the finish_func property to decide whether the method is asynchronous, as there is currently no definitive rule for this in the GirParser. Since only async functions in GObject can have the glib:finish-func attribute, a method is async if it's finish-func property is not null. We added this feature, and then added a test case in tests/gir/method-class.gir to check whether a method with this attribute is declared as asynchronous in the bindings.

In the GirWriter: Further improvement was done to add the support for glib:sync-func, glib:async-func,and glib:finish-func in:
  • Synchronous methods: Made the GirWriter write the glib:async-func attribute if the method is synchronous and has a corresponding asynchronous version.
  • Asynchronous methods: Made the GirWriter write the glib:sync-func attribute if the method is asynchronous (and has a corresponding synchronous version), and the glib:finish-func attribute.
This involved making tweaks in the write_signature and do_write_signature methods because they write the GIR attributes for method. Finally, we updated the test cases in tests/girwriter to test these changes.

So this was the summary of our work in the previous two weeks. Thanks for reading :). In the coming days we plan to work on Valadoc and add support for the remaining GObject Introspection attributes!




July 01, 2024

GNOME- What I’d like to know as a newcomer

Navigating an open-source community for the first time can be challenging. Understanding the project and deciding which aspects or areas to contribute can be difficult. Today, I aim to answer a few common questions that newcomers to GNOME might have and help someone get started.

What is GNOME?

GNOME Logo

Simply put, GNOME is a free and open-source desktop interface that lets users interact with their computers through graphical elements like icons, windows, and menus, instead of just command lines. GNOME is widely used in Linux distributions like Fedora and Ubuntu.

Who Participates in the GNOME Community?

The GNOME community comprises a diverse group of people, including software developers, designers, translators, documentation writers, and users. These contributors come together to improve and maintain GNOME and a collection of other Free and Open Source Projects (FOSS) that use GNOME.

The community is friendly and virtually anyone who has a computer can be a part of it.

Why does GNOME exist?

GNOME aims to provide a consistent and enjoyable user experience for all Linux users, regardless of their technical background, and to make Linux operating systems easy for non-programmers. The community focuses on making the desktop environment intuitive and accessible while ensuring it remains powerful enough for advanced users.

What do I do in GNOME?

As part of my work as an Outreachy intern with GNOME, I’m involved in a project focused on conducting a series of short user research exercises. The goal is to gather insights into how people use particular apps on GNOME, their challenges, and what improvements can be made to enhance their experience. This project is crucial because user feedback directly informs design and development decisions, leading to a better overall product.

What excites me most about this project is the opportunity to directly impact the user experience of thousands of GNOME users worldwide. I can make GNOME more user-friendly and efficient by gathering and analyzing user feedback.

You can visit the GNOME handbook to explore different teams and learn how to join them.

June 30, 2024

scikit-survival 0.23.0 released

I am pleased to announce the release of scikit-survival 0.23.0.

This release adds support for scikit-learn 1.4 and 1.5, which includes missing value support for RandomSurvivalForest. For more details on missing values support, see the section in the release announcement for 0.23.0.

Moreover, this release fixes critical bugs. When fitting SurvivalTree, the sample_weight is now correctly considered when computing the log-rank statistic for each split. This change also affects RandomSurvivalForest and ExtraSurvivalTrees which pass sample_weight to the individual trees in the ensemble. Therefore, the outputs produced by SurvivalTree, RandomSurvivalForest, and ExtraSurvivalTrees will differ from previous releases.

This release fixes a bug in ComponentwiseGradientBoostingSurvivalAnalysis and GradientBoostingSurvivalAnalysis when dropout is used. Previously, dropout was only applied starting with the third iteration, now dropout is applied in the second iteration too.

Finally, this release adds compatibility with numpy 2.0 and drops support for Python 3.8.

Install

scikit-survival is available for Linux, macOS, and Windows and can be installed either

via pip:

pip install scikit-survival

or via conda

 conda install -c conda-forge scikit-survival

June 28, 2024

Bisect’ing outside of the box

There is a sort of thrill to a bug hunt once you dig your heels in deep enough. This is one of those stories.

Earlier in the week a fellow Red Hatter messaged me about a bug in Ptyxis where if you right click on a tab, the tab stays in the active state. Clearly not a very good look. My immediate thought was that we don’t do anything special there, so maybe the issue is lower in the stack.

A screenshot of Ptyxis with 4 terminal tabs open. Two of which appear "focused" due to an overzealous active state on the tab widget.

Oopsie!

Libadwaita?

The first layer beneath that is of course libadwaita and so naturally I’ll test Text Editor to see if I get the same behavior. Lo-and-behold it exists there. Cool, file an issue in libadwaita while I simultaneously see if I can fix it.

Nothing stands out too obvious, but I try a few things which make it slightly better but I can still get into this state with enough attempts. Bummer. Also, Alice thinks the real issue is in GTK because we’ve seen this in a number of places. It appears to be something wrong with active state tracking.

GTK?

No worries, I know that code-base well, so we hop on over there and see what is going on. First things first though, where does active state tracking happen? A quick git grep later we see it is largely in gtkmain.c in response to incoming GdkEvent.

GTK or GDK?

Having written a large portion of the macOS back-end for GTK 4, I know that events can be extremely platform specific. So lets cut this problem in half by determining if the issue really is in the GTK-side or GDK-side. So we run the test again with GDK_BACKEND=x11 ptyxis -s and see if we can reproduce.

The issue appears to go away, lovely! Although, let’s test it on macOS too just to be certain. And again, it does not reproduce there. So that must mean the issue lies somewhere in gdk/wayland/ or related.

Regressions?

Another important detail that Alice mentioned was that it seemed to be a regression. That would be great because if so, it would mean that we can bisect to find an answer. However, since both gdk/wayland/ and compositors move forward at a somewhat lock-step pace, that can be quite difficult to prove reliably enough for bisect.

So instead I move on to something a bit whacky. I have a CentOS 7 installation here that I use to test that Ptyxis crazy GLibc hacks work for ptyxis-agent. That has GNOME 3.28 which can run as a Wayland compositor if you install the appropriate session file. Thus I do, and what do you know, it has the same issue there. That was surely to hit the “old wayland protocol” code-paths which are a bit different than the newer extensions, which was what I had hoped to test.

Either way, very old Mutter still equals broken. Womp.

Weston Enters the Ring

We have a “reference compositor” in the form of Weston which can help shake out protocol issues (assuming the diligence to Weston correctness is upheld). So I try running Ptyxis on my local machine inside a nested Weston instance (which appears to be 13.0.0). Surprising, things actually work!

Okay, but I still have that CentOS 7 VM running, I wonder if it has Weston available? It does not. Bummer. But I also have an Alma Linux 9 VM around that I use for testing occasionally and that must have it. Let’s try there.

In fact it does, but it’s version 8.0.0. Let’s see if it works there too!

A screenshot of GNOME Boxes running Alma Linux 9 in a GNOME session with a nested Weston compositor containing Ptyxis with 2 of 3 tabs broken.

Bisecting Weston

So now we know Weston 8.0.0 is broken and 13.0.0 works. Now we have something I can bisect!

So I fire up meson locally and ensure that my local build fails when it is 8.0.0. Indeed it does. And 13.0.0 appears to work. Bisect commence!

We find the commit which “fixes” things from a Weston point of view to be “Set grab client before calling grab functions“.

That gives me enough information to start commenting out lines of code in Mutter like the uncivilized monkey I am. Eventually, Shakespeare has been written and the bug goes away. Though certainly not in an upstream-able fashion. Bugs filed, lines of code pointed to, and here I hope for our lovely Mutter maintainers to take over.

Back to GDK

Lots of amazing things have happened since we got Wayland compositors. One of the more undeniable difficulties though has been equal event ordering amongst them. It’s almost certainly the case that other compositors are doing things in slightly different ways and it may not even be possible to change that based on their use-case/designs.

So some sort of mitigation will be required on the GDK side.

After some interpretation of what is really going on here, a very small patch to drop certain leave events.

And that’s the story of how I bisected Weston to find an issue in Mutter (and GDK).

June 26, 2024

Rethinking Planet GNOME with GitLab Pages/CI

Some GNOME websites are getting modernized and simplified, but Planet GNOME has fallen behind. Not anymore. I started a prototype for a Python script to publish Planet GNOME with GitLab Pages/CI.

As Planet GNOME Editor, I am often asked to look for blog and syndication issues I couldn’t really address due to limited server-side access. With this, debugging indexing issues should be easier as it is just about looking at the CI job output.

Also, the Planet website is perceived as messy and outdated. So this work allowed Jakub Steiner to quickly jump in and restyle the page from a clean state.

Try it live at https://felipeborges.pages.gitlab.gnome.org/planet.gnome.org and let me know what you think. Keep in mind this is a proof of concept. Tips, feedback, and contributions are welcome in the project repo.

This still doesn’t produce the global Planet rss feed, just the webpage, but that’s in my TODO list too.

P.S.: I know feed readers/parsers can over-request rss/atom feeds. So I plan to cache data and use metadata to avoid redundant downloads before this is even considered as a replacement for the current Planet implementation. No worries. 😉

GNOME tablet support papercut fixes

Over the last months I've started looking into a few of the papercuts that affects graphics tablet users in GNOME. So now that most of those have gone in, let's see what has happened:

Calibration fixes and improvements (GNOME 47)

The calibration code, a descendent of the old xinput_calibrator tool was in a pretty rough shape and didn't work particularly well. That's now fixed and I've made the calibrator a little bit easier to use too. Previously the timeout was quite short which made calibration quite stressfull, that timeout is now per target rather than to complete the whole calibration process. Likewise, the calibration targets now accept larger variations - something probably not needed for real use-cases (you want the calibration to be exact) but it certainly makes testing easier since clicking near the target is good enough.

The other feature added was to allow calibration even when the tablet is manually mapped to a monitor. Previously this only worked in the "auto" configuration but some tablets don't correctly map to the right screen and lost calibration abilities. That's fixed now too.

A picture says a thousand words, except in this case where the screenshot provides no value whatsoever. But here you have it anyway.

Generic tablet fallback (GNOME 47)

Traditionally, GNOME would rely on libwacom to get some information about tablets so it could present users with the right configuration options. The drawback was that a tablet not recognised by libwacom didn't exist in GNOME Settings - and there was no immediately obvious way of fixing this, the panel either didn't show up or (with multiple tablets) the unrecognised one was missing. The tablet worked (because the kernel and libinput didn't require libwacom) but it just couldn't be configured.

libwacom 2.11 changed the default fallback tablet to be a built-in one since this is now the most common unsupported tablet we see. Together with the new fallback handling in GNOME settings this means that any unsupported tablet is treated as a generic built-in tablet and provides the basic configuration options for those (Map to Monitor, Calibrate, assigning stylus buttons). The tablet should still be added to libwacom but at least it's no longer a requirement for configuration. Plus there's now a link to the GNOME Help to explain things. Below is a screenshot on how this looks like (after modifying my libwacom to no longer recognise the tablet, poor Intuos).

Monitor mapping names (GNOME 47)

For historical reasons, the names of the display in the GNOME Settings Display configuration differed from the one used by the Wacom panel. Not ideal and that bit is now fixed with the Wacom panel listing the name of the monitor and the connector name if multiple monitors share the same name. You get the best value out of this if you have a monitor vendor with short names. (This is not a purchase recommendation).

Highlighted SVGs (GNOME 46)

If you're an avid tablet user, you may have multiple stylus tools - but it's also likely that you have multiple tools of the same type which makes differentiating them in the GUI hard. Which is why they're highlighted now - if you bring the tool into proximity, the matching image is highlighted to make it easier to know which stylus you're about to configure. Oh, and in the process we added a new SVG for AES styli too to make the picture look more like the actual physical tool. The <blink> tag may no longer be cool but at least we can disco our way through the stylus configuration now.

More Pressure Curves (GNOME 46)

GNOME Settings historically presents a slider from "Soft" to "Firm" to adjust the feel of the tablet tip (which influences the pressure values sent to the application). Behind the scenes this was converted into a set of 7 fixed curves but thanks to a old mutter bug those curves only covered a small amount of the possible range. This is now fixed so you can really go from pencil-hard to jelly-soft and the slider now controls an almost-continous range instead of just 7 curves. Behold, a picture of slidery goodness:

Miscellaneous fixes

And of course a bunch of miscellaneous fixes. Things that I quickly found were support for Alt in the tablet pad keymappings, fixing of erroneous backwards movement when wrapping around on the ring, a long-standing stylus button mismatch, better stylus naming and a rather odd fix causing configuration issues if the eraser was the first tool ever to be brought into proximity.

There are a few more things in the pipe but I figured this is enough to write a blog post so I no longer have to remember to write a blog post about all this.

How & Why to Connect Threads to the Fediverse—including Mastodon

This week Threads announced it is rolling out wider support for connecting with “the fediverse,” and it’s a no-brainer to opt in to this feature if you’re on Threads.

Abstract animation of "the fediverse"

What is the fediverse?

The fediverse, sometimes referred to as “the open social web,” is a network of individual social networks that interoperate with one another using open standards. This includes social networks like Mastodon, but also includes news aggregation service Flipboard, individual WordPress sites, video streaming services powered by PeerTube, photo sharing communities using Pixelfed, link-aggregators using Lemmy, among many others.

It’s a tired-but-true comparison to think of it like email: you can use Gmail, I can use Fastmail, and we can all interact with a friend using their company’s self-hosted email service because email is fundamentally interoperable using open standards and protocols. The fediverse is just like that, but for social communities.

If you want to dive in to understand even more, The Verge has a great fediverse explainer—or if you want to hear it from Meta, they’ve written up this fancy release from their perspective.

Why connect to the fediverse?

So as someone who uses Threads, why should you opt into connecting your account to the fediverse? In short, because it’s an easy way to expand your reach! It’s difficult to definitively count the number of users across the entire fediverse due to the network’s decentralized nature, but current estimates show there are around 11 million users on the fediverse, and growing. That’s a lot of people out there!

Even if you’re someone who doesn’t really care about reaching a bunch of people with your posts, think of it this way: connecting to the fediverse makes it so you’re not artificially forcing anyone who wants to keep in touch (or see what you’re up to, or laugh at your shitposts) to use Facebook—since fundamentally, Threads is Instagram is Facebook (and accounts/posts are shared between Meta’s different platforms).

In practice, this means that I can be on my own Mastodon server (with the fancy handle @cassidy@blaede.family) and follow your account, e.g. @YourThreadsUsername​@threads.net. Remember that email comparison earlier? You can see how it’s pretty similar! Any of your public posts on Threads will then show up in my social feed alongside posts from people I follow on Mastodon and other sites.

Screenshots from Threads and Mastodon showing how the same post is presented

On your side, you’ll see when anyone on the fediverse “likes” or replies to your post, too—all without leaving Threads.

Screenshots from Threads showing what fediverse integration looks like

But for now, Meta is making you opt in to this feature since it’s pretty new. If you don’t turn on sharing to the fediverse on your Threads account, nobody will be able to find you outside of Threads itself.

If you want to learn even more, Meta has this fediverse-specific privacy guide for Threads.

How to turn on fediverse sharing

Luckily, turning on fediverse sharing for your Threads account is simple:

  1. Go to your Account Settings in Threads
  2. Select “Fediverse sharing”
  3. Follow the on-screen instructions

Fediverse sharing screen in Threads Account Settings

That’s it! You’re set up to share across the fediverse so that people like me—and millions of others—can follow you from our preferred social networks. Plus, you can always turn it back off later if you want from the same place if you change your mind!

Images via Meta newsroom

June 22, 2024

Fixing a memory leak of xmlEntityPtr in librsvg

Since a few weeks ago, librsvg is now in oss-fuzz — Google's constantly-running fuzz-testing for OSS projects — and the crashes have started coming in. I'll have a lot more to say soon about crashes in Cairo, which is where the majority of the bugs are so far, but for now I want to tell you about a little bug I just fixed.

The fuzzer found a memory leak that happens when librsvg tries to parse an invalid XML document that has definitions for XML entities — the things that you normally reference like &foo; in the middle of the XML.

For example, this invalid document causes librsvg to leak:

<!DOCTYPEY[<!ENTITY a ''

Valgrind reports this:

$ valgrind --leak-check=full ./target/debug/rsvg-convert leak.svg 
...
Error reading SVG leak.svg: XML parse error: Error domain 1 code 37 on line 2 column 1 of data: xmlParseEntityDecl: entity a not terminated

==3750== 
==3750== HEAP SUMMARY:
==3750==     in use at exit: 78,018 bytes in 808 blocks
==3750==   total heap usage: 1,405 allocs, 597 frees, 205,161 bytes allocated
==3750== 
==3750== 247 (144 direct, 103 indirect) bytes in 1 blocks are definitely lost in loss record 726 of 750
==3750==    at 0x4845794: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==3750==    by 0x4BD857F: xmlCreateEntity (entities.c:158)
==3750==    by 0x4BD932B: xmlNewEntity (entities.c:451)
==3750==    by 0x2EBC75: rsvg::xml::xml2_load::sax_entity_decl_cb (xml2_load.rs:152)
==3750==    by 0x4BED6D8: xmlParseEntityDecl (parser.c:5647)
==3750==    by 0x4BEF4F3: xmlParseMarkupDecl (parser.c:7024)
==3750==    by 0x4BEFB95: xmlParseInternalSubset (parser.c:8558)
==3750==    by 0x4BF50E9: xmlParseDocument (parser.c:11072)
==3750==    by 0x2ED266: rsvg::xml::xml2_load::Xml2Parser::parse (xml2_load.rs:466)
==3750==    by 0x4A8C49: rsvg::xml::XmlState::parse_from_stream::{{closure}} (mod.rs:628)
==3750==    by 0x2ACA92: core::result::Result<T,E>::and_then (result.rs:1316)
==3750==    by 0x34D4E2: rsvg::xml::XmlState::parse_from_stream (mod.rs:627)
==3750== 
==3750== LEAK SUMMARY:
==3750==    definitely lost: 144 bytes in 1 blocks
==3750==    indirectly lost: 103 bytes in 3 blocks
==3750==      possibly lost: 0 bytes in 0 blocks
==3750==    still reachable: 73,947 bytes in 746 blocks
==3750==         suppressed: 0 bytes in 0 blocks

Let's see what happened.

The code in question

Even after the port to Rust, librsvg still uses libxml2 for parsing XML. So, librsvg has to deal with raw pointers incoming from libxml2 and it must do their memory management itself, since the Rust compiler doesn't know what to do with them automatically.

Librsvg uses the SAX parser, which involves setting up callbacks to process events like "XML element started", or "an entity was defined".

If you have a valid document that has entity definitions like these:

<!ENTITY foo "#aabbcc">
<!ENTITY bar "some text here">

Then libxml2's SAX parser will emit two events to instruct your code that it should define entities, one for foo and one for bar, with their corresponding content. Librsvg stores these in a hash table, since it has to be able to retrieve them later when the SAX parser requests it. In detail, libxml2 requires that you create an xmlEntityPtr by calling xmlNewEntity() and then keep it around.

xmlEntityPtr xmlNewEntity (xmlDocPtr      doc,
                           const xmlChar *name,
                           int            type,
                           const xmlChar *ExternalID,
                           const xmlChar *SystemID,
                           const xmlChar *content);

Later, you must free each of your stored entities with xmlFreeNode() (it supports different data types, including entities), or if you are using libxml2 2.12.0 or later, with xmlFreeEntity().

void xmlFreeNode (xmlNodePtr node);
void xmlFreeEntity (xmlEntityPtr entity);

Librsvg creates a SAX parser from libxml2, calls it to do the parsing, and then frees the entities at the end. In the following code, XmlState is the struct that librsvg uses to hold the temporary state during parsing: a partially-built XML tree, some counters on the number of loaded elements, the current element being processed, things like that. The build_document() method is called at the very end of XmlState's lifetime; it consumes the XmlState and returns either a fully-parsed and valid Document, or an error.

struct XmlState {
    inner: RefCell<XmlStateInner>,  // the mutable part

    // ... other immutable fields here
}

type XmlEntityPtr = *mut libc::c_void;

struct XmlStateInner {
    // ... a few fields for the partially-built XML tree, current element, etc.
    document_builder: DocumentBuilder,

    // Note that neither XmlStateInner nor Xmlstate implement Drop.
    //
    // An XmlState is finally consumed in XmlState::build_document(), and that
    // function is responsible for freeing all the XmlEntityPtr from this field.
    //
    // (The structs cannot impl Drop because build_document()
    // destructures and consumes them at the same time.)
    entities: HashMap<String, XmlEntityPtr>,
}

impl XmlState {
    fn build_document(
        self,
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Document, LoadingError> {
        // does the actual parsing with a libxml2 SAX parser
        self.parse_from_stream(stream, cancellable)?;

        // consume self, then consume inner, then consume document_builder by calling .build()
        let XmlState { inner, .. } = self;
        let mut inner = inner.into_inner();

        // Free the hash of XmlEntityPtr.  We cannot do this in Drop because we will
        // consume inner by destructuring it after the for() loop.
        for (_key, entity) in inner.entities.drain() {
            unsafe {
                xmlFreeNode(entity);
            }
        }

        let XmlStateInner {
            document_builder, ..
        } = inner;
        document_builder.build()
    }
}

There are many Rust-isms in this code.

  • After doing the actual parsing with parse_from_stream(), self is destructured to consume it and extract its inner field, which is the actual mutable part of the XML loading state.

  • The code frees each xmlEntityPtr stored in the hash table of entities.

  • The inner value, which is an XmlStateInner, is destructured to extract the document_builder field, which gets asked to .build() the final document tree.

Where's the bug?

The bug is in this line at the beginning of the build_document() function:

        self.parse_from_stream(stream, cancellable)?;

The ? after the function call is to return errors to the caller. However, if there is an error during parsing, we will exit the function here, and it will not have a chance to free the values in the key-value pairs among the entities ! Memory leak!

This code had already gone through a few refactorings. Initially I had an impl Drop for XmlState which did the obvious thing of freeing the entities by hand:

impl Drop for XmlState {
    fn drop(&mut self) {
        unsafe {
            let mut inner = self.inner.borrow_mut();

            for (_key, entity) in inner.entities.drain() {
                // entities are freed with xmlFreeNode(), believe it or not
                xmlFreeNode(entity);
            }
        }
    }
}

But at one point, I decided to clean up the way the entire inner struct was to be handled, and decided to destructure it at the end of its lifetime, since that made the code simpler. However, destructuring an object means that you cannot have an impl Drop for it, since then some fields are individually moved out and some are not during the destructuring. So, I changed the code to free the entities directly into build_document() as above.

I missed the case where the parser can exit early due to an error.

The Rusty solution

Look again at how the entities hash table is declared in the struct fields:

type XmlEntityPtr = *mut libc::c_void;

struct XmlStateInner {
    entities: HashMap<String, XmlEntityPtr>,
}

That is, we are storing a hash table with raw pointers in the value part of the key-value pairs. Rust doesn't know how to handle those external resources, so let's teach it how to do that.

The magic of having an impl Drop for a wrapper around an unmanaged resource, like xmlEntityPtr, is that Rust will automatically call that destructor at the appropriate time — in this case, when the hash table is freed.

So, let's use a wrapper around XmlEntityPtr, and add an impl Drop for the wrapper:

struct XmlEntity(xmlEntityPtr);

impl Drop for XmlEntity {
    fn drop(&mut self) {
        unsafe {
            xmlFreeNode(self.0);
        }
    }
}

And then, let's change the hash table to use that wrapper for the values:

    entities: HashMap<String, XmlEntity>,

Now, when Rust has to free the HashMap, it will know how to free the values. We can keep using the destructuring code in build_document() and it will work correctly even with early exits due to errors.

Valgrind's evidence without the leak

# valgrind --leak-check=full ./target/debug/rsvg-convert leak.svg 
==5855== Memcheck, a memory error detector
==5855== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==5855== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==5855== Command: ./target/debug/rsvg-convert leak.svg
==5855== 
Error reading SVG leak.svg: XML parse error: Error domain 1 code 37 on line 2 column 1 of data: xmlParseEntityDecl: entity a not terminated

==5855== 
==5855== HEAP SUMMARY:
==5855==     in use at exit: 77,771 bytes in 804 blocks
==5855==   total heap usage: 1,405 allocs, 601 frees, 205,161 bytes allocated
==5855== 
==5855== LEAK SUMMARY:
==5855==    definitely lost: 0 bytes in 0 blocks
==5855==    indirectly lost: 0 bytes in 0 blocks
==5855==      possibly lost: 0 bytes in 0 blocks
==5855==    still reachable: 73,947 bytes in 746 blocks
==5855==         suppressed: 0 bytes in 0 blocks

Moral of the story

Resources that are external to Rust really work best if they are wrapped at the lowest level, so that destructors can run automatically. Instead of freeing things by hand when you think it's right, let the compiler do it automatically when it knows it's right. In this case, wrapping xmlEntityPtr with a newtype and adding an impl Drop is all that is needed for the rest of the code to look like it's handling a normal, automatically-managed Rust object.

June 21, 2024

New Cambalache development release 0.91.1!

I am please to announce a new development version of Cambalache

This comes with two major dependencies changes, the first one is a very basic port to Adwaita which fixes dark mode support with Gtk4The biggest one is that I have replaced the WebKit WebView used to show widgets in the workspace for a custom Wayland compositor widget based on wlroots.

So far, this is how Cambalache showed windows from a different process in its workspace.Workspace diagram using Bradwayd and WebKit WebView

It would run broadwayd or gtk4-broadwayd backend depending on the gtk version of your project and use a WebView to show all the windows.

With the new approach we do not need the extra broadway backend and also we do not need to run a whole web browser just to show a window.On top of that we get all the obvious optimizations from using Wayland instead of a protocol meant to go over the internet.

For example, with broadway, the client would render the window in memory, the broadway backend would compress the image and sent it over TCP to the webview which has to uncompress it and render it on an HTML5 canvas using JS api.

But now, the client just renders it in shared memory which the compositor use it to create a cairo surface from it and renders it directly in a GtkDrawingArea.

And this is how the new Cambalache looks like when editing its own UI.This also leaves room for improvement by leveraging all the new Gtk infrastructure for graphics offloading (see this blog post from Matthias about it)

As usual with so many changes I expect new bugs so please if you find any,  file them here.

Special thanks goes to emersion, kennylevinsen, vyivel and the wlroots community for their support and awesome project, I would not have been able to do this without wlroots and their help.

Where to get it?

You can get it from Flathub Beta

flatpak remote-add --if-not-exists flathub-beta flathub.org/beta-repo/flathub-

flatpak install flathub-beta ar.xjuan.Cambalache

or checkout main branch at gitlab

git clone https://gitlab.gnome.org/jpu/cambalache.git

Matrix channel

Have any question? come chat with us at #cambalache:gnome.org

Mastodon

Follow me in Mastodon @xjuan to get news related to Cambalache development.

Happy coding!

 

 

 

 

 

Advanced text features and PDF

The basic text model of PDF is quite nice. On the other hand its basic design was a very late 80s "ASCII is everything everyone really needs, but we'll be super generous and provide up to 255 glyphs using a custom encoding that is not in use everywhere else". As you can probably guess, this causes a fair bit of problems in the modern world.

To properly understand the text that follows you should know that there are four different ways in which text and letters need to be represented to get things working:

  • Source text is the "original" written text in UTF-8 (typically)
  • Unicode codepoints represent unique Unicode IDs as specified by the Unicode standard
  • A glyph id uniquely specifies a glyph (basically a series of drawing operations), these are arbitrary and typically unique for each font
  • ActualText is sort of like an AltText for PDF but uses UTF-16BE as was the way of the future in the early 90s

Kerning

The most common advanced typography feature in use is probably kerning, that is, custom spacing between certain letter pairs like "AV" and "To". The PDF text model has native support for kerning and it even supports vertical and horizontal kerning. Unfortunately the way things are set up means that you can only specify horizontal kerning when laying out horizontal text and vertical kerning for vertical text. If your script requires both, you are not going to have a good time.

There are several approaches one can take. The simplest is to convert all text to path drawing operations, which can be placed anywhere with arbitrary precision. This works great for printed documents but also means that document sizes balloon and you can't copypaste text from the document, use screen readers or do any other operation that needs the actual text those shapes represent.

An alternative is to render each glyph as its own text object with exact coordinates. While verbose this works, but since every letter is separate, text selection becomes wonky again. PDF readers seem to have custom heuristics to try to detect these issues and fix text selection in post-processing. Sometimes it works better than at other times.

Everything in PDF drawing operations is based on matrices. Text has its own transform matrix that defines where the next glyph will go. We could specify kerning manually with a custom translation matrix that translates the rendering location by the amount needed. There are two main downsides to this. First of all it would mean that instead of having a stream of glyphs to render, you'd need to define 9 floating point numbers (actually 6 due to reasons) between every pair of glyphs. This would increase the size of you output by a factor of roughly ten. The other downside is that unlike for all other matrices, PDF does not permit you to multiply an existing text state matrix with a new one. You can only replace it completely. So the actual code path would become "tell PDF to draw a glyph, work out what changes it would make to the currently active text matrix, undo that, multiply that matrix with one that has the changes that you wanted to happen and proceed to the next glyph".

Glyph substitution

Most of the time (in most scripts anyway) source text's Unicode codepoints get mapped 1:1 to a font glyph in the final output. Perhaps the most common case where this does not happen is ligatures.

The actual rules when and how this happens are script, font and language dependent. This is something you do not want to do yourself, instead use a shaping engine like Harfbuzz. If you give it the source text as UTF-8 and a font that has the ffi ligature, it will return a list of four glyph ids in the font to use, the way they map back to the original text, kerning (if any) and all of that good stuff.

What it won't give you is the information of what ligatures it replaced your source text with. In this example it will tell you the glyph id of the ffi ligature (2132) but not which Unicode codepoint it corresponds to (0xFB03). You need to tell that number in PDF metadata for the text to work properly in copypaste operations. At first this does not seem like such a big problem, because we have access to the original font file and Freetype. You'd think you can just ask Freetype for the Unicode codepoint for a given font glyph, but you can't. There is a function for finding a glyph for a given Unicode codepoint but mot the other way around. The stackoverflow recommended way of doing this is to iterate over all glyphs until you find the one that is mapped to the desired codepoint. For extra challenge you need to write an ActualText tag in the PDF command stream so that when users copypaste that text they get the original form with each individual letter rather than the ffi Unicode glyph.

All of this means that glyph lookup is basically a O(n^2) operation if it was possible to do. Sometimes it isn't, as we shall now find out.

Alternate forms

OpenType fonts can have multiple different glyphs for the same Unicode codepoint, for example the small caps versions of Noto Serif look like this.

These are proper hand-drawn versions of the glyphs, not ones obtained by scaling down upper case letters. Using these is simple, you tell Harfbuzz to use the small caps versions when shaping and then it does everything for you. For this particular font upper case small caps glyphs are the same as regular upper case glyphs. The lower case ones have their own glyphs in the font. However, according to Freetype at least, those glyphs are not mapped to any Unicode codepoint. Conceptually a small caps lower case "m" should be the same as a regular lower case "m". For some reason it is not and, unless I'm missing something, there is no API that can tell you that. The only way to do it "properly" is to track this yourself based on your input text and requirements.

How does CapyPDF handle all this?

In the same way pretty much all PDF generator libraries do: by ignoring all of it. CapyPDF only provides the means to express all underlying functionality in the PDF library. It is the responsibility of the client application to form glyph sequences and related PDF metadata in the way that makes sense for their application and document structure.

June 20, 2024

GSoC 2024: Week 1-2 Report

Project

Add support for the latest GIR attributes and GI-Docgen formatting to Valadoc.

Mentor

Lorenz Wildberg

Project Planning

In the Phase I of this project, our focus is on adding support for latest GObject Introspection attributes to vapigen and the vala compiler. Currently we are adding support for the glib:sync-func, glib:async-func, and glib:finish-func attribute for method, and the default-value attribute for property. To accomplish this, we need to understand how the GirParser builds the AST from the GIR data, and how the GirWriter writes into the GIR file using the AST. The latter is a bit easy, because the GirWriter simply visits all code nodes and prints each one of them into the GIR file. However, we found the first one to be pretty challenging, about which we will discuss in a bit!

Week 1: Support for glib:sync-func, glib:async-func, and glib:finish-func attributes

Firstly we went through vala GIR files to decide how to implement support for these attributes:

                                    The glib:finish-func attribute for method

Then we decided to begin by adding support for the glib:sync-func, glib:async-func and glib:finish-func attributes:

  • glib:sync-func: denotes the synchronous version of the asynchronous function.
  • glib:async-func: denotes the asynchronous version of a synchronous function.
  • glib:finish-func: denotes the finish function of the asynchronous function.

In the GirParser: It is clear that the property coroutine of Vala.Method is true for both async-func and finish-func in the AST. This is because there is only an asynchronous function, but no finish function in Vala. We have implemented support for glib:finish-func by using this attribute to identify whether a node in the AST created by the GirParser is an async-func. With the introduction of this attribute we no longer need to guess the finish function corresponding to an async-func by appending "_finish" to the name of the async-func. We can easily determine the finish function from the glib:finish-func attribute using:

                m.finish_func = girdata["glib:finish-func"];

In the GirWriter: To write these attributes onto the GIR files, we made the GirWriter to:
  • write the glib:async-func attribute if the method is a synchronous function and the corresponding asynchronous function exists.
  • write the glib:sync-func attribute and the glib:finish-func attribute if the method is an asynchronous function.

We also added a new property to Vala.Method - the property finish_func which, in addition to telling us the finish-func of the method, also tells if the method is an async-func (finish_func would be null if the method were not an async-func), thus making it possible to differentiate between async-func and finish-func in the GIR:

                                                                            

Week 2: Support for default-value attribute for property

As the name suggests, the default-value attribute gives the default value of a property. 

                                    The default-value attribute for property

It is worth mentioning that support for this attribute was already present in the GirWriter. So we only needed to implement support in the GirParser, and also ensure that the default value is outputted in the generated vapi files by adding support to the CodeWriter. We used the default-value attribute to set the initializer property of  Vala.Property. We then used initializer to output the default value onto the generated vapi files. This was a challenge to implement because the initializer is a Vala.Expression but the default-value that we get from the girdata is a string. So we need to convert the expression to a string in CodeWriter and parse the string into an expression in the GirParser. We are trying to accomplish by writing a function value_string_to_literal_expression in the GirParser:
 


Apart from working on these two attributes and adding test cases for them, we also worked on the merge request !374 which generates properties from getters and setters of compact classes in bindings. It needed some minor fixes and we are now closer to getting it merged. 

Challenges

Now let's talk about the challenges, because after all challenges are what make a project fun! As part of this project, we are working to add support for the latest GObject Introspection attributes in the GirParser. The GirParser works by building an AST of different code nodes in the code context. This involves parsing different symbols in the source file, and because there are so many symbols, the GirParser is dependent on over a hundred different files that are located in the directory vala/vala, and many of them have over thousands of lines of code. We need to understand how all of this is put together and how it results in the GirParser being able to parse GIR files. Currently we are trying to use a structured approach to code reading so that we can implement support for these attributes within the time we have to complete this project!

Thanks for reading :) Stay tuned for more updates!

June 19, 2024

printf(“Hello GNOME <3”)

Greetings, GNOME community! My name is Rachel and I’m currently studying computer science at the University of Cambridge. I’ve been looking for an opportunity to learn to contribute to the open source community and am super excited to be working with GNOME as a gsoc contributor this summer 😀

I’ll be working with my mentors Carlos Garnacho and Sam Thursfield, as well as my fellow contributor, Divyansh Jain (aka Demigod) in creating a web IDE for testing and running queries in TinySPARQL, as an effort to improve developer experience when working with TinySPARQL. I will be using this blog to update the community about our project and my current tasks, so please look forward to our work. Bye for now!

June 18, 2024

Outreachy Internship Blog Series: Self Introduction

Hi! I’m Udo Ijibike. This summer, I am contributing to a user research project as a GNOME intern in Outreachy. My mentors are Allan Day and Aryan Kaushik, and this is the first in a series of blog posts chronicling my internship experience.

About Me

I’m a passionate User Experience (UX) Designer from Nigeria. I love technology, and despite my degree in engineering, I’ve always had an appreciation for the arts and a keen interest in psychology. These seemingly disparate interests ultimately converge through UX Design in a way that’s incredibly fulfilling for me.

My core values include self-awareness, progress, and curiosity. Self-awareness helps me set the right goals, focusing on progress keeps me grounded enough to enjoy the process, and curiosity keeps me open to new ideas.

Why I Applied to Outreachy

Outreachy is a platform that advocates for everyone’s potential to contribute meaningfully to science and technology, and its goal validated my aspirations of being a UX Designer. I discovered Outreachy four months into my pivot to UX Design. This early in my transition, doubts were frequent, so learning about Outreachy strengthened my resolve to persevere.

It took a few rounds of deliberation before I mustered the courage to apply. I feel truly honored to have been selected as an intern on my second attempt, and the experience so far has been life-changing.

Why GNOME

The internship application had two stages: the initial application and the contribution period. Getting through the initial application was significant, but it was also just the first step. However, I saw the opportunity to contribute to an impactful project and work with a mentor for an entire month as invaluable for learning. Therefore, I approached the contribution phase resolved to find a project where I could contribute and improve my UX Design skills in a meaningful way.

I was drawn to GNOME because of its design-driven approach to solving complex problems in Free and Open Source Software. Its commitment to user-friendliness and inclusivity in FOSS made an impression on me and the internship project itself was the ideal match for my objectives. Therefore, there wasn’t a decision to make; GNOME felt like the only choice for me.

Internship Project

My internship project is a user research project that will focus on identifying usability issues and opportunities for improvement in GNOME software.

Over the next few months, we’ll be conducting user interviews, surveys, and user tests to derive actionable insights for enhancing select software within the GNOME ecosystem to better meet the needs of GNOME users.

Looking Forward

I’m thrilled to be learning so much from contributing to a project that I’m very excited about in such a welcoming community. As my internship continues, I’ll continue to share more about my GNOME experience and our project in subsequent posts.

Thank you for reading!

 

Update on Newton, the Wayland-native accessibility project

Several months ago, I announced that I would be developing a new accessibility architecture for modern free desktops. Now, I’m happy to provide an update on this project, code-named Newton. Before I begin, I’d like to thank the Sovereign Tech Fund for funding this work, and the GNOME Foundation for managing the contract.

A word on the name

When choosing a working name for this project, I decided to follow the convention established by Wayland itself, and followed by a couple of other projects including the Weston compositor, of naming Wayland and related projects after places in New England. Newton, Massachusetts is the town where the Carroll Center for the Blind is located.

Demo

Here’s a demo of me using a modified GNOME OS image with a couple of GTK 4 apps running inside Flatpak sandboxes without the usual accessibility exception.

 

Builds for testing

The following builds are based on GNOME 46.2 with my current unmerged Newton modifications. The corresponding Git branches are linked below.

I’ve also built a Flatpak repository, but it isn’t currently signed, so it doesn’t have a .flatpakrepo file. You can add it manually with this command:

flatpak remote-add --user --no-gpg-verify newton https://mwcampbell.us/gnome/gnome-46-newton/repo/

Because the Flatpak repository is based on GNOME 46, you can use Flatpak apps that were built for GNOME 46 with the Newton version of the org.gnome.Platform runtime. You can install that runtime with this command:

flatpak install newton org.gnome.Platform

Source repositories

Here are the links to the unmerged Newton branches of the individual components:

Here are my branches of the Buildstream metadata repositories, used to build the GNOME OS image and Flatpak runtime:

freedesktop-sdk gnome-build-meta

Only this last repository needs to be checked out directly. With it, one should be able to reproduce my builds.

If you want to do your own builds of the relevant components, my addition to the Orca README has instructions. The Orca GitLab project linked above is also a good place to provide end-user feedback.

What’s working so far

I’ve now implemented enough of the new architecture that Orca is basically usable on Wayland with some real GTK 4 apps, including Nautilus, Text Editor, Podcasts, and the Fractal client for Matrix. Orca keyboard commands and keyboard learn mode work, with either Caps Lock or Insert as the Orca modifier. Mouse review also works more or less. Flat review is also working. The Orca command to left-click the current flat review item works for standard GTK 4 widgets.

As shown in the recorded demo above, Newton-enabled applications can run inside a Flatpak sandbox without the usual exception for the AT-SPI bus, that is, with the --no-a11y-bus option to flatpak run. Support for such sandboxing was one of the major goals of this project.

The standard GTK text widgets, including GtkEntry and GtkTextView have fairly complete support. In particular, when doing a Say All command, the caret moves as expected. I was also careful to support the full range of Unicode, including emoji with combining characters such as skin tone modifiers.

What’s broken or not done yet

The GNOME Shell UI itself is not yet using Newton, but AT-SPI. The UI is still accessible with the Newton versions of Mutter and Orca, but it’s being accessed via AT-SPI, meaning that performance in this UI is not representative of Newton, and mouse review doesn’t work for this UI.

Synthesizing mouse events isn’t yet supported on Wayland. This means that while the Orca command for left-clicking the current flat review item is expected to work for standard GTK 4 widgets, that command doesn’t work for widgets that don’t support the appropriate accessible action, and the right-click command doesn’t work.

AccessKit doesn’t currently support sentences as text boundaries. This means that Orca’s Say All command fallback so treading by line, leading to unnatural breaks in the speech.

The GTK AccessKit implementation doesn’t yet support out-of-tree text widgets that implement the GtkAccessibleText interface, such as the GTK 4 version of the vte terminal widget. This means that GTK 4-based terminal apps like kgx don’t yet work with Newton. I don’t yet know how I’ll solve this, as the current GtkAccessibleText interface is not a good fit for the push-based approach of AccessKit and Newton.

Text attributes such as font, size, style, and color aren’t yet exposed. AccessKit has properties for these attributes, but the AccessKit AT-SPI backend, much of which is reused by the Newton AT-SPI compatibility library, doesn’t yet support them.

Tables aren’t yet supported. AccessKit has properties for tables, and the GTK AccessKit backend is setting these properties, but the AccessKit AT-SPI backend doesn’t yet expose these properties.

Some states, such as “expanded”, “has popup”, and “autocomplete available”, aren’t yet exposed.

I’m aware that some GTK widgets don’t have the correct roles yet.

When Caps Lock is set as the Orca modifier, you can’t yet toggle the state of Caps Lock itself by pressing it twice quickly.

Orca is the only assistive technology supported so far. In particular, assistive technologies that are implemented inside GNOME Shell, like the screen magnifier, aren’t yet supported.

Bonus: Accessible GTK apps on other platforms

Because we decided to implement Newton support in GTK by integrating AccessKit, this also means that, at long last, GTK 4 apps will be accessible on Windows and macOS as well. The GTK AccessKit implementation is already working on Windows, and it shouldn’t be much work to bring it up on macOS. To build and test on Windows, check out the GTK branch I linked above and follow the instructions in its README. I’ve built and tested this GTK branch with both Visual Studio (using Meson and the command-line Visual C++ tools) and MSYS 2. I found that the latter was necessary for testing real-world apps like gnome-text-editor.

Architecture overview

Toolkits, including GTK, push accessibility tree updates through the new accessibility Wayland protocol in the wayland-protocols repository linked above. The latest accessibility tree update is part of the surface’s committed state, so the accessibility tree update is synchronized with the corresponding visual frame. The toolkit is notified when any accessibility clients are interested in receiving updates for a given surface, and when they want to stop receiving updates, so the toolkit can activate and deactivate its accessibility implementation as needed. This way, accessibility only consumes CPU time and memory when it’s actually being used. The draft Wayland protocol definition includes documentation with more details.

Assistive technologies or other accessibility clients currently connect to the compositor through a D-Bus protocol, defined in the Mutter repository linked above. By exposing this interface via D-Bus rather than Wayland, we make it easy to withhold this communication channel from sandboxed applications, which shouldn’t have this level of access. Currently, an assistive technology can find out about a surface when it receives keyboard focus or when the pointer moves inside it, and can then ask to start receiving accessibility tree updates for that surface.

The same D-Bus interface also provides an experimental method of receiving keyboard events and intercepting (“grabbing”) certain keys. This is essential functionality for a screen reader such as Orca. We had originally planned to implement a Wayland solution for this requirement separately, but decided to prototype a solution as part of the Newton project to unblock realistic testing and demonstration with Orca. We don’t yet know how much of this design for keyboard event handling will make it to production.

The compositor doesn’t process accessibility tree updates; it only passes them through from applications to ATs. This is done using file descriptor passing. Currently, the file descriptors are expected to be pipes, but I’ve thought about using shared memory instead. The latter would allow the AT to read the accessibility tree update without having to block on I/O; this could be useful for ATs that run inside Mutter itself, such as the screen magnifier. (The current Newton prototype doesn’t yet work with such ATs.) I don’t know which approach is overall better for performance though, especially when one takes security into account.

The serialization format for accessibility tree updates is currently JSON, but I’m open to alternatives. Obviously we need to come to a decision on this before this work can go upstream. The JSON schema isn’t yet documented; so far, all code that serializes and deserializes these tree updates is using AccessKit’s serialization implementation.

In addition to tree updates, this architecture also includes one other form of serialized data: accessibility action requests. These are passed in the reverse direction, from the AT to the application via the compositor, again using file descriptor passing. Supported actions include moving the keyboard focus, clicking a widget, setting the text selection or caret position, and setting the value of a slider or similar widget. The notes about serialization of tree updates above also apply to action requests.

Note that the compositor is the final authority on which tree updates are sent to the ATs at what time, as well as on which surface has the focus. This is in contrast with AT-SPI, where ATs receive tree updates directly from applications, and any application can claim to have the focus at any time. This is important for security, especially for sandboxed applications.

Open architectural issues

The biggest unresolved issue at this point is whether the push-based approach of Newton, the motivation for which I described in the previous post, will have unsolvable drawbacks, e.g. for large text documents. The current AccessKit implementation for GtkTextView pushes the full content of the document, with complete text layout information. On my brand new desktop computer, this has good performance even when reading an 800 KB ebook, but of course, there are bigger documents, and that’s a very fast computer. We will likely want to explore ways of incrementally pushing parts of the document based on what’s visible, adding and removing paragraphs as they go in and out of view. The challenge is to do this without breaking screen reader functionality that people have come to depend on, such as Orca’s Say All command. My best idea about how to handle this didn’t occur to me until after I had finished the current implementation. Anyway, we should start testing the current, naive implementation and see how far it takes us.

The current AT protocol mentioned above doesn’t provide a way for ATs to explore all accessible surfaces on the desktop; they can only find out about an accessible surface if it receives keyboard focus or if the pointer moves inside it. A solution to this problem may be necessary for ATs other than Orca, or for automated testing tools which currently use AT-SPI.

The current architecture assumes that each Wayland surface has a single accessibility tree. There isn’t yet an equivalent to AT-SPI’s plugs and sockets, to allow externally generated subtrees to be plugged into the surface’s main tree. Something like this may be necessary for web rendering engines.

I’m not yet sure how I’ll implement Newton support in the UI of GNOME Shell itself. That UI runs inside the same process as the compositor, and isn’t implemented as Wayland surfaces but as Clutter actors (the Wayland surfaces themselves map to Clutter actors). So the existing AccessKit Newton backend won’t work for this UI as it did for GTK. One option would be for Mutter to directly generate serialized tree updates without going through AccessKit. That would require us to finalize the choice of serialization format sooner than we otherwise might. While not as convenient as using the AccessKit C API as I did in GTK, that might be the least difficult option overall.

Newton doesn’t expose screen coordinates, for individual accessible nodes or for the surfaces themselves. ATs are notified when the pointer moves, but the compositor only gives them the accessible surface ID that the pointer is inside, and the coordinates within that surface. I don’t yet have a solution for explore-by-touch, alternative input methods like eye-tracking, or ATs that want to draw overlays on top of accessible objects (e.g. a visual highlight for the screen reader cursor).

Next steps

The earlier section on what’s broken or not done yet includes several issues that should be straightforward to fix. I’ll fix as many of these as possible in the next several days.

But the next major milestone is to get my GTK AccessKit integration reviewed and merged. Since Newton itself isn’t yet ready to go upstream, the immediate benefit of merging GTK AccessKit support would be accessibility on Windows and macOS. The current branch, which uses the prototype Newton backend for AccessKit, can’t be merged, but it wouldn’t be difficult to optionally support AccessKit’s AT-SPI backend instead, while keeping the Newton version on an unmerged branch.

The main challenge I need to overcome before submitting the GTK AccessKit integration for review is that the current build system for the AccessKit C bindings is not friendly to distribution packagers. In particular, one currently has to have rustup and a Rust nightly toolchain installed in order to generate the C header file, and there isn’t yet support for installing the header file, library, and CMake configuration files in FHS-compliant locations. Also, that build process should ideally produce a pkg-config configuration file. My current gnome-build-meta branch has fairly ugly workarounds for these issues, including a pre-generated C header file checked into the repository. My current plan for solving the nightly Rust requirement is to commit the generated header file to the AccessKit repository. I don’t yet know how I’ll solve the other issues; I might switch from CMake to Meson.

The other major thing I need to work on soon is documentation. My current contract with the GNOME Foundation is ending soon, and we need to make sure that my current work is documented well enough that someone else can continue it if needed. This blog post itself is a step in that direction.

Help wanted: testing and measuring performance

I have not yet systematically tested and measured the performance of the Newton stack. To be honest, measuring performance isn’t something that I’m particularly good at. So I ask that Orca users try out the Newton stack in scenarios that are likely to pose performance problems, such as large documents as discussed above. Then, when scenarios that lead to poor performance are identified, it would be useful to have someone who is skilled with a profiler or similar tools help me investigate where the bottlenecks actually are.

Other desktop environments

While my work on Newton so far has been focused on GNOME, I’m open to working with other desktop environments as well. I realize that the decision to use D-Bus for the AT client protocol won’t be universally liked; I suspect that wlroots-based compositor developers in particular would rather implement a Wayland protocol extension. Personally, I see the advantages and disadvantages of both approaches, and am not strongly attached to either. One solution I’m considering is to define both D-Bus and Wayland protocols for the interface between the compositor and ATs, and support both protocols in the low-level Newton client library, so each compositor can implement whichever one it prefers. Anyway, I’m open to feedback from developers of other desktop environments and compositors.

Conclusion

While the Newton project is far from done, I hope that the demo, builds, and status update have provided a glimpse of its potential to solve long-standing problems with free desktop accessibility, particularly as the free desktop world continues to move toward Wayland and sandboxing technologies like Flatpak. We look forward to testing and feedback from the community as we keep working to advance the state of the art in free desktop accessibility.

Thanks again to the Sovereign Tech Fund and the GNOME Foundation for making this work possible.

Status update, 18th June 2024

Podcasts

If you’re into podcasts, the Blindboy Podcast is possibly the best one. Recent episode The State of the World begins on a pointy rock off the coast of Ireland that houses a 6th century monastery.

Photo of Skellig Michael
Skellig Michael – Photo credit – Tristan Reville

Then he raises the question of why, when the Russian government & the Russian military commit war crimes, US & European leaders apply punishing economic sanctions to Russia, and when the government and the military of Israel commit atrocities and war crimes, US & European leaders ask them nicely if they could please stop at some point, or at least commit slightly fewer of them.

On that note, I’d like to shout out the Bands Boycott Barclays movement in the UK — our politicians have failed, but at least our musicians aren’t afraid to follow the money and stand up for human rights.

Heisenbugs and spaghetti code

In computer programming jargon, a heisenbug is a software bug that seems to disappear or alter its behavior when one attempts to study it. (Wikipedia)

In the year 2018 I was passing time in Barcelona waiting to start a new job (and a new life, as it turned out). It was the middle of July and everyone had left for the summer. Meanwhile GNOME had recently gained a Gitlab instance and for the first time we could run automated test pipelines in Gitlab CI, so I set up initial CI pipelines for Tracker and Tracker Miners. (Which, by the way, are undergoing a rename).

Tracker Miners already has a testsuite, but in 2018 it was only run when someone decided to run it locally. Remember that our filesystem indexer is implemented as many GIO async callbacks, which means it is complex spaghetti code. These callbacks can fire in a different order depending on various factors. Tracker responds to filesystem notifications and disk reads, which are unpredictable. And of course, it has bugs — in 2018 nobody much was funding maintenance Tracker or Tracker Miners, and many of these would trigger, or not, depending on the execution order of the internal async callbacks.

So, many of the tests would just randomly fail, making the new CI rather useless.

Thanks to a lot of hard work by Carlos and myself, we documented and fixed these issues and the indexer is in a much better state. It was not much fun! I would rather be eating spaghetti than tracing through spaghetti code. I guess somewhere I made bad life choices.

Bowl of spaghetti

In 2021, somehow not learning my lesson, I adopted the nascent openQA tests for GNOME OS that were developed by one of my Codethink colleagues. These have been running for 3 years now at a “beta” quality service, and have caught some interesting bugs along the way.

In late 2023 we noticed a seemingly random test failure, where the machine under test makes it through initial setup, but didn’t get to a user session. Let’s call this issue 62.

This issue reproduces a lot, when you don’t want it to, and rarely if ever when you do. Here’s a recent example. See how the initial setup stage (gnome_welcome) passes, but the transfer to the final user session never happens:

openQA test result with gnome_desktop test failing

Random test failures make a test framework completely useless – if “test failed” can mean “your code is fine but fate is not on your side today”, then it’s not a useful signal for anyone. So for the GNOME OS automated testing to progress, we need to fix this intermittent failure.

GNOME OS runs many different processes during startup, which can start in whatever order the systemd daemon decides to start them, and can then start child processes of their own. No two boots are exactly the same. The graphical startup is driven by systemd, GDM, gnome-session, gnome-initial-setup and GNOME Shell, context switching between themselves hundreds of times following D-Bus message traffic. You could say that this is an extra large portion of spaghetti, with toxic meatballs.

As part of the STF engagement with GNOME OS, Neill Whillans spent a considerable amount of time comparing logs from good and bad boots, investigating the various processes that run at start-up time, and trying to catch the boot failure locally. I’ve tried my best to help as time permits (which it mostly does not).

Fundamentally, the issue is that there are two gnome-shell processes running, one for the initial-setup user and one for the new ‘testuser’ which should take over the session. GDM is supposed to kill the initial-setup shell when the new ‘testuser’ shell begins. This doesn’t happen.

We have a theory that its something failing during the startup of the new gnome-shell. The layout mechanism looks very complex, so its entirely possible that there is a heisenbug in there somewhere. As you would expect, enabling debug logs for GNOME Shell causes the issue to go away completely.

There’s a lesson here, which is that excessive complexity kills motivation, and ultimately kills projects. We could have done quite a lot in the last 7 months if not for this issue. Let’s all try to keep our code simple, debuggable and documented. We have succeeded in the past at killing overly complex abstractions in GNOME — remember CORBA? Perhaps we need to do so again.

What happens next? I am not sure – I think we may just have to give up on the end-to-end tests for the time being, as we can’t justify spending more of the current budget on this, and I’ve done enough volunteer debugging and documentation efforts for the time being – I plan to spend the summer evenings in a hammock, far away from any GNOME code, perhaps eating spaghetti.

June 17, 2024

Toolbx now enables the proprietary NVIDIA driver

… and why did it take so long for that to happen?

If you build Toolbx from Git and install it to your usual prefix on a host operating system with the proprietary NVIDIA driver, then you will be able to use the driver on all freshly started Toolbx containers. Just like that. There’s no need to recreate your containers or to use some special option. It will just work.

How does it work?

Toolbx uses the NVIDIA Container Toolkit to generate a Container Device Interface specification on the host during the toolbox enter and toolbox run commands. This is a JSON or YAML description of the environment variables, files and other miscellaneous things required by the user space part of the proprietary NVIDIA driver. Containers share the kernel space driver with the host, so we don’t have to worry about that. This specification is then shared with the Toolbx container’s entry point, which is the toolbox init-container command running inside the container. The entry point handles the hooks and bind mounts, while the environment variables are handled by the podman exec process running on the host.

It’s worth pointing out that right now this neither uses nvidia-ctk cdi generate to generate the Container Device Interface specification nor podman create --device to consume it. We may decide to change this in the future, but right now this is the way it is.

The main problem with podman create is that the specification must be saved in /etc/cdi or /var/run/cdi, both of which require root access, for it to be visible to podman create --device. Toolbx containers are often used rootless, so requiring root privileges for hardware support, something that’s not necessary on the host, will be a problem.

Secondly, updating the toolbox(1) binary won’t enable the proprietary NVIDIA driver in existing containers, because podman create only affects new containers.

Therefore, Toolbx uses the tags.cncf.io/container-device-interface Go APIs, which are also used by podman create, to parse and apply the specification itself. The hooks in the specification are a bit awkward to deal with. So, at the moment only ldconfig(8) is supported.

The issue with nvidia-ctk is relatively minor and is because it’s another different binary. It makes error handling more difficult, and downstream distributors and users of Toolbx need to be aware of the dependency. Instead, it’s better to directly use the github.com/NVIDIA/go-nvlib and github.com/NVIDIA/nvidia-container-toolkit Go APIs that nvidia-ctk also uses. This offers all the usual error handling facilities in Go and ensures that the dependency won’t go missing.

Why did it take so long?

Well, hardware support needs hardware, and sometimes it takes time to get access to it. I didn’t want to optimistically throw together a soup of find(1), grep(1), sed(1), etc. calls without any testing in the hope that it will all work out. That approach may be fine for some projects, but not for Toolbx.

Red Hat recently got me a ThinkPad P72 laptop with a NVIDIA Quadro P600 GPU that let me proceed with this work.

GSoC 2024: Week 3 Report

It’s been a while since I have given any updates, so here we are.

I listed down the demos that needed to be ported according to their length (S, M, L depending on the Lines of code)

I planned to work on 3 demos daily, 1 LOC type each, which was different from how it panned out. I’ll attach the list here for it to be more clear for everyone.

Week 1

The first demo I worked on was the Dialog demo. It was straightforward, but one thing that kept confusing me was the package declarations and how to know which packages to include. Only after going through the documentation of the functions that didn't work, did I understand the exact terms to use to declare these packages.

I also added the Python port for this demo, one thing (not specific for this port) but the first time I got to know about was the use of `_parameters` which are parameters that may or may not be used by the function but are still needed by the signal calls.

Actions demo took me some time to understand and implement mostly because of the lack of understanding of how Simple Actions work and the `notify.connect` function parameters. I also needed to make some changes to make the user-visible strings according to the style guide. Another interesting thing was that the Vala formatter checks the surrounding code and then formats the rest of the code. Initially, my code was not being formatted correctly because I had missed a space when declaring the main method.

All of the code improvements/suggestions were given to me by my reviewers Diego Ivan and Lorenz Wildberg. I am thankful to them for going through each of my demo ports (No matter how messy or unclean the code was) and answering all of my questions and doubts. If you’re reading this Diego I’m sorry for my Screencast port.

Okay, the next demo I worked on was CSS Gradients. This was a bigger port and needed the creation of different methods for generating and updating the CSS values. If you need more information about what each demo does, I recommend reading blogs written by Akshay and Sriyansh. This demo uses methods of the Gtk.StyleContext class, the class is deprecated and hence the CLI produces an error when running the checks. Sonny has had to make an exception for it in the CLI to make it work.

One thing that helped me during all of the ports was to see the previously written code and understand how it was implemented. The style guide was also helpful in keeping the code clean and aligned with the other demos. At a certain point, I combined all of these along with improvement suggestions from my reviewers into a notes file.

Week 2

(This week will look more fruitful, I had good flow days this week and was starting to understand more about how the ports function).

Drawing Area demo port utilized multidimensional arrays, which in Vala are declared differently from other languages! You use arr_name[,] or arr_name[,,] where the commas represent the dimensions.

No function in GLib can convert degrees to radians. Hence, Lorenz suggested creating a function that could be used for the next GLib iteration and our demo.

Then I worked on numerous smaller ports, I’ll summarize their difficulty and things I learned from them in brief:

Frames, Label, and Menu Button demos were relatively easier to port and I was able to implement them quickly. The one thing that I was getting wrong up until here was not using explicit declarations for variables to make the code easier to comprehend.

Font Dialog demo utilizes async methods with await/yield calls to ensure that there is something returned always (required output/ error thrown). When implementing this demo in Vala I had compiler warnings stating implicit .begin() is deprecated

//Compiler throws an implicit begin warning
custom_button.clicked.connect (on_clicked);

//Fixed by explicitly connecting to the begin method
custom_button.clicked.connect (on_clicked.begin);

Network Monitor demo in particular took a lot of my brain cells, the whole code was rather easy to implement. The only thing that took most of my time was setting the value for the LevelBar, the value was to be fetched from NetworkMonitor, and the function that does this is NetworkMonitor.connectivity (which has a return type of enum). I did not realize that enum values can be cast into double and ended up creating a switch case iterator for the various connectivity types. The conditional loop was a better solution for the problem but Diego suggested that we cast it to double for consistency with other demos.

This week ended with a few more ports, namely Popovers, Power Profile Monitor, Spell Checker, Toggle Button, and Tooltip.

Here is your reward for reaching this far into the post

Week 3 (This may have taken longer than I anticipated)

The table of the ports? This is that table after Week 3

This week I worked on porting Progress Bar, Save File, Select Folder, and Spin Button(This demo was fun). All of the demos seemed less intimidating since they utilized similar functions and methods that I had already implemented in previous demos. Needless to say, it took extra time to port the last remaining demos because those were much longer and harder to keep track of.

Menu demo used container variables and one way to implement key and value pairs in Vala is to use HashTables (I had only heard about Hash tables being very useful before). It took me a lot of googling and trial and error to use a HashTable. Vala also supports syntactic sugar for set and get methods. This allows us to insert/lookup values to and from the table using keys and values as table indices

Eg:     content_type_params.insert ("charset", "UTF-8");
//Instead of calling insert method, you can insert into the value of
//charset index of the table
content_type_params["charset"] = "UTF-8";

//Similarly for lookup
var name = query.lookup ("name");
string name = query["name"];

HTTP Server demo uses HashTables, it also uses Soup.Server to deliver a local host server that can handle web requests.

Maps demo was an interesting one to port, the code in Python and Javascript utilized checking for (Not a Number) NaN characters but we used float.try_parse to check if the latitude and longitude entries are valid. It checks if the number is a valid float and if possible parses it to float.

The same week I ported Session Monitor and Inhibit. I had no understanding of the demo itself until Diego explained it to me very thoroughly.

File Monitor allows you to monitor file changes and see the changes you’ve made to the file in any text editor of your choice. The only issue with this port was the method to replace the contents of the file was inserting nonreadable text and the text editor was giving me issues when opening the file.

We fixed it by moving the handler to a separate async method call. It also took a huge chunk of time to port the Notification demo into Vala only to realize it couldn’t work. When using Vala in Workbench, the code and preview run as separate processes, and hence GApplication would not be registered properly and just gives a lot of other errors that I do not understand at the moment.

Another demo that used HashTables was Scrolled Window demo. The table was used to store Orientation and Scrollbars as key and value pairs.

As of writing this blog, Screencast, and Camera demos remain to be ported. These make use of the GStreamer library. There is already a draft PR for Screencast, the implementation requires further discussion with the reviewers.

These have been the updates from my side, thank you for reading. I would again like the thank Diego and Lorenz for all their help with reviewing and improving the ports.

It is time I move to the next part of my GSoC project and work on redesigning the Workbench Library. I will be back with more updates, stay tuned for those!

Until then

Understanding “Leading Questions” in Usability Research.

I’m in my internship's 3rd and 4th week, “conducting a series of short user research exercises using a mix of research methods”. Today, I want to talk about “leading questions,” a term that fascinated and confused me.

I heard of the the term “leading questions” during the 1st week of our internship when Allan Day, our Outreachy mentor for the May to August 2024 cohort, mentioned it briefly while discussing research design. I didn’t understand what it meant at the time. If you’re like me and wondering what it means, don’t worry — I got you.

Leading questions subtly prompt or encourage the respondent to answer in a particular way. These questions can influence the respondent’s answer by implying a preferred or expected response. In usability research, leading questions can bias the results and provide inaccurate insights into the user experience. Here’s an example:

Leading Question: “How much do you like our new drag-and-drop feature?”

Neutral Question: “What are your thoughts on our new drag-and-drop feature?”

The leading question assumes that the respondent likes the new feature, while the neutral question allows for any kind of feedback.

The benefits of not using leading questions are significant. Researchers can gather more accurate and genuine feedback by asking neutral, open-ended questions. This unbiased information helps in understanding the true opinions, needs, and pain points of users, leading to better design decisions and more effective improvements. It ensures that the data collected reflects the user’s real experience, rather than being skewed by the researcher’s assumptions.

You can read more about leading questions on this link.

On a more relaxed note, you can apply the concept of leading questions in your everyday life. For example, next time you cook dinner, don’t ask your guest, “How did you enjoy the meal?” Instead, ask, “What are your thoughts about the meal?” This way, you might get better insights into your cooking skills🍲.