This entry summarizes MOBILE_SOFTWARE.md
in the repo root — a userland inventory for the FreeBSD-on-PinePhone-Pro
setup. Where the rest of this site is about the kernel, drivers, and
firmware, this is about everything above init: Sway session,
omfreebdy menus, on-screen keyboard, gesture daemon, phone controls,
telephony scripts, and per-category recommendations for browser, email,
music, calendar, etc.
The full document includes detailed comparison tables for every category and a phased install plan. The summary below picks the load-bearing decisions; consult the source doc for the long tables.
Our setup baseline
- Device: PinePhone Pro (RK3399S), 4 GB RAM, 720×1440 portrait.
- OS: FreeBSD 15 on eMMC; the old SD-card install remains useful as a forensic baseline but the live phone migrated to eMMC on 2026-05-04.
- Display: DRM/KMS on the MIPI DSI panel; Sway runs on the phone, and the current live session is no longer forced through pixman.
- Compositor / shell: Sway plus omfreebdy helpers and generated config blocks.
- Launcher / menus: fuzzel, wrapped by omfreebdy’s system, app, and
WiFi menus. The phone needs the touch-fixed
fuzzel 1.14.1binary, not the older package-default1.13.1. - On-screen keyboard:
wvkbd-mobintl. - Gesture daemon:
lisgd, configured for edge gestures and started against the Goodix evdev node through the restartablestart-lisgd-swaywrapper. - Touchscreen: Goodix GT917S multitouch, interrupt-driven.
- Networking: USB Ethernet remains the management link; native
AP6255/BCM43455 SDIO WiFi now scans, associates to WPA2, and gets DHCP
on
wlan0. - Audio: RT5640 + I2S0 loudspeaker playback works. Volume keys drive
mixer(8)through a single themed on-screen volume meter. - Phone controls: side power button toggles Sway output power;
RGB notification LED is controlled through
phone-led. - Modem: Quectel EG25-G via USB, AT commands confirmed on
/dev/cuaU*; cellular data/SMS/calls remain userspace work.
GTK fallback knob
The live path is Wayland/Sway. Keep this fallback for GTK4 apps if they hit rendering problems on the phone:
export GSK_RENDERER=cairo
GTK3 apps usually default to Cairo and do not need it.
Sxmo as the reference
Sxmo is still the reference architecture: small
programs, shell glue, one-purpose menus, and gestures instead of a giant
phone daemon. Our live stack swaps in Sway, fuzzel, mako, wvkbd, and
omfreebdy, but the shape is the same. The sxmo gesture map is the direct
model for our lisgd bindings: edge swipes for back/forward, app menu,
system menu, and keyboard control.
What we can’t steal from sxmo: the dwm patches (we use Sway),
callaudiod (Linux-only audio daemon — we’ll use sndio/OSS),
ModemManager/mmcli (replaced with direct AT commands), pipewire
(Linux-only).
Telephony stack: AT commands, not ModemManager
FreeBSD has no ModemManager, so we talk directly to the EG25-G over
USB serial. After mise run modem:power:phone, u3g0 exposes five
ports:
| Device | Purpose |
|---|---|
/dev/cuaU0.0 | DM / diagnostic candidate |
/dev/cuaU0.1 | GPS NMEA candidate |
/dev/cuaU0.2 | AT command port (confirmed) |
/dev/cuaU0.3 | AT / PPP data candidate (AT confirmed) |
/dev/cuaU0.4 | auxiliary port |
The plan is a small set of shell scripts modeled on sxmo:
ppp-dial.sh— fuzzel interface to dial a number or pick fromcontacts.tsvppp-sms.sh— fuzzel interface to compose / read SMSppp-monitor.sh— daemon watching/dev/cuaU0.2for incoming call/SMS URCs (unsolicited result codes); fires mako + LED blinkppp-contacts.sh— managecontacts.tsv
Reference repos: fuzzybritches0/pinephone (bash AT scripts), milky-sway/pinephone-scripts, and the gold-standard sxmo-utils.
Audio routing for calls is no longer blocked on basic playback: RT5640 loudspeaker output works. The remaining call-audio work is microphone capture plus routing policy between earpiece, loudspeaker, headset, and Bluetooth.
Per-category app picks (one-line summary)
The full document compares 5–8 options per category in a table; the short version of the picks is:
| Category | Pick (FreeBSD pkg) | Notes |
|---|---|---|
| Browser | netsurf | Lightweight; falls back to surf for sites needing better rendering, w3m in terminal |
| File manager | nnn (terminal) / pcmanfm (GTK2) | Both touch-usable |
| Music | cmus (TUI) / mpv (CLI) | Daemon setup: musicpd + ncmpcpp |
aerc | Modern terminal client, works with wvkbd | |
| Calendar | calcurse | Terminal, lightweight, touch-usable |
| Notes | plain text + vi/nano | Sxmo approach |
| Calculator | galculator | GTK2, big touch targets, no GPU |
| Maps | foxtrotgps | OSM tiles, offline cache, GPS support |
| RSS | sfeed (sxmo style) or newsboat | |
| Podcasts | newsboat + podboat + mpv | All in pkg |
mupdf (touch pinch-zoom) / zathura (keyboard) | ||
| Image viewer | nsxiv | Thumbnail mode works on small screens |
| Camera | — | Blocked: no V4L2/media-request on FreeBSD |
| Clock/alarm | shell + at/cron + mako | |
| Weather | curl wttr.in | Zero deps |
| Password manager | pass + fuzzel wrapper | |
| Night light | wlsunset or custom helper | Wayland path, package availability TBD |
| Messaging | weechat (IRC + Matrix) / profanity (XMPP) |
What not to bother with
- Firefox / Chromium as baseline apps — even with GPU progress,
they are still too heavy to treat as the default phone browser on 4 GB
RAM. Firefox is installed for compatibility testing and should come from
the FreeBSD package directly; do not shadow
/usr/local/bin/firefoxwith a wrapper. Session-wide browser environment belongs in Sway or the launcher environment, and package desktop files are what make fuzzel list the app. The mobile UI layer is the postmarketOSmobile-config-firefoxruntime, vendored into/usr/local/lib/mobile-config-firefoxwith the Firefox autoconfig path adjusted for FreeBSD’s/usr/local/lib/firefox. On the PinePhone Pro image that autoconfig also setsmedia.cubeb.backend=ossandmedia.cubeb.sandbox=false, while the user PulseAudio client config setsautospawn = no. This is deliberate: the 2026-05-06 YouTube repro left PulseAudio’s OSS thread pegged even though Sway and Panfrost were still serviceable. Direct OSS keeps the diagnostic path in Firefox/FreeBSD PCM/RT5640 instead of silently spawning PulseAudio. - Megapixels — needs OpenGL + Linux media-request API. Camera is blocked anyway.
- GNOME Calls / Chatty — depend on ModemManager.
- Phosh / Plasma Mobile — useful references, but they assume the Linux phone stack around ModemManager, PipeWire, logind, and friends.
- Flatpak — not available on FreeBSD.
- Maemo Leste / Hildon — interesting mobile stack but deeply tied to Linux/Debian. Take ideas, not code.
Phased install plan
Phase 1 — Core mobile UX (now)
pkg install -y sway swaybg swayidle swaylock fuzzel mako \
grim slurp wl-clipboard htop picocom
Then install omfreebdy, run its phone install task, and let it manage
Sway config blocks for gestures, fuzzel menus, notification/OSD styling,
screen power on the hardware power button, WiFi UI, and theme hooks.
The install path must also build or install the phone-specific pieces that
FreeBSD packages do not provide in the right shape yet: wvkbd-mobintl,
lisgd, the fuzzel 1.14.1 touch fix, and the gdk-pixbuf loader cache used
by image-backed swaybg. Firefox comes from the package, while the mobile
chrome comes from the vendored postmarketOS mobile-config-firefox files.
The account shell is part of the install contract. The phone user must use
/usr/local/bin/bash, with .bash_profile sourcing .profile and .bashrc,
because omfreebdy’s CLI layer is bash-based. Without that, packages such as
eza and the Nerd Font set are present but the visible behavior is missing:
plain ls runs instead of the eza --icons --group-directories-first alias.
Install gettext-runtime with bash, then refresh ldconfig. If a locally
built bundle such as Ladybird is extracted into /usr/local, do not preserve
the build user’s owner IDs: a jadams-owned /usr/local/lib makes
ldconfig ignore the directory, which breaks SSH login with
libintl.so.8 missing before the shell can start.
The live overlay carries the current phone-session repair rules:
theme-setruns the post-theme repair hook from anEXIT/ signal trap.post-theme-setrestartsswaybgthrough/usr/sbin/daemon, not a PATH-dependent baredaemon.- Sway owns exactly one
lisgdthroughexec_always ~/.local/bin/start-lisgd-sway. Theme hooks must not daemonize a second gesture process;swaymsg reloadruns the session-owned wrapper, and the wrapper kills any stalelisgdimmediately beforeexec. - Sway owns
swaybarfrom itsbar {}block. The phone config disables the tray withtray_output none; manually launching replacement swaybars caused tray DBus failures and left the bar missing after theme changes. - Sway,
.profile, and lisgd-spawned launchers exportXDG_DATA_DIRS=$HOME/.local/share:/usr/local/share:/usr/shareso fuzzel sees package-installed.desktopfiles. fuzzeluses phone-sized geometry (width=34,lines=12,font size=14) with side gutters left open for edge gestures.lisgdaction strings do not embedDBUS_SESSION_BUS_ADDRESS; the process inherits it from Sway, and embedding the address breaks lisgd’s comma-delimited gesture parser because dbus addresses include,guid=....
Phase 2 — Communication (after modem + SIM working)
Install minicom and picocom; build the AT shell scripts above.
Phase 3 — Daily apps
ssh honor "pkg install -y netsurf pcmanfm nnn galculator calcurse \
newsboat sfeed feh nsxiv mupdf zathura zathura-pdf-mupdf mpv cmus \
password-store"
Phase 4 — Rich apps (after audio works)
ssh honor "pkg install -y musicpd ncmpcpp aerc foxtrotgps gpodder"
Phase 5 — Optional enhancements
ssh honor "pkg install -y gnome-calculator gnome-clocks gnome-weather \
geary keepassxc pidgin weechat profanity"
Remember GSK_RENDERER=cairo as the first fallback for GTK4/GNOME apps
that misbehave on the current GPU stack.
Sway media bindings on the phone
The live PinePhone Pro setup is Sway, and the working volume path is
FreeBSD mixer(8) plus a tiny wrapper that keeps a single transient OSD
on screen. The hardware side buttons arrive as KEY_VOLUMEUP /
KEY_VOLUMEDOWN from the local rk_adc_keys driver; Sway just binds those
to volume-osd.
bindsym XF86AudioRaiseVolume exec ~/.local/bin/volume-osd up
bindsym XF86AudioLowerVolume exec ~/.local/bin/volume-osd down
bindsym XF86AudioMute exec ~/.local/bin/volume-osd mute
exec_always ~/.local/bin/volume-osd init
Under the hood the helper drives mixer with FreeBSD’s current syntax:
mixer vol=+5%
mixer vol=-5%
mixer vol.mute=toggle
and sends a single replacing mako popup with a progress hint, so the UI
behaves like a volume meter instead of a notification stack. The mako
styling is generated from the active omfreebdy theme during post-theme-set,
so the volume OSD tracks the same accent/background colors as the rest of the
session instead of living in a hardcoded config file.
Sway screen power on the phone
The side power button now toggles panel power without locking or suspending
the device. libinput debug-events reports the GPIO key as KEY_POWER (116);
Sway receives it through XKB as XF86PowerOff, so omfreebdy installs this
managed block:
### OMFREEBDY_PHONE_SCREEN_START
bindsym --release XF86PowerOff exec ~/.local/bin/screen-toggle toggle
### OMFREEBDY_PHONE_SCREEN_END
The helper uses swaymsg output <active-output> power off|on on Sway and
Hyprland DPMS on Hyprland. On the PinePhone Pro this is intentionally only a
screen toggle; suspend and lock policy can be layered on later when the rest
of the power-management path is deterministic.
Brightness without desktop assumptions
The panel backlight is PWM-driven, not an ACPI laptop backlight. Do not
assume xbacklight or RandR brightness will exist. The right path is a
small omfreebdy helper over the kernel backlight/sysctl interface, then
bind it to Sway keys or a fuzzel menu.
Battery monitoring
The RK818 PMIC has an on-chip fuel gauge; the driver loads but doesn’t yet expose to userspace. Until it does, polling I²C registers directly is the workaround:
# RK818 on i2c0 addr 0x1c
# 0x21 = BAT_VOL_REGL, 0x22 = BAT_VOL_REGH
i2c -a 0x1c -d r -c 1 -o 0x21 ...
Convert voltage to percentage in your status-bar script.
Cross-references
- Essay 13 — Audio on device — why the RT5640 audio path is the gating dependency for music, podcasts, and call audio.
- The full document at
MOBILE_SOFTWARE.mdhas comparison tables for every app category that this summary collapses into one row each.