greshake / i3status-rust

Very resourcefriendly and feature-rich replacement for i3status, written in pure Rust
GNU General Public License v3.0
2.88k stars 475 forks source link

[net] emojis in SSID are not forwarded; hex code shown instead #293

Closed sebastianst closed 5 years ago

sebastianst commented 6 years ago

I'm sometimes connected to a wireless network with emojis in its name. Those are not shown in the title, but their hex code, including \x. Like so \x0f0\x9f\x8d\x8d. It would be great if i3status-rs would just forward unicode chars to i3bar. Thanks!

atheriel commented 6 years ago

It might worth checking if your bar font supports emojis as well.

sebastianst commented 6 years ago

It does, I use emojis for my keyboard layout indicator and the CPU symbol. I use

bar {
    font pango:DejaVu Sans Mono, FontAwesome 10
...

Running i3status-rust in a terminal doesn't print the emojis, but escaped slashes and hex codes, so you see \\x0f\\x9f\\x8d\\x8d where the ssid is. Which means, i3status-rs already internally converts the emojis into multi byte hex codes.

sebastianst commented 6 years ago

It would probably also just print weird boxed glyphs otherwise...

sebastianst commented 6 years ago

I found out the reason. The SSID is queried by calling iw, which returns the ssid with all unicode chars as escaped hex codes, see https://github.com/greshake/i3status-rust/blob/192274fe392f9658ca824d57166c7a2a801840b1/src/blocks/net.rs#L101 The good news is, this line also has nmcli -g general.connection device show {} as a backup should the first call fail and nmcli returns the SSID as is.

I propose to change the order of commands to nmcli ... || iw ... because it is better anyways to first query nmcli instead of iw | grep | sed. Also note here that the grep | sed call could be combined into a single sed call. I also think that this parsing should rather be done in rust using regexp than calling sh -c 'iw | grep | sed' ;)

jheyens commented 6 years ago

nmcli is part of NetworkManager, though, which most i3 (and thus i3status-rust) users don't even install on their machines.

I spent the last hour trying to find a clean linux way of obtaining the ESSID of the currently connected network, but unfortunately, there are no good news. The ESSID attribute is neither exposed to /proc/net/ nor to /sys/class/net filesystems. Additionally, due to Linux' history of wifi interfaces, we won't ever be able to get a generic way of obtaining this information. The iw tool links against the nl80211 kernel library and is probably the most widespread choice. Assuming we don't want to link that library ourselves, we probably should simply parse the iw output (which is discouraged since the iw authors don't consider its output stable).

The source of the printing C function is very simple, so we only need to take care of non-utf-8 sequences:

util.c:332

void print_ssid_escaped(const uint8_t len, const uint8_t *data)
{
    int i;

    for (i = 0; i < len; i++) {
        if (isprint(data[i]) && data[i] != ' ' && data[i] != '\\')
            printf("%c", data[i]);
        else if (data[i] == ' ' &&
             (i != 0 && i != len -1))
            printf(" ");
        else
            printf("\\x%.2x", data[i]);
    }
}
sebastianst commented 6 years ago

We could check if the command nmcli is available and only use it then. (All people I know who use i3 on their laptop do use nm because of the dynamic networking situation with a laptop ;)

Another, probably nicer, option could be to listen to wpa_supplicant DBUS events so we don't even have to poll the SSID but get it delivered when it changes.

atheriel commented 6 years ago

The lack of a native way to get the ESSID was a major source of irritation when I did the implementation, and I’d be very glad to be rid of the shell-out-to-iw-or-nmcli approach.

Since we’re increasingly using D-Bus I like the idea of using it here as well — does anyone know how widespread the wpa_supplicant interface is?

Alternatively, we could look at what other bars are doing here.

ps. One thing to keep in mind on this topic is that the ESSID is in no way required to be UTF-8. We’ll likely end up doing lossy conversion on the Rust side, but I don’t know how emojis will fare in that conversion.

jheyens commented 6 years ago

Since WEP networks aren't common any more, people seem to be forced to either use wpa_supplicant or iwd, which seems to be supposed to replace wpa_supplicant, though didn't use it yet.

NetworkManager requires wpa_supplicant by the way.

sebastianst commented 6 years ago

I checked how py3status and i3status do it. i3status natively gets the ssid somehow but the code looked quite convoluted and I couldn't be bothered to fully understand how ;) py3status also just calls iw, parses its output in python and decodes the escaped unicode, see
https://github.com/ultrabug/py3status/commit/cdf34b93673fbdc3722a6bb42e0b97c242b3bfc7 where unicode support was introduced.

jheyens commented 6 years ago

i3status links against the nl80211 library I mentioned above.

@atheriel @greshake Is this something we want? Linking to C libraries? There's not yet a rust binding to nl80211 and I'm not sure I'ld want to maintain one. Additionally, we would need research a bit about how stable across kernels it is.

€: nl80211

sebastianst commented 6 years ago

what about dbus subscription, so changes are only pushed on change, not polled?

atheriel commented 6 years ago

@jheyens We're not dying to maintain bindings, no. I'd strongly prefer going to the D-Bus route proposed by @sebastianst in the long term.

It would also be possible to resolve this specific issue by looking for \x sequences and translating them to the appropriate UTF-8.

atheriel commented 5 years ago

I'm going to close this in favour of #308, although if anyone wants to take on parsing \x sequences with the existing code I will happily accept such a PR in the meantime.