--- gitea: none include_toc: true --- # linux-on-desktop My journey to get Alpine + Wayland + River to work on ThinkPad T14 G3 AMD (21CF004PGE) ## Preparing hardware Disable secure boot and fn-lock in bios (so that F-keys work as F-keys by default and require Fn for their secondary multimedia functions). ## Installing Alpine Follow https://wiki.alpinelinux.org/wiki/Installation Note that you will need to use Rufus; Ventoy does not work on this laptop (hangs after choosing the image). ## Postinstall In `/etc/apk/repositories`, comment out 3.16, uncomment edge (main, community, testing), since some of the packages we're going to install (river, element-desktop) only exist in testing. Then ``` doas apk update doas apk upgrade ``` ## Installing river ``` doas apk add eudev doas setup-udev doas apk add mesa-dri-gallium mesa-va-gallium doas apk add river river-doc mandoc doas apk add adwaita-icon-theme foot ttf-dejavu doas rc-update add seatd doas rc-service seatd start doas addgroup YOURUSER audio doas addgroup YOURUSER input doas addgroup YOURUSER seat doas addgroup YOURUSER video doas addgroup YOURUSER wheel install -Dm0755 /usr/share/doc/river/examples/init -t ~/.config/river ``` Also: ``` doas apk add xwayland ``` because the latest river in testing is built in a way that requires xwayland. Try to run with ``` XDG_RUNTIME_DIR=/tmp river ``` You should see the blue screen of river. Try to open terminal with Win+Shift+Enter. Try to exit with Win+Shift+E. ### Login manager To enter username/password in GUI, get to river after that, and get back to logon screen after exiting river: ``` doas apk add elogind polkit-elogind doas rc-update add elogind doas rc-service elogind start doas apk add greetd greetd-gtkgreet cage doas addgroup greetd video doas rc-update add greetd ``` change `/etc/greetd/config.toml` ``` command = "cage -s -- gtkgreet" ``` and create `/etc/greetd/environments` with a single line `river` and reboot. I didn't find a way to make cage+gtkgreet handle HiDPI, the text is very tiny. An alternative is agreety, but for some reason it seems that both greeter and standard linux login prompt run at the same time on the same terminal, making it impossible to actually login. ### Installing Waybar ``` doas apk add waybar ``` and add startup section at the end of river init file (`~/.config/river/init`): ``` # startup riverctl spawn "waybar" ``` For time to work, ``` doas apk add tzdata ``` ### HiDPI ``` doas apk add kanshi mkdir .config/kanshi ``` and create `.config/kanshi/config` with the following: ``` profile { output eDP-1 enable scale 2.5 } ``` (`eDP-1` identifier was obtained by installing sway, running sway (by adding it to `/etc/greetd/environments`, and in terminal inside sway executing `swaymsg -t get_outputs`). #### HiDPI - cursors And in order to have decently sized mouse cursors instead of the tiniest ones, add the following line to the top of `.config/river/init`: ``` riverctl xcursor-theme Adwaita 24 ``` This will only affect river itself, the cursor will stay tiny in waybar and firefox and maybe other applications. To solve this: ``` doas apk add gsettings-desktop-schemas gsettings set org.gnome.desktop.interface cursor-theme 'Adwaita' ``` ### Environment In order to not have to create wrapper scripts for all apps: Create `/usr/local/bin/inga-river` (and later `chmod +x /usr/local/bin/inga-river`) with the following (found in google): ``` #!/bin/sh export GDK_BACKEND=wayland,x11 export MOZ_ENABLE_WAYLAND=1 export CLUTTER_BACKEND=wayland export QT_QPA_PLATFORM=wayland-egl export ECORE_EVAS_ENGINE=wayland-egl export ELM_ENGINE=wayland_egl export SDL_VIDEODRIVER=wayland export _JAVA_AWT_WM_NONREPARENTING=1 export NO_AT_BRIDGE=1 export BEMENU_BACKEND=wayland river $@ ``` and replace `river` with `inga-river` in `/etc/greetd/environments`. ### Launcher `doas apk add bemenu`, and then add this line into your river config: ``` riverctl map normal Super R spawn 'pidof bemenu-run || bemenu-run -i -n' ``` ### Screenshots `doas apk add wayshot`, then add this line to river config: ``` riverctl map normal None Print spawn 'wayshot --stdout | wl-copy' ``` ### Clipboard Works by default, use Ctrl+Shift+C and Ctrl+Shift+V in foot ### Emoji keyboard `doas apk add rofi-emoji rofi-emoji-wayland wtype` and add this line to your river config ``` riverctl map normal Super period spawn 'rofi -modi emoji -show emoji' ``` ## Other software ### Firefox ``` doas apk add firefox firefox ``` go to `about:support` and make sure that Window Protocol is wayland, not xwayland. (it should be wayland because MOZ_ENABLE_WAYLAND is set to 1 by inga-river) For some reason, while sound in general works fine in firefox (after following the steps from Hardware section), in WebRTC pages there is crackling much louder than the actual voices, making it unusable for voice/video calls/meetings. ### Chrome ``` doas apk add chromium ``` It should also be wayland by default, but you can check it by `doas apk add xeyes && xeyes`. In order for screen sharing to work, go to `chrome://flags/#enable-webrtc-pipewire-capturer` and enable it. Note though that every time you screenshare, there will be two promps from xdg-desktop-portal, one for picking a source, and another for actually sharing. ### Git ``` doas apk install git git config --global credential.helper --store ``` ### Telegram ``` doas apk install telegram-desktop ``` ### Element ``` doas apk install element-desktop ``` ## Hardware ### Sleep With default settings, laptop goes to sleep after some idle period. When it wakes up, the root fs is readonly, meaning that I have to restart the laptop. Adding `acpiphp.disable=1` and `pcie_aspm=off` to grub config does not solve the issue. Disabling S0ix in UEFI only made things worse: even though `cat /sys/power/mem_sleep` reported that S3 (deep) is default, after system goes to sleep it is impossible to wake it up, it does not react to key or power button presses. What did solve the issue was: * reenable S0ix in BIOS, * `doas apk add linux-firmware-amdgpu`, * editing `GRUB_CMDLINE_LINUX_DEFAULT` in `/etc/default/grub` to add the following options: `acpiphp.disable=1 pcie_aspm=off acpi_osi='Windows 2020' iommu=soft`. Now after waking up (after being suspended with `doas pm-suspend` from `pm-utils` package) root fs is still readwrite. But sometimes network disappears after wakeup. For suspend on lid close and unsuspend on open, follow https://wiki.alpinelinux.org/wiki/Suspend_on_LID_close : ``` doas mkdir -p /etc/acpi/LID doas nano /etc/acpi/LID/00000080 ``` should have the following content ``` #!/bin/sh exec pm-suspend ``` and then ``` doas chmod +x /etc/acpi/LID/00000080 doas rc-service acpid restart ``` ### WiFi At the moment Linux kernel does not support Qualcomm NFA725. Otherwise, alpine wiki describes how to configure WiFi using `iwd`. ### Backlight ``` doas apk add light doas nano /etc/udev/rules.d/backlight.rules ``` add the following lines into backlight.rules to make it possible for all users in video group (not just superusers) to control backlight: ``` ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="amdgpu_bl0", RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness" ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="amdgpu_bl0", RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness" ``` and then ``` doas rc-service udev restart ``` Backlight control with Fn+F5/F6 should work now. (`amdgpu_bl0` is specific for this laptop; value for others can be obtained from `/sys/class/backlight/`) ### Trackpoint Disregard this: > ``` >find /sys/devices/platform/i8042/ -name name | xargs grep -Fl TrackPoint > ``` > to find which serio corresponds to trackpoint, then > ``` > echo 70 | doas tee /sys/devices/platform/i8042/serio1/sensitivity > ``` > for reasonably low sensitivity (does not persist; for persistence TODO separate udev rule). Instead of configuring sensitivity, it's probably better to configure pointer speed in river. Find your trackpoint with `riverctl list-inputs | grep -i trackpoint`, it will look like `2:10:TPPS/2_Elan_TrackPoint`. Then add the following to your river config: ``` riverctl input 2:10:TPPS/2_Elan_TrackPoint accel-profile adaptive riverctl input 2:10:TPPS/2_Elan_TrackPoint pointer-accel -0.5 ``` ### Sound Based on https://wiki.alpinelinux.org/wiki/PipeWire ``` doas apk add dbus dbus-openrc doas rc-service dbus start doas apk add pipewire wireplumber rtkit alsa-utils pipewire-alsa doas addgroup YOURUSER rtkit doas addgroup root audio alsamixer ``` In alsamixer, use F6 to find the target sound card (most likely 0 is HDMI and 1 is ordinary). Remember its number, and in `/usr/share/alsa/alsa.conf`, replace `defaults.ctl.card` and `defaults.pcm.card` with the target number. In `/usr/local/bin/inga-river`, replace `river $@` with `dbus-run-session -- river $@`, relogin (Ctrl+Shift+E). Then ``` doas rc-service alsa start doas rc-update add alsa /usr/libexec/pipewire-launcher ``` Make sure that everything works (with `wpctl status`, `pw-cat -p YOURFILE.flac` or just opening YouTube in FF). Then make pipewire start automatically: in river config, add another startup line: ``` riverctl spawn "/usr/libexec/pipewire-launcher" ``` Control microphone and volume with `alsamixer`. And change the handlers for XF86Audio (adding `-repeat` and replacing the spawned command): ``` riverctl map -repeat $mode None XF86AudioRaiseVolume spawn 'amixer set "Master" 5%+' riverctl map -repeat $mode None XF86AudioLowerVolume spawn 'amixer set "Master" 5%-' riverctl map $mode None XF86AudioMute spawn 'amixer set "Master" toggle' ``` Note that the internal microphone does not work and is not detected by pipewire. Only external microphones work. Mic in browser didn't work, but then it started to work at some point, without me seemingly changing anything. #### Mic mute button Create `/etc/acpi/events/lenovo-mutemic` with the following content: ``` event=button/micmute MICMUTE 00000080 00000000 K action=amixer --card 1 set 'Capture' toggle ``` where card number is the one obtained in previous section; and event could be determined by running `acpi_listen` and pressing mic button. Then, after `doas rc-service acpid restart`, mic button should control internal mic capture in alsa and switch internal mic led on and off. ### Webcam Should work after following the steps for "Audio". Can be tested in https://webrtc.github.io/test-pages ## Additional ### Screen sharing ``` doas apk add xdg-desktop-portal xdg-desktop-portal-wlr ``` and DO NOT add to your river config ``` riverctl spawn "/usr/libexec/xdg-desktop-portal-wlr" ``` (for some reason it does not work when spawned by river during init (process is running, but screensharing attempts lead to nothing), but `/usr/libexec/xdg-desktop-portal-wlr &` from shell works fine) ### Prevent firefox sharing indicator from taking the entire tile Add the following lines to river config (before the last `exec` line): ``` riverctl float-filter-add title "Firefox — Sharing Indicator" riverctl float-filter-add title 'Firefox — Sharing Indicator' ``` (TODO: check which kind of quotes works) ## Development (containers) ### Unprivileged LXC with routing (based on https://linuxcontainers.org/lxc/getting-started and https://wiki.alpinelinux.org/wiki/LXC) #### Networking (host) (assuming that your internet-connected interface is eth0, and that you want to use 10.157.1.0/24 subnet for the container) ``` doas apk add bridge doas modprobe dummy doas brctl addbr br0 doas brctl setfd br0 0 doas brctl addif br0 dummy0 doas ifconfig br0 10.157.1.1 netmask 255.255.255.0 up echo 1 | doas tee -a /proc/sys/net/ipv4/ip_forward doas apk add iptables doas rc-update add iptables doas iptables --table nat --append POSTROUTING --out-interface eth0 -j MASQUERADE doas iptables --append FORWARD --in-interface br0 -j ACCEPT ``` to persist: ``` echo dummy | doas tee -a /etc/modules doas /etc/init.d/iptables save ``` #### Containers support ``` doas apk add lxc lxcfs lxc-download xz gnupg echo "$(id -un):2000000:65536" | doas tee -a /etc/subuid echo "$(id -un):2000000:65536 | doas tee -a /etc/subgid echo "$(id -un) veth br0 10" | doas tee -a /etc/lxc/lxc-usernet ``` #### Creating container Create `~/.config/lxc/CONTAINERNAME.conf" with the following content: ``` lxc.net.0.type = veth lxc.net.0.flags = up lxc.net.0.link = br0 lxc.net.0.ipv4.address = 10.157.1.2/24 10.157.1.255 lxc.net.0.ipv4.gateway = 10.157.1.1 lxc.net.0.veth.pair = veth-if-0 lxc.idmap = u 0 2000000 65536 lxc.idmap = g 0 2000000 65536 ``` Then: ``` lxc-create -n CONTAINERNAME -f .config/lxc/CONTAINERNAME.conf -t download # pick OS (alpine/edge/amd64 in my case) lxc-start -n CONTAINERNAME # make sure it does not produce any errors lxc-attach -n CONTAINERNAME ``` You'll get into a container root console. #### Networking (container) In container root console, check if network is up with `ifconfig`. If there are no IPv4 address for eth0, you'll have to configure it manually, by editing `/etc/network/interfaces` either with VI or with cat/echo. In the end it should look like ``` auto eth0 iface eth0 inet static address 10.157.1.2 netmask 255.255.255.0 gateway 10.157.1.1 hostname ........ ``` Then `rc-service networking restart`, and check `ifconfig`. If everything is right, there should be an ipv4 address in `ifconfig`, and `ping 10.157.1.1` inside container and `ping 10.157.1.2` inside host should work. `ping 8.8.8.8` inside container should work too, thanks for routing. Now, if `ping google.com` does not work, configure DNS in container: ``` echo nameserver 8.8.8.8 >> /etc/resolv.conf echo nameserver 8.8.4.4 >> /etc/resolv.conf ``` Make sure `ping 8.8.8.8` works. APK should work too: `apd add nano neofetch` #### Creating an user inside container In container root shell: ``` adduser -g USERNAME USERNAME adduser USERNAME wheel echo "permit persist :wheel" > /etc/doas.d/doas.conf ``` Now exit root shell (just with `exit`), and try `lxc-console -n CONTAINERNAME`. You should be able to log in using the new username and password. (To exit lxc console, use Ctrl+A, Q) ## TODO * Fix internal mic * Docker * IDE * Notifications * Make river usable * Make waybar usable (+waybar fonts) * nushell + starship instead of ash * Mail client * Fix call audio in firefox * WiFi