Newer
Older
EARS-LINUX / OS / Readme.md

Xubuntu Setup — Asus T101HA (ears-tablet)

Working notes for setting up an Asus T101HA (Atom x5-Z8350, 2 GB RAM / ~1.8 GB usable, eMMC) as a Mono + GTK#3 development and runtime machine.

User: ears · Hostname: ears-tablet


Contents


Target: Xubuntu 24.04 LTS (Minimal ISO).

Reasons, from the apt/Mono investigation:

  • The machine was found running Ubuntu 25.10 "questing", which hit end of life on 9 July 2026. No further updates will ever be published for it.
  • gtk-sharp3 is not packaged on 25.10, and upgrading to 26.04 would not bring it back.
  • gtk-sharp3 is packaged on 24.04 (noble), supported to 2029.
  • Xfce on 24.04 idles around 500–600 MB, which matters at 1.8 GB RAM.

Mono + mcs + GTK# is the correct toolchain for this hardware — the .NET SDK (~800 MB) and Avalonia are far heavier.

Before reinstalling, back up: *.cs, *.glade, SSH keys, browser profile.

lsblk            # is /home a separate partition?
df -h /
free -h
mkdir -p ~/backup && cp ~/*.cs ~/*.glade ~/backup/

If /home is its own partition, it can be preserved during install (assign /home without format). Use the same username to keep permissions clean.


1. Installing Xubuntu

1.1 Check firmware bitness first — this determines everything

In Windows: Settings → System → About → System type

  • 32-bit operating system, x64-based processorIA32 UEFI, needs the bootia32 workaround below.
  • 64-bit → skip the GRUB steps.

1.2 Build the USB

Ventoy includes IA32 UEFI support, so try it first. If it won't boot, fall back to Rufus and copy a 32-bit bootia32.efi (GRUB) into /EFI/BOOT on the stick.

From Linux, writing directly:

sudo dd if=xubuntu-24.04-minimal-amd64.iso of=/dev/sdX bs=4M status=progress conv=fsync

Verify the target with lsblk first. dd to the wrong disk destroys it silently.

Verify the ISO: sha256sum against the checksum on the download page.

1.3 BIOS

  • F2 at power-on
  • Disable Secure Boot (the ia32 GRUB is unsigned)
  • Boot menu is usually F12 / Esc

1.4 Install

Choose Install, not Try — the live session eats RAM you don't have. Don't open Firefox or anything else during install. Let it create swap.

1.5 Post-install GRUB fix (IA32 machines only)

The installer writes a 64-bit grubx64.efi the firmware can't read. Boot the live session, chroot in, and:

grub-install --target=i386-efi --efi-directory=/boot/efi

This is not optional on IA32 firmware.

1.6 Expect to fix afterwards

ComponentNotes
Wi-FiRTL8723BS SDIO — finicky
AudioESS ES8316 codec, needs UCM configs
TouchscreenWorks, but rotation needs manual matrix (§6)
Auto-rotationNeeds iio-sensor-proxy + custom handler

A recent 6.x kernel fixes most of these.


2. First boot / recovery mode

If the user account lacks admin rights:

usermod -aG sudo ears
groups ears                       # confirm 'sudo' appears
hostnamectl set-hostname ears-tablet
localectl set-locale LANG=en_US.UTF-8   # optional, cosmetic
exit                              # then 'resume'

usermod -aG sudo is the one that isn't optional — without it you can log in but can't run any admin command.


3. Low-RAM tuning

Option A — zram-tools (simple)

sudo apt install zram-tools

Edit /etc/default/zramswap:

PERCENT=60
ALGO=zstd

Option B — systemd-zram-generator (current, preferred on 24.04+)

sudo apt install systemd-zram-generator

sudo tee /etc/systemd/zram-generator.conf > /dev/null <<'EOF'
[zram0]
zram-size = min(ram / 2, 4096)
compression-algorithm = zstd
swap-priority = 100
EOF

sudo systemctl daemon-reload
sudo systemctl start systemd-zram-setup@zram0.service
swapon --show     # expect /dev/zram0
zramctl

The package does nothing without a config file containing at least one section.

Swappiness

Swapping to zram is cheap, so raise it so the kernel actually uses it:

echo 'vm.swappiness=100' | sudo tee /etc/sysctl.d/99-zram.conf

Reboot. Verdict: worth it at 2–4 GB, marginal at 8 GB, skip above 16 GB. zram compresses RAM — it does nothing for low disk space.


4. apt: repair and sources

4.1 The modern layout

Since 24.04, archive config lives in /etc/apt/sources.list.d/ubuntu.sources (deb822 format). /etc/apt/sources.list being empty is normal, not a symptom. Most apt advice online predates this and edits a file that does nothing.

4.2 Correct stock sources (25.10 example — substitute your codename)

sudo tee /etc/apt/sources.list.d/ubuntu.sources > /dev/null <<'EOF'
Types: deb
URIs: http://archive.ubuntu.com/ubuntu/
Suites: questing questing-updates questing-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

Types: deb
URIs: http://security.ubuntu.com/ubuntu/
Suites: questing-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
EOF

sudo apt update

Do not drop the Signed-By: line — without it apt falls back to the legacy trusted-keyring and you get confusing "not signed" errors later.

ubuntu.sources.curtin.orig is the installer's pristine backup. apt ignores .orig files. Useful as a reference, don't copy blindly.

4.3 Which host? archive vs old-releases

An EOL release only moves to old-releases.ubuntu.com when the migration actually runs — which can lag the EOL date by weeks. Pointing there too early gives four clean 404s. Test before switching:

for p in questing questing-updates questing-backports questing-security; do
  echo -n "$p: "
  curl -sI "http://old-releases.ubuntu.com/ubuntu/dists/$p/Release" | head -1
done

curl -sI http://archive.ubuntu.com/ubuntu/dists/questing/Release | head -1

200 = pocket exists. 404 = drop it or use the other host.

4.4 Check for genuinely broken packages

All read-only, cheapest first:

sudo dpkg --audit          # clean = no output at all
sudo apt-get check         # verifies the dependency tree
dpkg -l | grep -v '^ii' | grep -v '^rc'
apt-mark showhold          # held packages are silently skipped on upgrade

dpkg status codes (desired state, then actual):

CodeMeaning
iiHealthy
rcRemoved, config left — harmless
iUUnpacked, never configured
iFHalf-configured
iHHalf-installed
iW / itWaiting on / pending triggers
*R*Reinstall required → apt install --reinstall <name>

Then repair:

sudo dpkg --configure -a
sudo apt --fix-broken install
sudo apt full-upgrade

4.5 Don't do these

# WRONG — killall receives 'sudo', 'rm', paths etc. as process names
sudo killall apt apt-get dpkg 2>/dev/null sudo rm /var/lib/apt/lists/lock ...

# DANGEROUS — rm -rf also targets files named sudo, apt, clean, update in $PWD
sudo rm -rf /var/lib/apt/lists/* sudo apt clean sudo apt update

These must be separate lines. Also: killing dpkg mid-transaction is what creates the broken state. Check first with ps aux | grep -E 'apt|dpkg'.

Don't remove universe — it's an Ubuntu component, not a PPA, and gtk-sharp3 lives there. And never run add-apt-repository --remove ppa:repository-name/ppa literally; that's a placeholder from a copied guide.

4.6 full-upgrade checklist

  • Use full-upgrade, not upgrade — it permits removals where deps changed
  • Read the summary line; mass removals of desktop/kernel packages = stop
  • Plug the tablet in; power loss mid-dpkg recreates the broken state
  • On config prompts, keeping the local version (the default) is safe unless you know you edited it
  • Then sudo apt autoremove — but answer n if anything with efi appears
  • Reboot before do-release-upgrade so the newest kernel is running

4.7 Reboot needed?

[ -f /var/run/reboot-required ] && cat /var/run/reboot-required.pkgs
sudo needrestart     # interactive; shows what's running old libs

Kernel / initramfs / systemd / glibc → reboot. Everything else → restart the service.


5. Mono + GTK#3

Install

sudo add-apt-repository universe
sudo apt update
sudo apt install mono-complete gtk-sharp3
PackageContentsSize
mono-runtimeJIT + mscorlib only~15 MB
mono-develCompiler + the BCL you need~200–400 MB
mono-completeRuntime + mcs + entire class library + F#/VB~300–500 MB

Avoid mono-runtime. The BCL is split across dozens of libmono-system-*-cil packages; hello-world runs, but anything touching LINQ, XML, HTTP, or System.IO.Ports (the COM-port combo box in homepage.glade) throws Could not load file or assembly. On a tight disk, mono-devel saves ~100 MB over mono-complete and still covers everything this project needs.

Optional

sudo apt install glade         # visual designer — skip on this machine
sudo apt install libgdiplus    # only if using System.Drawing
sudo apt install libcanberra-gtk3-module   # silences a harmless console warning

Verify

mono --version
mcs --version
pkg-config --list-all | grep gtk-sharp     # want gtk-sharp-3.0
apt policy gtk-sharp3

Smoke test

cat > gtkcheck.cs <<'EOF'
using System;
using Gtk;

class GtkCheck {
    static void Main() {
        Application.Init();
        var win = new Window("Hello GTK#3");
        win.SetDefaultSize(300, 200);
        win.DeleteEvent += (o, args) => Application.Quit();

        var button = new Button("Click me");
        button.Clicked += (o, args) => Console.WriteLine("Clicked!");
        win.Add(button);

        win.ShowAll();
        Application.Run();
    }
}
EOF

mcs -pkg:gtk-sharp-3.0 gtkcheck.cs
mono gtkcheck.exe

Do the throwaway test first — it isolates "is gtk-sharp3 working" from "does my app compile."

Then the real thing:

mcs -pkg:gtk-sharp-3.0 program.cs MainWindow.cs ArduinoConnection.cs -out:test.exe
mono test.exe

(ArduinoConnection.cs is needed because MainWindow calls _arduino.Disconnect(); omitting it gives CS0246.)

Notes vs. the WSL guide

  • No WSLg, no $DISPLAY fiddling — Xfce is itself GTK3, so the app picks up the system theme and the window just appears.
  • Only exception: over SSH you need ssh -X.
  • Companion files (-r: DLLs, homepage.glade) must sit next to the .exe, or you get FileNotFoundException.
  • homepage.glade targets GTK 3.24, which 22.04 and 24.04 both ship.
  • gtk-sharp2 is gone from 24.04 onward — use gtk-sharp3.

6. Display rotation + touchscreen

X11 does not rotate touch input when you rotate the display. The touch device needs its own coordinate transformation matrix.

Identify devices

xrandr                          # output name — DSI-1, or None-1 on this unit
xinput list --name-only         # look for Silead / GSL / SIS, NOT 'Touchpad'

On this tablet the output reported as None-1 (xrandr's fallback when the connector type isn't identified — it can change between kernels) and the touchscreen as SIS0457:00 0457:11ED.

Matrices

RotationMatrix
normal1 0 0 0 1 0 0 0 1
right (90° CW)0 1 0 -1 0 1 0 0 1
left (90° CCW)0 -1 1 1 0 0 0 0 1
inverted (180°)-1 0 1 0 -1 1 0 0 1
xrandr --output None-1 --rotate right
xinput set-prop "SIS0457:00 0457:11ED" "Coordinate Transformation Matrix" 0 1 0 -1 0 1 0 0 1

Persistence script

nano ~/rotate.sh
#!/bin/bash
sleep 3
OUT=$(xrandr | awk '/ connected/{print $1; exit}')
xrandr --output "$OUT" --rotate right
TS=$(xinput list --name-only | grep -iE 'silead|gsl|touchscreen|SIS' | grep -vi 'touchpad' | head -1)
[ -n "$TS" ] && xinput set-prop "$TS" "Coordinate Transformation Matrix" 0 1 0 -1 0 1 0 0 1
xset s off
xset -dpms
chmod +x ~/rotate.sh
~/rotate.sh          # test before automating

Then Settings → Session and Startup → Application Autostart → Add, command /home/ears/rotate.sh.

Gotcha: grepping bare touch matches the trackpad and rotates the pointer instead. Use touchscreen and exclude touchpad. To undo:

TP=$(xinput list --name-only | grep -i -m1 touchpad)
xinput set-prop "$TP" "Coordinate Transformation Matrix" 1 0 0 0 1 0 0 0 1

Or just log out — the matrix isn't saved anywhere.

The sleep 3 matters; the touchscreen isn't registered the instant the session starts. Raise to 5 if touch still comes out wrong.

Try Settings → Display first. Its rotation dropdown often persists on its own, leaving the script to handle only the xinput part.

Login screen (LightDM — separate from your session)

sudo nano /etc/lightdm/lightdm.conf
[Seat:*]
display-setup-script=/usr/bin/xrandr --output None-1 --rotate right

Better: rotate before the session starts

sudo nano /etc/X11/xorg.conf.d/10-monitor.conf
Section "Monitor"
    Identifier "DSI-1"
    Option "Rotate" "right"
EndSection

Whole machine including the text console — add to /etc/default/grub: fbcon=rotate:1 plus video=DSI-1:panel_orientation=right_side_up, then sudo update-grub.

Wallpaper breaks after rotating

The desktop paints the wallpaper onto a surface sized for the old geometry and doesn't repaint after xrandr. On Xfce:

xfdesktop --reload

Also set the image style to scaledspanned and zoom misbehave badly when the aspect ratio flips. Rotating before the session starts avoids this entirely.

Auto-rotation (optional)

Xfce has no built-in handler. Confirm the sensor works before writing anything:

sudo apt install -y iio-sensor-proxy
monitor-sensor        # tilt the tablet; orientation should print

If nothing prints, this unit needs a kernel quirk — not worth chasing.

monitor-sensor | while read -r line; do
  case "$line" in
    *normal*)    R=normal;   M="1 0 0 0 1 0 0 0 1" ;;
    *right-up*)  R=right;    M="0 1 0 -1 0 1 0 0 1" ;;
    *left-up*)   R=left;     M="0 -1 1 1 0 0 0 0 1" ;;
    *bottom-up*) R=inverted; M="-1 0 1 0 -1 1 0 0 1" ;;
    *) continue ;;
  esac
  xrandr --output DSI-1 --rotate "$R"
  xinput set-prop "SIS0457:00 0457:11ED" "Coordinate Transformation Matrix" $M
done

Most community auto-rotate scripts only call xrandr and leave touch behind. If a script doesn't contain xinput set-prop, it won't rotate touch.


7. Audio

Install and test

sudo apt install alsa-utils      # this is what provides aplay

aplay --version
aplay -l                          # list playback devices
aplay /usr/share/sounds/alsa/Front_Center.wav
speaker-test -c 2 -t wav -l 1

PipeWire/PulseAudio layer:

pactl info                  # "Server Name" tells you which
pactl list short sinks
paplay /usr/share/sounds/alsa/Front_Center.wav

Choosing the right card

Neither config file exists by default — create whichever you prefer:

nano ~/.asoundrc              # per-user; wins over the system file
# or
sudo nano /etc/asound.conf    # system-wide, incl. root and systemd services
defaults.pcm.card 1
defaults.ctl.card 1

ctl is what alsamixer reads, pcm is what aplay reads — set both. No reload needed; it's read per-process.

With plughw conversion behaviour as the default:

pcm.!default {
    type plug
    slave.pcm "hw:1,0"
}
ctl.!default {
    type hw
    card 1
}

Verify — -v prints the resolved device:

aplay -D default -v /usr/share/sounds/alsa/Front_Center.wav 2>&1 | head -20

Renumbering at the kernel level

cat /proc/asound/modules
sudo nano /etc/modprobe.d/alsa-card-order.conf
options snd_soc_sst_bytcr_rt5640 index=0
options snd_hda_intel index=1
sudo update-initramfs -u && sudo reboot

Caveats

  • PipeWire ignores all of the above. ~/.asoundrc only governs programs talking to ALSA directly (aplay, alsamixer). For desktop apps use pactl set-default-sink or pavucontrol.
  • Save your mixer levels or they come back muted: sudo alsactl store 1
  • Device busy → use aplay -D default file.wav, not -D hw:0,0
  • Permission errors → sudo usermod -aG audio $USER, then re-login
  • aplay handles WAV/AU/RAW only — use mpg123 or ffplay for MP3/FLAC

8. Running a script at boot

sudo nano /usr/local/bin/myscript.sh
sudo chmod +x /usr/local/bin/myscript.sh

Start with #!/bin/bash and use absolute paths — there's almost no environment at boot.

sudo nano /etc/systemd/system/myscript.service
[Unit]
Description=My boot script
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/myscript.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now myscript.service
systemctl status myscript.service
journalctl -u myscript.service

Variations:

  • Long-running daemon → Type=simple, drop RemainAfterExit
  • Before the login screen → Before=display-manager.service in [Unit], WantedBy=display-manager.service in [Install]
  • Quick and dirty → sudo crontab -e, then @reboot /usr/local/bin/myscript.sh (no logging, no ordering)
  • Drop After=/Wants= if networking isn't needed — it'll run earlier

A root systemd service cannot rotate the display — no DISPLAY, no XAUTHORITY. Use the config-level methods in §6 instead. Same for audio: a root service has no audio session, so use systemctl --user or target the hardware device directly.


9. File transfer to/from Windows

Tailscale is not required. It creates a private network across the internet — only needed to reach the tablet from outside your home. For a tablet and PC on the same Wi-Fi it's just another background daemon on a 2 GB machine.

Lightest option — SSH:

sudo apt install -y openssh-server

Connect from Windows with WinSCP or scp in PowerShell. No configuration beyond the above.

Samba is only worth it if you want the tablet's folders to appear in Windows Explorer as a normal network drive.


10. Quick reference

# state
free -h; df -h /; lsblk; swapon --show
cat /etc/os-release
. /etc/os-release; echo $VERSION_CODENAME

# apt health
sudo dpkg --audit; sudo apt-get check; apt-mark showhold
ls /etc/apt/sources.list.d/
cat /etc/apt/sources.list.d/ubuntu.sources

# display
xrandr; xinput list --name-only
~/rotate.sh; xfdesktop --reload

# audio
aplay -l; pactl list short sinks
aplay /usr/share/sounds/alsa/Front_Center.wav
sudo alsactl store 1

# build
mcs -pkg:gtk-sharp-3.0 program.cs MainWindow.cs ArduinoConnection.cs -out:test.exe
mono test.exe