feat: start fullscreen no-WFB display at boot
Build Ground Station / build (push) Successful in 29s

This commit is contained in:
2026-09-30 17:58:31 +02:00
parent 91ab3eff37
commit d069754532
5 changed files with 96 additions and 1 deletions
+2 -1
View File
@@ -25,7 +25,8 @@ jobs:
go vet ./... go vet ./...
mkdir -p dist mkdir -p dist
GOOS=linux GOARCH=arm64 go build -trimpath -ldflags='-s -w' -o dist/groundstation-linux-arm64 ./cmd/groundstation GOOS=linux GOARCH=arm64 go build -trimpath -ldflags='-s -w' -o dist/groundstation-linux-arm64 ./cmd/groundstation
tar -C dist -czf dist/groundstation-linux-arm64.tar.gz groundstation-linux-arm64 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags='-s -w' -o dist/groundstation-display-linux-arm64 ./cmd/groundstation-display
tar -C dist -czf dist/groundstation-linux-arm64.tar.gz groundstation-linux-arm64 groundstation-display-linux-arm64
sha256sum dist/groundstation-linux-arm64.tar.gz > dist/groundstation-linux-arm64.tar.gz.sha256 sha256sum dist/groundstation-linux-arm64.tar.gz > dist/groundstation-linux-arm64.tar.gz.sha256
sha256sum dist/groundstation-linux-arm64 sha256sum dist/groundstation-linux-arm64
+71
View File
@@ -0,0 +1,71 @@
package main
import (
"fmt"
"log"
"os"
"os/exec"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
)
const (
width = 1920
height = 1080
)
func main() {
conn, err := xgb.NewConn()
if err != nil {
log.Fatal(err)
}
defer conn.Close()
screen := xproto.Setup(conn).DefaultScreen(conn)
window, err := xproto.NewWindowId(conn)
if err != nil {
log.Fatal(err)
}
values := uint32(xproto.CwBackPixel | xproto.CwEventMask)
xproto.CreateWindow(conn, screen.RootDepth, window, screen.Root, 0, 0, width, height, 0,
xproto.WindowClassInputOutput, screen.RootVisual, values,
[]uint32{screen.BlackPixel, xproto.EventMaskExposure})
xproto.MapWindow(conn, window)
conn.Sync()
// XFCE owns the actual fullscreen policy; use its existing wmctrl helper.
_ = exec.Command("wmctrl", "-i", "-r", fmt.Sprintf("0x%08x", window), "-b", "add,fullscreen").Run()
gc, err := xproto.NewGcontextId(conn)
if err != nil {
log.Fatal(err)
}
xproto.CreateGC(conn, gc, xproto.Drawable(window), xproto.GcForeground|xproto.GcBackground,
[]uint32{screen.WhitePixel, screen.BlackPixel})
font := xproto.Font(window)
xproto.OpenFont(conn, font, uint16(len("fixed")), "fixed")
xproto.ChangeGC(conn, gc, xproto.GcFont, []uint32{uint32(font)})
draw := func() {
xproto.ClearArea(conn, false, window, 0, 0, width, height)
message := "Brak polaczenia WFB"
xproto.ImageText8(conn, byte(len(message)), xproto.Drawable(window), gc, 32, 56, message)
conn.Sync()
}
draw()
for {
event, err := conn.WaitForEvent()
if err != nil {
if os.IsTimeout(err) {
continue
}
return
}
if _, ok := event.(xproto.ExposeEvent); ok {
draw()
}
}
}
+20
View File
@@ -8,6 +8,7 @@ import (
"io" "io"
"log" "log"
"os" "os"
"os/exec"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"syscall" "syscall"
@@ -37,6 +38,15 @@ func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop() defer stop()
if display, err := startDisplay(); err != nil {
log.Printf("display start deferred: %v", err)
} else {
defer func() {
_ = display.Process.Signal(os.Interrupt)
_ = display.Wait()
}()
}
var video *videoController var video *videoController
if cfg.Video.Enabled { if cfg.Video.Enabled {
video = newVideoController(cfg.Video.Command) video = newVideoController(cfg.Video.Command)
@@ -70,6 +80,16 @@ func main() {
} }
} }
func startDisplay() (*exec.Cmd, error) {
cmd := exec.Command("runuser", "-u", "orangepi", "--", "env",
"DISPLAY=:0", "XAUTHORITY=/home/orangepi/.Xauthority", "groundstation-display")
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
if err := cmd.Start(); err != nil {
return nil, err
}
return cmd, nil
}
func setupLogger(cfg config.Config) (*log.Logger, func(), error) { func setupLogger(cfg config.Config) (*log.Logger, func(), error) {
writers := []io.Writer{os.Stdout} writers := []io.Writer{os.Stdout}
closeLog := func() {} closeLog := func() {}
+1
View File
@@ -3,6 +3,7 @@ module git.piksar.eu/piotr/groundstation
go 1.24.0 go 1.24.0
require ( require (
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc
golang.org/x/sys v0.40.0 golang.org/x/sys v0.40.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
+2
View File
@@ -1,3 +1,5 @@
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc h1:7D+Bh06CRPCJO3gr2F7h1sriovOZ8BMhca2Rg85c2nk=
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=