Avalonia ShowDialog and a Minimize Button

If you use ShowDialog in Avalonia, you will notice that under KDE/Wayland, window will retain minimize button. Even worse, minimize will hide the window, leaving its owner visible but not clickable. Slightly annoying and user might not even understand immediatelly why clicks on the main window are not working until they restore minimized dialog.

And, try as I might, I found no way to remove minimize box without removing the whole title. Since I was not in the mood to handle titleless windows, the next best thing was to close dialog window on minimize. Of course, detecting when window gets minimized is not really straightforward.

In the end, this is the code I ended up using:

PropertyChanged += (sender, e) => {
  if (e.Property == Window.WindowStateProperty && WindowState == WindowState.Minimized) {
    Dispatcher.UIThread.InvokeAsync(() => {
      WindowState = WindowState.Normal;
      Close();
    });
  }
};

We inject our check into the general PropertyChanged event handling. However, we cannot directly close window here as it will not revert focus to the owner. What we need is to ask dispatcher to invoke our code once UI thread has a spare moment. Then we restore the freshly minimized window, followed by close.

Not great, but not terrible either.

Synchronous Wait for Avalonia Dialog

Ideally, showing Avalonia dialog would be called from async method:

var frm = new Window(owner);
await frm.ShowDialog(this);

And 90% of time you’re good with that.

However, what if we don’t have async method handy? What if we really need to ShowDialog from non-async code?

Well, a bit more convoluted code can be used.

using var source = new CancellationTokenSource();
window.ShowDialog(owner).ContinueWith(t => source.Cancel(),
                                      TaskScheduler.FromCurrentSynchronizationContext());
Dispatcher.UIThread.MainLoop(source.Token);

A bit of handful and not really portable, but it does the job.

3GPP-Session-Stop-Indicator

Illustration

Fair amount of my time is spent in 3GPP specification world. Quite often I do that to figure out some minor details of random AVP I am analyzing. And no, I won’t probably go to the specification directly if search can work as well. So, imagine my surprise when I found 3GPP-Session-Stop-Indicator is specified incorrectly wherever I looked for it.

Want examples? Check it here, or here, or even here. Every time you see it specified, you will find the same data type: UTF8String. Which sounds plausible until you see it in the real world diameter snoop. There you’ll find its content is always 0xFF. So, what’s wrong with that?

If you read through RFC 6733, you will see that UTF8String data type is defined to use UTF-8. Guess what bytes sequence can NEVER appear in UTF-8? Darn 0xFF. As per RFC 3629, the highest byte value actually defined would be 0xF3. If you know anything about Diameter, the only sane answer is that this AVP is supposed to be of OctetString data type. So, where is this confusion coming?

I would place blame on ETSI TS 129.061 aka 3GPP 29.061. Incidentally, you won’t find 3GPP-Session-Stop-Indicator text anywhere in the specification. But you will find 3GPP-Session Stop Indicator defined with the following text:

3GPP Type: 11
Length: 3
Value is set to all 1.
3GPP-Session Stop Indicator value: Bit String type

Both 3GPP-Session Stop Indicator and 3GPP-Session-Stop-Indicator share code 11 with vendor 10415 (yes, we’ll ignore 3GPP calling it a “type” instead of “code”). They are the same AVP. Interestingly, length is specified as 3 which makes no sense as far as diameter goes. This is because this wasn’t originally specified for Diameter but for RADIUS. And, in RADIUS, length is indeed 3 bytes (or octets if you want to be fancy) once we add header to the single data byte. When Diameter came, 3GPP just promoted it into the new world without bothering to redefine it in any follow-up specification.

Now that we know we’re looking at the correct AVP, despite slight differences in name and length, what is the definition? It is defined as a bit string with all bits set to 1. Not UTF-8. Bit string. Which, in Diameter world, would mean its type is OctetString. And something that’s a single byte with all ones set is also known as 0xFF.

My guess is that someone originally misinterpreted “bit string” for “string” and thus it got written as UTF8String in the dictionary. Since 0xFF is just a single byte, nobody actually bothered to set it as string but they assign direct value when it comes from the sender. On consumer side, why would you even read this AVP when its value is always the same - there is no other value that 0xFF defined in any of 3FPP specifications as far as I can see. Thus, received only checks if AVP is present. Maybe some also check content but don’t bother converting to UTF-8 for a single byte. And that’s it - mystery solved - this was an OctetString all along.

Am I the first person to find this issue? I doubt it - anybody converting this to UTF-8 would see the issue. It is clearly visible in the darn Wireshark as text converts to “�” which is what UTF-8 decoder would return when it CANNOT convert. But I guess that most people looking at this were smart enough to just let it go. :)

Larger Live File System on Fedora 44

Illustration

While I am spending most of my time on Ubuntu these days, I do like to see what Fedora is up to. So, with Fedora 44 out, I started medling with installing some extra ZFS packages on top of it. However, I was soon hit with “Npt enough space” message. Fedora’s in-memory root pertition was a bit too small. But there’s a way around it.

During boot just select the main boot option and press e key. This will lead you into text editor and there just add

rd.live.overlay.size=10G

to the “linux” line.

This will make in-memory store larger (albeit you won’t see it be 10G in size). With this, I was finally able to fit ZFS on it.

Dealing with "No Signal" on HDMI Input

Illustration

For my media PC I use old Intel’s gen 11 board. As I upgraded my Framework laptop, it just made sense to use it. Bazzite works flawlessly on it. At least until paired with my Samsung “smart” TV.

I might be turning into an old man yelling at clouds but it seems that smart TVs are getting dumber and dumber. My PC is connected to TV via HDMI cable and it works wonderfully. Until TV is off for some time. Then suddenly, TV cannot find HDMI source.

I already wrote about this so I will spare you details on how I figured the TV was to blame. Suffice it to say that I found the fix even back then. But now I had an issue with my fix. It was too slow.

You see, I decided to use 1 minute timer. Which means that, once TV has gone crazy, one had to wait a minute at worst. And that caused some impatience in my household. So, I needed a faster detection…

The new script at /usr/local/bin/dp-reconnect looks something like this:

#!/usr/bin/env bash

while(true); do
    SLEEP_INTERVAL=$(( 3 - `date +%S` % 3 ))
    sleep $SLEEP_INTERVAL

    DP_CONNECTED=0
    for DP_PATH in /sys/class/drm/card1-DP-*; do
        DP_STATUS=$( cat "$DP_PATH/status" | grep '^connected$' | xargs )
        if [[ "$DP_STATUS" == "connected" ]]; then
            DP_CONNECTED=1
            break
        fi
    done

    if [[ $DP_CONNECTED -ne 0 ]]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') Display $( basename "$DP_PATH" ) connected"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') No connected displays"
        chvt 2
        sleep 1
        chvt 1
    fi
done

Instead of “one-shot”, this script loops forever. The very first calculation is there just to slow things a bit and ensure payload is executed every 3 seconds. And yes, this could have been replaced with sleep 3 without any functionality loss. However, I like to “align” my execution times so I did it in slightly more complicated manner.

To run this code, we can simply create /etc/systemd/system/dp-reconnect.service with the following content:

[Unit]
Description=Switch terminal if no DP is connected

[Service]
ExecStart=/usr/local/bin/dp-reconnect
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

With all files in place, the only remaining thing was enabling the service:

sudo systemctl daemon-reload
sudo systemctl enable --now dp-reconnect.service

Now my TV reacts much faster, hopefully putting this story to an end.