.plan

2024-04-15

[Technology] GTK and Rust: "This application can not open files"

If you're writing a simple GTK application in Rust (e.g. with gtk4-rs) and you get this error

GLib-GIO-CRITICAL **: 13:39:45.953: This application can not open files.

and go "But I'm not opening any files, and I didn't ask to?!", you might want to be more explicit about that.

It might be because you're taking command-line arguments intended for other purposes, but gtk4 for Rust's Application::run() 'helpfully' collects any CLI arguments on your behalf without being asked to, even though you did not provide them directly (e.g. with Application::run_with_args()).

So, if you'd like to parse some CLI arguments for other reasons, you'll need to explicitly not provide them to your GTK application, or you'll need to pretend to handle them.

Approach 1: Explicitly ignore arguments

Instead of running your Application `app` like this:

let exit_code : ExitCode = app.run ();

 Be explicit, like this:

let no_args : [&str; 0] = [];
let exit_code : ExitCode = app.run_with_args (&no_args);

Approach 2: Claim to handle files (and then don't!)

let app : Application = Application::builder ()
  .application_id (APP_ID)
  .flags (ApplicationFlags::HANDLES_OPEN)
  .build ();
...  
app.connect_open (open_cb);
let exit_code : ExitCode = app.run ();

...

pub fn open_cb (_app : &Application, _files : &[File], _hint: &str) { }

This can be unintuitive, because in C, if you don't claim to handle opening of files, and don't explicitly provide argc/argv to g_application_run (), it does not assume you want to handle open.  But C is a bit like approach 1, in that you are explicitly not providing them, since you have to pass 0 and NULL for argc and argv respectively.

Simple C application

This will simply and naively pop-up a window that shows the text provided in argv[1].

main.c
#include <gtk/gtk.h>

int activate (GApplication *app, gpointer *user_data) {
  GtkWidget *window = gtk_application_window_new (GTK_APPLICATION (app));
  gtk_window_set_title (GTK_WINDOW (window), "Demo");
  gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);

  GtkWidget *label = gtk_label_new ((char*)user_data);

  gtk_window_set_child (GTK_WINDOW (window), label);

  gtk_window_present (GTK_WINDOW (window));
}

int main (int argc, char **argv) {
  GtkApplication *app = gtk_application_new ("org.kosmokaryote.test20240415.C", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (G_APPLICATION (app), "activate", G_CALLBACK (activate), argv[1]);
  return g_application_run (G_APPLICATION (app), 0, NULL);
}

Compiled with:

gcc `pkg-config --cflags --libs gtk4`  main.c   -o main

Corresponding Rust

cargo new test
cargo add gtk4 --features v4_12
src/main.rs
use gtk4::{Application,gio::ApplicationFlags, glib::ExitCode, prelude::*, ApplicationWindow, Label};
use std::env::{self, Args};

fn activate (app : &Application) {
    let mut args : Args = env::args ();
    
    let window : ApplicationWindow = ApplicationWindow::builder ()
        .application (app)
        .title ("Demo")
        .default_width (600)
        .default_height (400)
        .build ();

    let label : Label = match args.nth (1) {
        Some(s) => Label::new (Some (s.as_str ())),
        None => Label::new (None)
    };

    window.set_child (Some(&label));

    window.present ();
}

fn main() -> ExitCode {
    let app : Application = Application::new (Some ("org.kosmokaryote.test20240415.Rust"), ApplicationFlags::FLAGS_NONE);
    app.connect_activate (activate);
    // app.run ()
    let no_args : [&str; 0] = [];
    app.run_with_args (&no_args)
}

2023-11-22

[Technology] How to "crop" a video in Pitivi

Tl;dr: first, go to Render > Advanced > Project Settings ... > Size, and change the width and height of the output video to be rendered. Then go to Clip > Transformation to shift (and scale) the input as needed to fit the cropped output region.

The problem

I found the process of trying to crop a video in Pitivi unintuitive, so I thought I would document it. I have a video that is in landscape orientation, and I'd like it in a portrait orientation to make it easier to watch on mobile devices.

Here is a 4s clip from the video:

I'd like to crop it from its original 1440x1080 size to 768×1024, centered.

From this:

To this:

(shaded region showing what will be excluded from the final video)

1st attempt: Clip Transformation

Once you have a Pitivi project started and you've imported your video file into it, there's a promising tab called "Clip" thas has some promising properties: X, Y, Width and Height. There is even a video preview with an adjustable bounding box that you can use to manually select a region! Nice! However, it will not crop the video. X and Y will translate (shift) the input video image, and Width and Height will scale it, but it won't change actually crop the output video. Look at the preview. Now you just get a huge black box around your attempt.

Here is the output video: not what we wanted.

2nd attempt: Crop effect

Pitivi has many cool effects you can apply to your video, and one is called 'crop'! A-ha! That must crop the video for us! No, no it does not.

Here is the output video: still not what we wanted.

Final attempt: Project Settings output resolution + Clip transformation

This probably makes sense to video editor people, but to actually crop a video such that the output is actually cropped, you have to dig through a few menus to find settings for the resolution of your output video, rather than acting upon the video that you're editing itself. To do this, go to Render > Advanced > Project Settings ... > Size and change the values to your desired output width and height.

Go to 'Render'

Expand 'Advanced' and go to 'Project Settings'

Once in 'Project Settings', go to 'Size' and update the output Width and Height as desired.

NOW we can go to Clip Transformation from earlier and adjust X and Y to shift our desired region over to where it should be. Since cropping via the Render Project Settings is anchored to the top-left corner, we'll use negative X and Y values to shift the image region left and up towards that anchor. Width and Height for Clip Transformation should continue to match the input video's dimensions, or we'll squish things up and ruin the crop.

And here is the final output, properly cropped.  (Blogger's thumbnail is misleading; but the video itself is correct.)

 
 

Issue

There's some discussion on making it easier in the future here:

[Technology] Nextcloud on a Raspberry Pi: the easy way

Note: nextcloudpi, while easy, is actually going away! But you can do a similar approach with Nextcloud All-in-One which I will try to play with soon.

In my previous post, I talked about setting up Nextcloud from source, including configuring a web server, a database, and PHP, based on Nextcloud's detailed guide.

In comparison, deploying it from a container is much simpler. E.g. using the (recently deprecated :|) ownyourbits' NextcloudPi project:

# # let's make sure our system is up-to-date first :)
# apt update
# apt upgrade   # don't forget to reboot periodically!
#
# # Use docker's handy install script to get it on your Raspberry Pi
# curl -fsSL https://get.docker.com -o get-docker.sh
# bash get-docker.sh          # adds 1.6GB
# 
# # pull nextcloudpi from ownyourbits and run it! (adjust ports to avoid collisions with any existing web servers as necessary)
# docker pull docker.io/ownyourbits/nextcloudpi:latest
# docker run --detach \
           --publish 4443:4443 \
           --publish 443:443 \
           --publish 80:80 \
           --volume ncdata:/data \
           --name nextcloudpi \
           ownyourbits/nextcloudpi YourPisHostName
           

Then visit https://YourPisHostName/ and follow steps for configuration. It will give provide you with administrative and user credentials, defaulting to the username 'ncp'. There will be some administrative configuration on port 4443 and regular user/admin experience on 443. Stored data will end up in /var/lib/docker/volumes/ncdata (based on the volume label given above in the docker run command). If you'd like to enter the container while it's running to adjust something, you can use

sudo docker exec -it nextcloudpi bash

I had to install vim once inside to have an editor. :) A useful tool for administering nextcloudpi from the command-line is ncp-config.

[Technology] Nextcloud on a Raspberry Pi: setting up a web server, a database, PHP and Nextcloud itself

Installing Nextcloud on a Raspberry Pi can be fun, or "fun".  Run your own suite of open source web services for greater control, customization, independence, and whatever.  I'll share my process for installing it from a tarball on a Raspberry Pi (4) running Raspberry Pi OS (bookworm).  Let's hope I don't skip any steps.

My Pi, luxuriating on the soft bedding it deserves
 
Nextcloud's website offers a lot of options, and you can run Nextcloud in containers, install it from snaps, from Fedora's RPM repositories, etc. One benefit of installing it from a simple tarball, from its source, is that you can better understand a lot of its dependencies and the configuration needs.  I used their extensive directions found here:

Honestly, they're comprehensive enough that you can follow that instead of the below, but hopefully in the end I'll have a streamlined guide, at least for myself.

Regarding storage, going through this on my Raspberry Pi (already in use for other things) used 3.5GB of disk space. Going through it on the docker.io/library/debian:latest container image, I used 4.8GB of disk space (starting from a minimal 172MB!). And this is before user data gets accumulated. So if you want to store a lot, you will likely want to get a larger microSD card for your Pi or use some external USB storage.

Regarding performance, I've had some mixed results. During a demo to a group of friends on a local network using the nextcloudpi docker container, many features were quite slow. However, testing at home recently, using either the container or the source installation described below, those same features were now fast with very low latency!

Environment

My environment:

  • Hardware:
    • Raspberry Pi 4 Model B Rev 1.4
    • Storage: 29GB
  • OS:
    • Raspberry Pi OS (Debian GNU/Linux 12 (bookworm))
    • image: 2023-10-10-raspios-bookworm-arm64-lite.img
  • Nextcloud
    • version: 27.1.3
    • build: 2023-10-26T17:25:16+00:00 565dc36226d08d071c30d8ad4fd54126dfa4be79

Nextcloud can work with a variety of databases and web servers, and the choices can be overwhelming.  For this process, I'm going with their recommendations in their install guide and making some boring, conventional choices. 

Software choices:

  • web server: Apache
    • apache2-2.4.57-2
  • database: MariaDB (mysql)
    • mariadb-server-1:10.11.4-1~deb12u1

If you would like to follow along but don't have a Raspberry Pi to play with, you can get a very similar set-up experience using a standard Debian GNU/Linux 12 (bookworm) installation.  If you like containers, docker.io/library/debian:latest is currently bookworm.  I used that with podman to do testing along the way.

Another note is that I generally use systemd but feel free to use your favourite service manager/init system.

1. Set-up Environment

# # update the base system
# apt update
# apt upgrade
# # install some useful tools
# apt install vim
# apt install less
# apt install wget 
# # install some tools that Nextcloud requires
# apt install bzip2
  

2. Database

As noted above, I'm using MariaDB, a MySQL-compatible derivative. You'll create an empty database, as well as a username and a password, for Nextcloud. Nextcloud will handle table creation on its own. Don't forget to replace username and password with your own unique values below.

# apt install mariadb-server       # 18.3MB download, 197MB installed
# systemctl enable --now mariadb
# mysql -u root
> CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
> CREATE DATABASE IF NOT EXISTS nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
> GRANT ALL PRIVILEGES ON nextcloud.* TO 'username'@'localhost';
> FLUSH PRIVILEGES;
  

Next, we need to add some configuration to MariaDB/MySQL in /etc/mysql/my.cnf (which maps to /etc/mysql/mariadb.cnf for me). The installation guide suggests the following settings. I'll note that my default config already has a similar [client-server] section, so I excluded I only added the other ones that weren't present. The transaction_isolation and binlog_format settings are particularly important.

[server]
skip_name_resolve = 1
innodb_buffer_pool_size = 128M
innodb_buffer_pool_instances = 1
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 32M
innodb_max_dirty_pages_pct = 90
query_cache_type = 1
query_cache_limit = 2M
query_cache_min_res_unit = 2k
query_cache_size = 64M
tmp_table_size= 64M
max_heap_table_size= 64M
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

[client-server]
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mariadb.conf.d/

[client]
default-character-set = utf8mb4

[mysqld]
character_set_server = utf8mb4
collation_server = utf8mb4_general_ci
transaction_isolation = READ-COMMITTED
binlog_format = ROW
innodb_large_prefix=on
innodb_file_format=barracuda
innodb_file_per_table=1    
  

Later on, when configuring Nextcloud itself, you'll provide it with the database name, username, password and host (in this case, localhost). You can manually configure it or use the web installation wizard (which I'll do for in post.)

3. PHP

Nextcloud requires a bunch of PHP modules, some of which were already installed on my system, and some not. Below I only show install commands for those that I were not already there. You can view Nextcloud's documentation to see all the modules they require and recommend, including some optional ones that I am skipping.

# apt install php
#
# # install required modules
# apt install php-curl
# apt install php-gd
# apt install php-json
# apt install php-mbstring
# apt install php-xml                  # SimpleXML, XMLReader, XMLWriter
# apt install php-zip
# apt install php-mysql                # for DB since I use MariaDB
#
# # install recommended modules
# apt install php-bz2                  # for installing packages
# apt install php-intl                 # faster language translation performance (I run it in German)
# apt install php-redis                # cache for faster performance; alt: php-acpu, php-memcached
# apt install redis-server             # see notes
# systemctl enable --now redis-server  # or redis-server
#
# # file preview generation
# apt install php-imagick              # image preview generation (22.4MB down, 84.0MB installed)
# apt install ffmpeg                   # video preview generation; alt: avconv;  (127MB down, 415MB installed)
# apt install libreoffice              # (318MB down, 1,235MB installed)
  

You may want to review suggested configuration changes to see which ones you need.

4. Web server

As noted above, I'm using Apache 2. We will enable some modules; several that Nextcloud want were enabled by default but I'm listing them anyway.

It's 2023, so of course we will encrypt our connections and traffic. In this case, we are going to use a default self-signed certificate, but if you are actually going to deploy it, at least use Let's Encrypt to get a certificate for your actual domain.

# apt install apache2
# systemctl enable --now apache2
#
# # required modules, for at least pretty urls :)
# a2enmod rewrite
# # recommended modules
# a2enmod headers
# a2enmod env            # already enabled for me
# a2enmod dir            # already enabled for me
# a2enmod mime           # already enabled for me
#
# # SSL
# a2enmod ssl
# a2ensite default-ssl
#
# # use php-fpm, Nextcloud recommends it over mod_php
# apt install php-fpm
# a2enmod proxy_fcgi
# a2enmod setenvif       # already enabled for me
# a2enconf php8.2-fpm
# systemctl enable --now php8.2-fpm
#
# systemctl restart apache2
# systemctl reload apache2
  

Next, we'll configure our Nextcloud site in Apache. You can set it up as either a sub-directory or a subdomain on your host. I picked sub-directory. More details here. Note that it disables mod_dav, as Nextcloud uses SabreDAV.

Nextcloud Apache Configuration

Create this file /etc/apache2/sites-available/nextcloud.conf with the following content:

Alias /nextcloud "/var/www/nextcloud/"

<Directory /var/www/nextcloud/>
  Require all granted
  AllowOverride All
  Options FollowSymLinks MultiViews

  <IfModule mod_dav.c>
    Dav off
  </IfModule>
</Directory>
  

Afterwards, run this:

    a2ensite nextcloud.conf
    systemctl reload apache2
  

We will actually create and populate the /var/www/nextcloud directory when we finally get to installing Nextcloud itself from its tarball.

5. Nextcloud

Now that we have our web server, database, and PHP configured, we can finally install Nextcloud itself. As noted above, I am installing it from their latest source tarball. You can find it by going to their Install page > Community Projects > Archive, or just follow this direct link: https://download.nextcloud.com/server/releases/latest.tar.bz2.

# cd /var/www
# wget https://download.nextcloud.com/server/releases/latest.tar.bz2  # 172MB download, 607MB unpacked
# tar -xf latest.tar.bz2                                              # this unpacks 'nextcloud/' here at '/var/www/nextcloud'
# chown -R www-data:www-data /var/www/nextcloud/
#
# # verify that necessary services are running
# systemctl status mariadb
# systemctl status php8.2-fpm
# systemctl status apache2
  

Now, if everything went smoothly, you should be able to pop open https://yourpihostname:443/ and be greeted with the initial configuration page!

  • create an admin account
  • configure database settings (back from step #2 up above)
  • install recommended apps

Now you can have some fun playing around. Some features require extra configuration, like using Nextcloud Office with Collabora Online (Development Edition, CODE), and I may update this in the future with some more nuance.

Admin user and DB set-up screen


User dashboard with some test data added

Next steps

In a future post, I may discuss setting up a Google Docs-like experience with Nextcloud Office using Collabora Online office, as well as explore more of the options and configuration settings. I will also discuss the much-simpler ways of installing and running Nextcloud (e.g. from VM or container images!)

Update: just wrote up the very quick-to-start-but-deprecated nextcloudpi container image.

2023-11-04

[Technology] IDEs, Emacs and LSP: Python and TypeScript

Back in my day, we had a text editor, a compiler and a Makefile, and we liked it like that.  Maybe we didn't really like it like that.  But it let you write and compile code.

I got attached to Emacs early on (sorry vi) and over the years have accumulated a number of additional packages and lisp code to make development easier and more efficient.  More "recently," Language Server Protocol (LSP) [wiki] [git] was born, initially targeting Microsoft's VSCode, which has allowed the creation of Language Servers that are independent of any specific editor, and made editors more agnostic to which languages they support, by facilitating a standardized interface between the two facilitating the helpful features wanted in Integrated Development Environments (IDEs).

Some common features include:

  • code completion
  • syntax highlighting
  • in-line error/warning indicators
  • refactoring support
  • code navigation to definitions and references
  • in-line API documentation for function signatures and variable typing

With Emacs, many of these features have been available through a variety of internal and external packages independently for a while.   Modes for languages (c-mode, python-mode, web-mode, etc.) have enabled syntax highlighting and code formatting assistance.  company has supported code completion.  flymake has supported syntax checking and error/warning indicators.  Etc.

I decided to update my environment, though, to benefit from the existence of LSP language servers and hopefully simplify my configuration and approach.  With Emacs often comes a lot of choice and some custom configuration.  That can be great to achieve an ideal environment for yourself, but it can also require a lot of time and attention to figure out what that ideal environment should look like, and just how to glue interacting packages together.  So, I'll share my process.

LSP client modes in Emacs

There are now two prominent options in Emacs

Eglot has recently been merged into core GNU Emacs v29.  According to "the Internet", lsp-mode is more featureful.  I want to try both, but Fedora 38 only ships GNU Emacs v28.3, so I have tried lsp-mode first!   I'll try Eglot once I upgrade to Fedora 39 next week. 

Language Servers for Python

I'm decided to try two different languages, first Python and then TypeScript.  However, both have multiple language servers available.  The pain of making choices!

lsp-mode's language server page lists several options for Python already

  • Pylsp / python-lsp-server [git]
  • Jedi Language Server [git]
  • Palantir Python Language Server [git] (deprecated)
  • Microsoft's Pyright [git]
  • Microsoft Python Language Server [git] (superseded by Pyright)
  • Ruff [git]

So many nice options.  Which to pick?  Well, an interviewer once recommended Pyright so there I go to start.

 Language Servers for TypeScript/JavaScript

Again, options!

  • Deno [site]
  • Sourcegraph's javascript-typescript-langserver [git] (unmaintained)
  • TypeScript Language Server [git] (formerly theia-ide by TypeFox)

This time I went with TypeScript Language Server because lsp-mode's page for it explicitly marks it as recommended.

Additional packages

One issue with IDEs is that all of their features can make an interface busy/noisy and slow.  Sometimes similar information will be redundantly surfaced at multiple points on screen.  lsp-mode is delightful in that it's very modular and (like many IDEs) you can toggle features on and off as you need them.  They even offer a handy visual guide to disabling features.  

lsp-mode also leverages a lot of other packages, some internal and some external, to offer a lot of its functionality.  They list these as some of them: lsp-ui, company, flycheck, flymake, projectile, imenu, xref, lsp-treemacs, dap-mode, lsp-helm, lsp-ivy, consult-lsp, which-key, dired, iedit, emacs-tree-sitter.

Some of these will also leverage other packages and external tools, too.  E.g. flycheckwhich runs syntax checkers can also call pylint, mypy, etc. from outside emacs, to surface errors and warnings on screen. :)  The bolded ones will be included in my set-up steps below.

My configuration: lsp-mode, pyright, pylint

After configuring my system, I reproduced it in a minimal container image (registry.fedoraproject.org/fedora) via podman 

Install emacs and (for pyright) npm.

  $ sudo dnf install emacs
  $ sudo dnf install npm    # for pyright, which is written in typescript

Make sure you have the MELPA package archive configured in your ~/.emacs

  ;; ---- Package Management (package.el, melpa) ----                                
  (require 'package)
  (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
  (package-initialize)

Next, you can start emacs and run these commands:

  M-x package-refresh-contents
M-x package-install use-package
;; install a few minimal packages we'll use for LSP/IDE features M-x package-install lsp-mode M-x package-install lsp-ui M-x package-install flycheck ;; show errors and warnings from pyright M-x package-install company ;; auto-complete

;; now to install language support (servers + mode) M-x package-install lsp-pyright
M-x package-install typescript-mode M-x lsp-install-server pyright-tramp
M-x lsp-install-server typescript-language-server

Additional .emacs configuration for newly added LSP/IDE packages:

;; From https://emacs-lsp.github.io/lsp-mode/page/installation/                         
(use-package lsp-mode
:init
;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l")
(setq lsp-keymap-prefix "s-l")
:hook ((python-mode . lsp-deferred))
:hook ((typescript-mode . lsp-deferred))
:commands (lsp lsp-deferred))

(use-package lsp-ui :commands lsp-ui-mode) (setq lsp-ui-doc-show-with-cursor t) ;; From https://emacs-lsp.github.io/lsp-pyright/ (use-package lsp-pyright :ensure t :hook (python-mode . (lambda () (require 'lsp-pyright) (lsp-deferred))))
Show some additional API documentation on screen for symbols!
M-x lsp-ui-sideline-toggle-symbols-info

"flycheck isn't working?!"

One issue that stumped me for a while was lsp failing to show errors/warnings from flycheck when flycheck was configured to use "lsp" as its syntax checker. It turns out that because I often use symlinks from my home directory to my current active projects, I ran into lsp-ui's previously-resolved issue #119 (or potentially a new, similar one). So, if lsp-ui doesn't indicate warnings/errors from flycheck (or flymake), make sure your path isn't via a symlink.  Oops.

Handy Keybindings

Here are some of my most-used/commands keybindings:

  • s-l r r - refactor > rename a symbol
  • s-l g g - go to definition
  • s-l g r - list references
  • C-x ` - go to next error (flycheck)
  • M-x lsp-format-region
  • M-x lsp-format-buffer

Here is a list of others: https://emacs-lsp.github.io/lsp-mode/page/keybindings/

I don't see "s-" often as a prefix, but it's for the Super key (e.g. Windows key).  On my system (Fedora 38 with GNOME), s-l locks my screen, but holding shift along with super works for Emacs too, e.g. "<shift>+<super>+l r r" to rename a symbol.

Conclusion

Something I appreciate most about IDEs and their fancy features is the early detection of problems and API guidance as you code.  I still will add linting/syntax checking/test steps to my build process to guard against bad code entering my repositories.  I will say that I think there is some benefit to being able to code without the guidance of an IDE (e.g. code completion) as it can encourage you to better learn an API in the first place.  But there comes a point when you can focus more on being productive (with the help of tools!) than on exercising your API memorization for a hundredth time. :)

2023-10-06

[Technology] Element and Signal crashing on notification closure :(: novel-edition

I run Fedora and have messaging app Element (and Signal) installed via Flatpak.  Last week, I upgraded to versions that upped their base dependencies on its runtime org.freedesktop.Platform and base image org.electronjs.Electron2.BaseApp from 22.08 to 23.08.  Since, clicking on notifications have made Element go kaboom :(  

 I decided to see if I could identify the problem and submit a fix.  (Spoiler: someone else has solved it first, but I learned a lot along the way).

 tl;dr: filed some issues, and learned new things about Flatpak

0. tl;dr #2

In the general case, following debugging instructions from these two resources has been sufficient in the past:

Basically, for im.riot.Riot, I would normally want to:

  • $ flatpak install im.riot.Riot.Debug   # install .Debug extension
  • $ flatpak run --devel --command=sh im.riot.Riot # run a shell in devel mode for debug info and tools like 'gdb'

In this case, that was insufficient because the debugging information was in the Electron base layer, so I ultimately rebuilt Element locally, specifying the electron .Debug extension in Element's YAML file's "base-extensions:" list, and spent too much time figuring out how to install my locally-built Flatpak's own .Debug extension from my build directory.  (See "Detour 2.1" below for details.)

Here are all my steps in case that helps someone else:

1. Investigate

Check for existing bug reports

First I wanted to look to see if another user/developer had already noticed/identified the issue.  I thought the problem might be specific to the its Flatpak package, given the constraints on how it can interact with the host system (e.g portals).   So my first stop was Flathub, going to Element (im.riot.Riot)'s app page:

Detour 1: broken app pages

That led to the above issue getting filed, as visiting Element's app page .  No one directly replied but I was flattered to see it referenced in the #flathub:matrix.org chat:

Check for existing bug reports, take 2

So for now I just googled and found that Flathub Flatpaks have their package repos stored under Flathub's github like:

There wasn't an existing issue created for this, so I made one!

Finding the change that broke things

First I had to see if I could find the version/change that introduced the problem.  I referred to Flatpak's bisect documentation but for reasons that I will investigate later, `flatpak bisect` didn't actually work for me, so I went the classic route of ... clone the package repo and bisect it myself!

Building locally

I referred to more documentation (Building your first Flatpak (even though this is like my third!))

~$ git clone https://github.com/flathub/im.riot.Riot.git
[~]$ cd im.riot.Riot/
[im.riot.Riot]$ git clone https://github.com/flathub/shared-modules.git
[im.riot.Riot]$ flatpak-builder build-dir im.riot.Riot.yaml
error: org.freedesktop.Sdk/x86_64/23.08 not installed
Failed to init: Unable to find sdk org.freedesktop.Sdk version 23.08

:(  Let's install our prerequisites:

$ flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
$ flatpak install --user --no-related  org.freedesktop.Sdk/x86_64/23.08
$ flatpak install --user --no-related  org.freedesktop.Platform/x86_64/23.08
$ flatpak install --user --no-related  org.electronjs.Electron2.BaseApp/x86_64/23.08

Notes:

  • remote-add: for my test environment, flathub exists already for the system, but I just want to test this locally, so adding it for user.
  • --no-related: I use this to skip Locale extensions that are very large and likely unnecessary for testing (in this case, they're going to get installed anyway, ah well)

Now it basically builds!

Manually bisecting

I added a branch tag to im.riot.Riot.yaml to mark it as my debugging version:

app-id: im.riot.Riot
branch: dbg
base: org.electronjs.Electron2.BaseApp
...

Repeat this a few times:

$ git log       # view changes and commit IDs
$ git checkout <COMMIT_ID>    # test a commit
$ flatpak-builder --user --install --force-clean build-dir im.riot.Riot.yaml 
$ flatpak run im.riot.Riot/x86_64/dbg    # run my dbg branch to test

Testing in this case saw me messaging one of my Matrix accounts from another to generate the notification, clicking, and seeing if it crashed.

This quickly narrowed it down to this:

While the commit name references the org.freedesktop.Platform runtime change, spoiler: the issue is more related to the org.electronjs.Electron2.BaseApp base image change to 23.08.

 This led to issue 406 (above) being filed, but while working on this, a friend sent me a message via Signal (also based on Electron) and that crashed when I clicked its notification!  Uh oh.

Check for existing bug reports, Signal-edition

In Signal's case, someone had already filed an issue:

They had identified the issue as existing in libnotify (rather than specifically how the Flatpak used it), and had already filed an issue there:

2. Debug

Finding the change that broke things, part 2: libnotify edition

 So from there I decided to try to debug the source of the crash.  First I tried installing the debug info for Element (im.riot.Riot) and running it in devel mode, so I could use gdb to catch the crash and look at the backtrace:

$ flatpak install --user im.riot.Riot.Debug
$ flatpak run --user --devel --command=sh --devel im.riot.Riot
[📦 im.riot.Riot ~]$ cd /app/bin
[📦 im.riot.Riot bin]$ ./element &
[📦 im.riot.Riot bin]$ gdb --pid=<PID>
...
Enable debuginfod for this session? (y or [n]) y
...
(gdb) continue
...
Downloading separate debug info for /app/lib/libnotify.so.4
(element-desktop:6): libnotify-WARNING **: 18:22:08.633: Running in confined mode, using Portal notifications. Some features and hints won't be supported
Thread 1 "element-desktop" received signal SIGSEGV, Segmentation fault.
0x00007f945f8d1418 in ?? () from /app/lib/libnotify.so.4
(gdb) bt
#0  0x00007f945f8d1418 in  () at /app/lib/libnotify.so.4
#1  0x00007f946f4fb4ea in g_closure_invoke (closure=0x173001ee90b0, return_value=0x0, n_param_values=4, param_values=0x7ffd598a9cb0, invocation_hint=0x7ffd598a9c30) at ../gobject/gclosure.c:832
...

Huh, despite installing the .Devel extension for Element, running it using --devel, and even having gdb try to download debug info for libnotify.so.4, there are no symbols.

I took a peek inside my Flatpak to see that running with --devel meant /app/lib/debug/ did have debug info for various libraries and packages, but not libnotify.

Note:

  • for PID above, element launches multiple element-desktop processes, but the one I want is the following invocation, which abrt was kind enough to identify when I first encountered this problem:
    • /app/Element/element-desktop --enable-wayland-ime --ozone-platform-hint=auto --enable-features=WaylandWindowDecorations,WebRTCPipeWireCapturer 

Detour 2.0: the quest for libnotify debugging symbols

So I tried a variety of things and did a lot of reading.

Looking around, I saw that libnotify wasn't directly included in im.riot.Riot but in its base layer, org.electronjs.Electron2.BaseApp:

  • https://github.com/flathub/org.electronjs.Electron2.BaseApp/

I went back to #flatpak:matrix.org and asked around and barthalion helpedfully noted that apps using Electron usually didn't retain their debugging symbols.

I tried to build a local version of that under my own dbg branch, and then rebuilt my local dbg build of im.riot.Riot using that dbg branch of Electron2, but they were still missing.  Hmm.

Detour 2.1: successfully including debugging symbols from a base image

After some more reading, I found this very helpful blog post by Redhat's Stefan Hajnoczi, and a helpful digging deeper into Flatpak builder docs:

So I was finally able to test Element with libnotify debugging info by:

  1. Adding the following to my im.riot.Riot.yaml, telling it to actually use the Debug extension for org.electronjs.Electron2.BaseApp (using the flathub version, rather than my dbg build):
    base-extensions:
    app-id: im.riot.Riot
    branch: dbg
    base: org.electronjs.Electron2.BaseApp
    base-version: '23.08'
    base-extensions:
      - org.electronjs.Electron2.BaseApp.Debug
    runtime: org.freedesktop.Platform
    ...
  2. actually locally installing the Debug extension of my locally-built dbg build (thanks to Stefan Hajnoczi's blog post above!).  From my local git repo,
    [im.riot.Riot]$ flatpak install --user im.riot.Riot.Debug
    [im.riot.Riot]$ flatpak install --user --reinstall --assumeyes "$(pwd)/.flatpak-builder/cache" im.riot.Riot.Debug

Repeating earlier steps to run gdb and catch the crash, I got to:

...
[New Thread 0x7f61bff1b6c0 (LWP 184)]
(element-desktop:4): libnotify-WARNING **: 18:20:44.116: Running in confined mode, using Portal notifications. Some features and hints won't be supported
Thread 1 "element-desktop" received signal SIGSEGV, Segmentation fault.
0x00007f61b33cb418 in close_notification (reason=NOTIFY_CLOSED_REASON_DISMISSED, notification=0x25d801795f20) at ../libnotify/notification.c:708
708            if (notification->priv->closed_reason != NOTIFY_CLOSED_REASON_UNSET ||
(gdb) bt
#0  0x00007f61b33cb418 in close_notification (reason=NOTIFY_CLOSED_REASON_DISMISSED, notification=0x25d801795f20) at ../libnotify/notification.c:708
#1  proxy_g_signal_cb (proxy=<optimized out>, sender_name=<optimized out>, signal_name=<optimized out>, parameters=0x25d8002ae990, notification=0x25d801795f20) at ../libnotify/notification.c:795
#2  0x00007f61c2f154ea in g_closure_invoke (closure=0x25d8002d1150, return_value=0x0, n_param_values=4, param_values=0x7ffc29493d70, invocation_hint=0x7ffc29493cf0) at ../gobject/gclosure.c:832
...

The notification wasn't valid by the time we got to close_notification.  Likely, it was unref'd prematurely, possibly by Electron.  From there, I spent some time reading source code, setting break points, etc.  However, I ultimately ran out of time before Maximiliano beat me to it!

3. Resolution

As you can see in the libnotify issue #34 previously noted above, Maximiliano (@msandova) was hard at work today on libnotify's fix-electron branch working on a fix.

 

I rebuilt Electron's BaseApp using a gitlab-generated .tar.gz from the fix-electron branch, and rebuilt Element atop that, and that resolved the problem!

While I have to switch to other projects in life now, and I didn't really help solve the actual problem, it's been fun learning more about Flatpak and hopefully these notes will prove useful when debugging Flatpak issues in the future.

Thank yous

Thank you to the following very-helpful people:

  • Maximiliano (@msandova)
  • Stefan Hajnoczi
  • barthalion

Dieses Blog durchsuchen

Labels

#Technology #GNOME gnome gxml fedora bugs linux vala google #General firefox security gsoc GUADEC android bug xml fedora 18 javascript libxml2 programming web blogger encryption fedora 17 gdom git emacs libgdata memory mozilla open source serialisation upgrade web development API Spain containers design evolution fedora 16 fedora 20 fedora 22 fedup file systems friends future glib gnome shell internet luks music performance phone photos php podman preupgrade tablet testing typescript yum #Microblog Network Manager adb apache art automation bash brno catastrophe css data loss debian debugging deja-dup disaster docker emusic errors ext4 facebook fedora 19 gee gir gitlab gitorious gmail gobject google talk google+ gtk html libxml mail microsoft mtp mysql namespaces nautilus nextcloud owncloud picasaweb pitivi ptp python raspberry pi resizing rpm school selinux signal sms speech dispatcher systemd technology texting time management uoguelph usability video web design youtube #Tech Air Canada C Electron Element Empathy Europe GError GNOME 3 GNOME Files Go Google Play Music Grimes IRC Mac OS X Mario Kart Memento Nintendo Nintendo Switch PEAP Selenium Splatoon UI VPN Xiki accessibility advertising ai albums anaconda anonymity apple ask asus eee top automake autonomous automobiles b43 backup battery berlin bit rot broadcom browsers browsing canada canadian english cars chrome clarity comments communication compiler complaints computer computers configuration console constructive criticism cron cropping customisation dataloss dconf debug symbols design patterns desktop summit development discoverability distribution diy dnf documentation drm duplicity e-mail efficiency email english environment estate experimenting ext3 fedora 11 festival file formats firejail flac flatpak forgottotagit freedom friendship fuse galaxy nexus galton gay rights gdb german germany gimp gio gjs gnome software gnome-control-center google assistant google calendar google chrome google hangouts google reader gqe graphviz growth gtest gtg gvfs gvfs metadata hard drive hard drives hardware help hp humour ide identity instagram installation instant messaging integration intel interactivity introspection jabber java java 13 jobs kernel keyboard language language servers languages law learning lenovo letsencrypt libreoffice librpm life livecd liveusb login lsp macbook maintainership mariadb mario matrix memory leaks messaging mounting mouse netflix new zealand node nodelist numix obama oci ogg oggenc oh the humanity open open standards openoffice optimisation org-mode organisation package management packagekit paint shedding parallelism pdo perl pipelight privacy productivity progress progressive web apps pumpkin pwa pyright quality recursion redhat refactoring repairs report rhythmbox rust sandboxes scheduling screenshots self-navigating car shell sleep smartphones software software engineering speed sql ssd synergy tabs test tests themes thesis tracker travel triumf turtles tv tweak twist typing university update usb user experience valadoc video editing volunteering vpnc waf warm wayland weather web apps website wifi wiki wireless wishes work xinput xmpp xorg xpath
Powered by Blogger.