Compare commits

..
22 Commits
Author SHA1 Message Date
StefanandClaude Opus 5 ab847cc46f S1: vorhandene Shells der Reihe nach durchprobieren
KWIN_COMPOSE=Q hat gegriffen — die Meldung 'Configured compositor not supported
by Platform' ist verschwunden, KWin compositet jetzt sauber in Software. Die
Grafik ist als Ursache damit ausgeschieden.

Bleibt, dass plasmashell die Shell verwirft. Statt weiter einen Namen zu raten,
werden die tatsächlich vorhandenen Shells nacheinander gestartet, bis eine ohne
'invalid corona' durchläuft. Das ist das brauchbare Abbruchkriterium: es sagt,
dass nichts gezeichnet wird, ohne dass man hinschauen muss.

Dabei die lange Optionsform --shell-plugin statt -p. Kurze Formen ändern sich
zwischen Plasma-Fassungen leichter, und eine unbekannte Kurzoption wird still
ignoriert statt bemängelt — genau der Verdacht.

Trägt keine Shell, wird das Ergebnis als Teilerfolg benannt statt als Fehlschlag:
der Compositor ist der schwierige Teil und bewiesen, die Shell der austauschbare.
Der doppelte Auswertungsblock nach der Schleife ist raus.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 23:02:20 +02:00
StefanandClaude Opus 5 a3a6d7216b S1: Software-Pfad auch dann setzen, wenn gar kein GL nachweisbar ist
Ein Fehler in meiner Verzweigung: schlugen beide GL-Prüfungen fehl, wurden
LIBGL_ALWAYS_SOFTWARE, KWIN_COMPOSE und QT_QUICK_BACKEND NICHT gesetzt —
ausgerechnet dann nicht, wenn sie am nötigsten sind. Der Software-Pfad wurde
dadurch nie ausprobiert.

Dazu ein Denkfehler dahinter: glxinfo taugt hier nicht als Richter. Unter
Termux-X11 scheitert es mit 'BadMatch bei X_GetImage', weil es Pixel zurücklesen
will — das sagt etwas über glxinfo, nichts über OpenGL. Die Prüfungen laufen
jetzt nur noch zur Information; im Zweifel wird immer der Software-Pfad gewählt.

Der virgl-Fehlschlag ist unterdessen als Zeitüberschreitung erkennbar
(Rückgabewert 124): der Client hängt, statt einen Fehler zu melden. Deshalb wird
jetzt vorab geprüft, ob der Socket im Container überhaupt sichtbar ist — das
trennt 'Server läuft nicht' von 'Container sieht ihn nicht'.

Vor Versuch A wird die tatsächlich gesetzte Umgebung ausgegeben, damit man nicht
raten muss, womit gestartet wurde. Beide Zweige durchgespielt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:57:47 +02:00
StefanandClaude Opus 5 1caaeedef1 S1: Reste vom Vorlauf selbst aufräumen, ohne sich dabei abzuschießen
Auf Wunsch räumt das Skript jetzt zu Beginn selbst auf — und zwar mehr als nur
kwin_wayland: auch plasmashell und xdg-desktop-portal bleiben hängen, dazu
verwaiste Wayland-Sockets. Bleiben die liegen, startet KWin auf wayland-1 statt
wayland-0, während plasmashell auf wayland-0 sucht.

Bewusst nicht mit 'pkill -f': das durchsucht die gesamte Kommandozeile und trifft
dabei auch den eigenen Aufruf oder eine Eltern-Shell, wenn das Muster darin
vorkommt. Beim Entwickeln dieses Blocks hat sich der Test zweimal selbst
abgeschossen — erst reichte 355477 nicht, dann auch 3136466 nicht, weil der Treffer
weiter oben in der Kette lag.

Jetzt wird die vollständige Ahnenkette über /proc/PID/stat gesammelt und
verschont. Geprüft: zwei Opfer beendet, ein Vorfahre verschont, Skript überlebt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:54:05 +02:00
StefanandClaude Opus 5 f3ef48fd10 S1: GL-Prüfung mit Rückgabewert, Software-Rendering als Rückfallebene
Die GPU-Prüfung lieferte auf dem Gerät nicht einmal eine Fehlermeldung — die
Ausgabe war leer. Ursache: stürzt glxinfo ab, kommt die Meldung von der Shell und
nicht vom Programm, und geht in einer Kommandoersetzung verloren. Ein Absturz sah
dadurch aus wie 'kein Treiber gefunden'. Jetzt wird der Rückgabewert ausgewertet
und ein Wert über 128 ausdrücklich als Absturz benannt. Gegen Erfolg, Absturz und
stillen Fehlschlag geprüft.

Davor steht jetzt ein xdpyinfo: ist schon der X-Server aus dem Container nicht
erreichbar, sagt jeder GL-Test darunter nichts aus.

Der wichtigere Punkt ist aber ein Denkfehler im Aufbau: S1 fragt, OB Plasma
Mobile in proot läuft — nicht, wie schnell. Die GPU zur Voraussetzung zu machen
verwechselt Machbarkeit mit Leistung. Schlägt virgl fehl, wird deshalb auf
Software-Rendering ausgewichen und weitergemacht: KWin mit QPainter-Compositor,
QtQuick mit Software-Renderer. Beide umgehen die GL-Ebene ganz und sind für eine
Oberfläche ohne Animationen brauchbarer als llvmpipe.

Damit lässt sich S1 auch dann beantworten, wenn die GPU-Anbindung noch klemmt —
und die wird zu dem, was sie ist: ein eigenes, nachgelagertes Problem.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:51:02 +02:00
StefanandClaude Opus 5 c2b602dcb4 S1: GPU im Container über virgl statt Zink — die eigentliche Ursache
Der Protokollauszug enthielt die entscheidende Zeile nicht bei der Shell, sondern
zwei Zeilen darunter:

    ! glxinfo lieferte nichts
    ! vulkaninfo lieferte nichts

Im Container gab es überhaupt kein OpenGL. Mein Denkfehler: Turnip liegt in
Termux' Verzeichnis, nicht im Debian-Rootfs. GALLIUM_DRIVER=zink im Container zu
setzen geht deshalb ins Leere — Zink bräuchte dort einen Vulkan-Treiber, und da
ist keiner.

Damit erklärt sich die ganze Kette: kein OpenGL, also kein QtQuick, also kann
plasmashell die MobileShell nicht laden und fällt wortlos auf die Voreinstellung
zurück. Das 'starting invalid corona' war das Symptom, nicht die Krankheit — und
der schwarze Bildschirm sah nach einem Compositor-Problem aus, obwohl KWin lief.

Richtig ist der Weg über virgl: der Server läuft in TERMUX und rendert dort über
Zink auf Turnip, der Container spricht nur virpipe mit ihm. Turnip bleibt also im
Spiel, nur eine Ebene tiefer als gedacht.

Der GPU-Nachweis im Container wertet jetzt aus statt nur zu drucken und sagt
ausdrücklich, dass ohne OpenGL die Shell nicht laden wird — damit dieselbe
Verwechslung nicht noch einmal Stunden kostet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:46:31 +02:00
StefanandClaude Opus 5 d4a6b59f81 S1: Rückfall der Shell erkennen, Empfehlungen mitinstallieren
Die Übergabe an plasmashell ist nachweislich korrekt — geprüft, dass
${SHELL_PAKET:+-p "$SHELL_PAKET"} zu drei getrennten Wörtern expandiert. Und
beide Shells liegen auf dem Gerät: org.kde.plasma.mobileshell und
org.kde.plasma.nano. Trotzdem meldet plasmashell die Voreinstellung
org.kde.plasma.desktop als 'invalid corona'.

Das ist kein Rate-, sondern ein Ladefehler: plasmashell bekam -p, konnte die
MobileShell nicht laden und fiel zurück. Verdacht ist --no-install-recommends —
bei KDE stecken die QML-Module traditionell in den Empfehlungen, nicht in den
harten Abhängigkeiten. Die Shell ist dann formal da, aber innen hohl. Für einen
Machbarkeitsbeweis sind ein paar hundert MB zu viel das kleinere Übel gegenüber
einem Fehlschlag aus dem falschen Grund.

Zusätzlich wertet das Skript den Rückfall jetzt selbst aus und unterscheidet die
beiden Fälle: meldet plasmashell die ÜBERGEBENE Shell als ungültig, liegt es an
ihr; meldet es die Voreinstellung, ist es ein Rückfall. Davon hängt ab, wo man
weitersucht — bisher sah beides gleich aus.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:44:11 +02:00
StefanandClaude Opus 5 3643cbd5cb S1: fehlende Plasma-Shell nachinstallieren statt nur melden
In Debian liefert ausgerechnet 'plasma-mobile' keine Shell mit — nur QML-Module
und KWin-Skripte. Die Shell steckt in 'plasma-nano' und heißt org.kde.plasma.nano.
Der aus den KDE-Quellen bekannte Name org.kde.plasma.mobileshell existiert in
Debian nicht.

Auf dem Gerät kam weiterhin 'starting invalid corona org.kde.plasma.desktop' —
das ist plasmashells Standardwert, wenn kein -p übergeben wird. Die Suche fand
also nichts: das Verzeichnis /usr/share/plasma/shells ist leer oder fehlt.

Das Skript gibt sich damit nicht mehr zufrieden, sondern installiert plasma-nano
nach und sucht erneut. Bleibt es dabei, wird der Prüfbefehl genannt und klar
gesagt, dass Versuch A ohne Shell zum Scheitern verurteilt ist — dann zeigt erst
die XFCE-Gegenprobe, ob X-Server und GPU tragen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:42:20 +02:00
StefanandClaude Opus 5 ec76b785b3 README: Rückfallebene dokumentieren, wenn git pull scheitert
Der Befehl stand bisher nur in den Antworten im Chat, nicht im Repo — genau die
Sorte Wissen, die verloren geht.

Aufgenommen mit dem Grund dahinter: 'fatal: early EOF' beim Klonen ist ein
serverseitiges Problem, upload-pack stirbt beim Schnüren des Pakets, während die
Objekte heil sind. Erkennbar daran, dass sich ein Archiv weiterhin erzeugen lässt
— und genau darauf weicht init-phone.sh aus.

Ausdrücklich dabei: das Ergebnis ist ein Abzug ohne .git. 'git pull' geht damit
nicht mehr, und derselbe Befehl stellt später auch die Arbeitskopie wieder her,
sobald der Server sich erholt hat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:37:31 +02:00
StefanandClaude Opus 5 985f48177d S1: Plasma-Shell nachsehen statt raten, Beurteilungsfrage gestaffelt
Aus dem Gerätelauf, entscheidende Zeile:

    starting invalid corona "org.kde.plasma.desktop"

Der Paketname der Mobile-Shell hat sich mit Plasma 6 geändert: aus
org.kde.plasma.phoneshell wurde org.kde.plasma.mobileshell. Der falsche Name
fällt nicht auf — plasmashell startet klaglos, meldet nur beiläufig 'invalid
corona' und zeichnet dann nichts. Von außen sieht das aus wie ein Fehlschlag von
KWin, ist aber keiner: der genestete Compositor lief.

Statt einen Namen fest einzutragen, wird jetzt nachgesehen, welche Shells unter
/usr/share/plasma/shells/ tatsächlich liegen, und in der Reihenfolge
mobileshell, phoneshell, nano, desktop gewählt. Die gefundene Liste wird
protokolliert — beim nächsten Umbenennen sieht man sofort, woran es liegt.

Dazu XCURSOR_THEME gesetzt: ohne das meldet KWin 'Failed to load cursor theme'
und zeichnet gar keinen Zeiger.

Die Beurteilungsfrage fragte nach der Bildschirmtastatur in einem Eingabefeld —
in dem Zustand, in dem sie gestellt wird, gibt es aber gar keine Felder. Sie ist
jetzt nach dem gestaffelt, was tatsächlich zu sehen ist, mit dem X-Cursor auf
schwarzem Grund als eigener Zwischenstand: X11 verbunden, aber nichts zeichnet
darauf. Genau dieser Fall lag hier vor und hatte vorher keine passende Antwort.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:35:55 +02:00
StefanandClaude Opus 5 bafa2f3dd1 S1: Termux:X11 am Start prüfen statt an der Paketliste
Die eingebaute Prüfung schlug fehl, obwohl die App installiert ist. Ursache:
seit Android 11 sieht eine App nur noch die Pakete, die sie im Manifest anmeldet
oder die ihre UID teilen. 'pm list packages' liefert aus Termux heraus eine
gefilterte Liste — ein Nichtfinden beweist also gar nichts, und ein Abbruch
darauf ist schlicht falsch.

Die Paketprüfung bleibt als Hinweis erhalten, blockiert aber nicht mehr.
Verlässlich ist stattdessen der Startversuch selbst: 'am start' meldet einen
Fehler, wenn es die Aktivität nicht gibt. Das wird jetzt ausgewertet.

Dazu der Hinweis, dass 'not connected' in der App bis zu diesem Zeitpunkt der
normale Zustand ist — die App wartet auf den X-Server. Bleibt es danach stehen,
ist es ein echtes Signal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:12:30 +02:00
StefanandClaude Opus 5 0e301f2455 S1: prüfen, ob die Termux:X11-APP installiert ist
Das Termux-Paket termux-x11-nightly und die Termux:X11-App sind zwei verschiedene
Dinge. Fehlt die App, startet der X-Server anstandslos — nur bleibt der Bildschirm
leer. Das sähe aus wie ein Fehlschlag von Plasma Mobile und wäre keiner, und genau
solche Fehldiagnosen soll der Spike nicht produzieren.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:07:02 +02:00
StefanandClaude Opus 5 95de7b6614 proot-distro 5.5.0: 'already exists' als unkritisch erkennen
Das Gerät läuft proot-distro 5.5.0. Dort gibt es '--installed' nicht mehr, und
die Meldung lautet "container 'debian' already exists" statt 'already installed'
— mein Mustervergleich hätte sie nicht erkannt und wäre in den Fehlerzweig
gelaufen, obwohl alles in Ordnung ist.

Beide Formulierungen werden jetzt abgedeckt. Gegen die tatsächliche Meldung vom
Gerät geprüft.

Die Hinweise zum Nachbessern nennen zusätzlich 'proot-distro reset', das in 5.5.0
der vorgesehene Weg zum Neuaufsetzen ist — mit dem ausdrücklichen Vermerk, dass
dabei die Daten verloren gehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:04:15 +02:00
StefanandClaude Opus 5 f20ad44e25 Rootfs am Anmeldeversuch erkennen, nicht an Pfaden
Der zweite Bootstrap-Lauf brach ab, obwohl beim ersten alles stand: proot-distro
hat sein Ablageformat gewechselt — sichtbar an 'Migrating legacy container' —
und danach lag das Rootfs nicht mehr dort, wo meine Prüfung nachsah. Sie meldete
'nicht installiert', die folgende Installation scheiterte an 'already installed',
und der Lauf brach ab. Ein Fehler in der Erkennung, nicht auf dem Gerät.

Geprüft wird jetzt mit 'proot-distro login <distro> -- true'. Das fragt nicht, wo
etwas liegt oder wie eine Ausgabe formatiert ist, sondern ob es funktioniert —
und überlebt damit auch den nächsten Formatwechsel.

Zusätzlich gilt 'already installed' nicht mehr als Fehler: entscheidend ist
allein, ob die Anmeldung danach klappt. Tut sie es nicht, nennt das Skript die
Handgriffe zum Neuaufsetzen, statt nur abzubrechen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:03:26 +02:00
StefanandClaude Opus 5 10851df46a Spike-Protokoll: Gerätefakten und Bootstrap-Ergebnis eingetragen
Bootstrap auf dem OnePlus 7 Pro bestanden. Turnip ist nachweislich aktiv —
vulkaninfo meldet 'Turnip Adreno (TM) 640', also nicht nur installiert, sondern
tatsächlich der genutzte Treiber. Damit steht für S1 der schnellste GPU-Pfad zur
Verfügung, ohne virgl-Proxy.

Beide Stolpersteine sind mit dokumentiert, auch der unschöne: proot-distro
verlangt python3.13, installiert ist 3.14, und weder Nachinstallieren noch
Vollupgrade beheben das — die Paketquelle liefert einen unstimmigen Satz. Der
Verweis python3.13 auf 3.14 ist ein Behelf und steht als solcher im Protokoll,
samt Hinweis, dass er beim nächsten Update von proot-distro wieder aufbrechen kann.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:00:25 +02:00
StefanandClaude Opus 5 ab4432cf84 Dateikonflikte beim Upgrade gezielt auflösen
Auf dem Gerät scheiterte das Upgrade an libmagic:

  trying to overwrite '.../include/magic.h', which is also in package file 5.47

Termux hat libmagic aus dem Paket file herausgelöst. Beide bringen dieselbe Datei
mit, und dpkg verweigert grundsätzlich, wenn zwei Pakete um eine Datei streiten.
Das ist kein Defekt, sondern der normale Zwischenzustand einer Paketaufteilung —
und er blockiert danach jede weitere Installation.

Beide Skripte erkennen den Konflikt jetzt an der Meldung und wiederholen mit
--force-overwrite. Bewusst erst dann und nicht von vornherein: pauschal
mitgegeben würde die Option jede künftige Dateikollision stillschweigend
übergehen, auch eine, die auf ein echtes Problem hinweist. Die betroffene Zeile
wird vorher angezeigt, damit nachvollziehbar bleibt, was übergangen wird.

Gegen die tatsächliche Ausgabe vom Gerät geprüft: Konflikt wird erkannt, und nach
dem Rauschfilter bleiben genau die zwei Zeilen übrig, auf die es ankommt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 21:52:20 +02:00
StefanandClaude Opus 5 7d0063278c Upgrade-Abbrüche abfangen und das Rauschen filtern
Beim Wechsel der Python-Version wirft dpkg dutzende Zeilen 'unable to delete old
directory ... Directory not empty'. Das sind Warnungen — in site-packages liegen
noch .pyc-Dateien oder selbst installierte Module. Sie verdecken aber die eine
Zeile, auf die es ankommt, und werden deshalb gefiltert.

Bricht das Upgrade ab, steckt dpkg meist halb fertig fest und blockiert jede
weitere Installation. Beide Skripte versuchen jetzt 'dpkg --configure -a' und
'apt-get -f install', dann das Upgrade erneut, bevor sie aufgeben. Scheitert es
weiterhin, nennen sie den Befehl, der den echten Fehler ohne Rauschen zeigt.

Dazu eine Platzprüfung im Bootstrap gegen $PREFIX statt gegen /. In Termux zeigt
'df -h' als / die Systempartition mit oft nur zweistelligen MB — die aber nichts
mit dem Platz zu tun hat, der Termux zur Verfügung steht. Wer danach geht,
diagnostiziert am falschen Datenträger.

Der Vulkan-Treiber ist geklärt: mesa-vulkan-icd-freedreno ist verfügbar und steht
in der Kandidatenliste bereits an erster Stelle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 21:50:18 +02:00
StefanandClaude Opus 5 488851101e Vollupgrade vor alle Paketinstallationen ziehen
Termux verträgt keine Teilupgrades: die Pakete werden gegeneinander gebaut, und
ein einzeln nachinstalliertes trifft sonst auf veraltete Abhängigkeiten. Genau
daran ist proot-distro gescheitert — es trug die Shebang-Zeile python3.13,
während eine andere Fassung installiert war.

'pkg update && pkg upgrade -y' läuft deshalb jetzt an beiden Einstiegspunkten,
jeweils vor der ersten Einzelinstallation: in init-phone.sh gleich zu Beginn, und
in 00-bootstrap-termux.sh für alle, die ohne init-phone einsteigen.

--force-confold, damit die Rückfrage nach Konfigurationsdateien den Lauf nicht
mitten in der Nacht anhält. Überspringbar mit HPOS_OHNE_UPGRADE=1 für knappes
Datenvolumen; ein Fehlschlag bricht nicht ab, wird aber deutlich als erste
Stelle zum Nachsehen benannt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 21:48:04 +02:00
StefanandClaude Opus 5 603ad38fc3 Bootstrap: proot-distro-Reparatur und Vulkan-Treiber mit Alternativen
Aus dem Gerätelauf: proot-distro trägt die Shebang-Zeile python3.13, und dieser
Interpreter fehlt. proot-distro ist inzwischen ein Python-Programm — der
klassische Termux-Teilupgrade-Fehler, bei dem ein Paket gegen eine Fassung gebaut
wurde und eine andere installiert ist.

Die Reparatur läuft in drei Stufen, von der saubersten zur unsaubersten:
python nachziehen, dann Vollupgrade (die eigentliche Lösung, denn Termux verträgt
keine Teilupgrades), und erst als Notnagel ein Verweis der fehlenden auf eine
vorhandene Python-Fassung. Der Notnagel gilt ausdrücklich nur für Python und nur
weil proot-distro reines Python ohne kompilierte Erweiterungen ist; bei anderen
Interpretern wäre das grob fahrlässig. Bleibt es defekt, nennt das Skript die
Handgriffe einzeln, statt nur aufzugeben.

Ebenfalls aus dem Lauf: mesa-vulkan-icd-wrapper gibt es unter dem Namen nicht
mehr. Die Paketnamen haben in Termux mehrfach gewechselt, deshalb werden jetzt
mehrere Kandidaten der Reihe nach probiert. Greift keiner, wird das klar benannt:
ohne Vulkan-Treiber bleibt Software-Rendering, S1 wird träge, ist aber weiterhin
aussagekräftig.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 21:42:52 +02:00
StefanandClaude Opus 5 3c41623e52 install-scrcpy.sh statt apt — in Debian 12 gibt es das Paket nicht
Meine Anweisung 'sudo apt install scrcpy' war falsch: scrcpy ist in bookworm gar
nicht enthalten, und wo es existiert (ab trixie-backports) liegt es in contrib
statt main. 'Paket kann nicht gefunden werden' war also die richtige Antwort des
Systems, kein Konfigurationsfehler.

Das Projekt liefert ein eigenständiges Linux-Paket. Gegen das System geprüft:
keine fehlenden Bibliotheken, läuft ohne root. Installiert wird nach ~/.local,
Rückbau mit --deinstallieren.

Wichtiger Teil ist der erzeugte Starter: das Paket bringt ein eigenes adb mit,
hier 37.0.0 gegen 36.0.0 im System. Trifft ein abweichendes adb auf einen
laufenden Server, beendet es ihn — und kappt damit mitten in der Arbeit die
WLAN-Verbindung zum Telefon. Der Starter schiebt scrcpy deshalb dasselbe adb
unter, das auch die Provisioning-Skripte benutzen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 21:33:10 +02:00
StefanandClaude Opus 5 14f054c8e3 Entwicklerzugang: SSH nach Termux, sshfs, scrcpy
Auf der Bildschirmtastatur eines Telefons lässt sich nicht arbeiten. Zwei Wege,
die sich ergänzen: SSH für alles Textliche, scrcpy für alles, was man sehen muss —
Beweis S1 zeigt sich nur auf dem Bildschirm, die Kamera in S3 prüft man im Browser.

tools/dev-ssh.sh richtet Termux' sshd ein (Port 8022, weil eine gewöhnliche App
keine Ports unter 1024 belegen darf). Schlüssel vor Passwort: ein Passwort auf der
Bildschirmtastatur einzutippen ist mühsam, und wer es sich deshalb kurz wählt, hat
einen offenen Dienst im WLAN. provision.sh legt den öffentlichen Schlüssel des
Rechners deshalb gleich mit ab.

Über sshfs lassen sich die Dateien des Telefons im Editor des Rechners öffnen —
kein Hin- und Herkopieren mehr.

docs/entwicklung.md beschreibt beides samt Richtung der Änderungen: entwickelt
wird auf dem Rechner, das Telefon führt nur aus und holt per git pull. Genau
deshalb kommt per adb auch nur das Startskript aufs Gerät.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 21:09:41 +02:00
StefanandClaude Opus 5 2d3c2fbc6d Vorabprüfung: proot-distro und Rootfs vor jedem Beweis
Aus dem Gerätelauf: S2 lief bis Stufe 1 durch und scheiterte dann an
'proot-distro: cannot execute: required file not found'. Danach meldete die
Auswertung folgerichtig 'S2 gescheitert' — was den Beweis inhaltlich belastet,
obwohl nur die Grundlage fehlte. Genau diese Verwechslung soll der Spike nicht
produzieren.

Alle Beweise prüfen jetzt vorab, ob proot-distro ausführbar ist und das Rootfs
steht, und brechen sonst mit klarer Ansage ab statt mitten im Test aufzulaufen.

Die Meldung selbst ist irreführend: 'required file not found' heißt nicht, dass
proot-distro fehlt — bash hat es gefunden und nennt den vollen Pfad. Es fehlt der
Interpreter aus dessen Shebang-Zeile. Die Prüfung sagt das ausdrücklich und nennt
Datei und Shebang, statt den Suchenden an der falschen Stelle graben zu lassen.
00-bootstrap versucht in dem Fall selbsttätig eine Neuinstallation.

Dazu die Berechtigungsprüfung entschärft: dumpsys verlangt die DUMP-Berechtigung,
die eine gewöhnliche App nicht hat. Das Fehlschlagen ist erwartbar und kein
Warnsignal; die Meldung nennt jetzt den Weg über adb statt Ratlosigkeit zu säen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 18:27:40 +02:00
StefanandClaude Opus 5 69802cde6b init-phone: Archiv als Rückfallebene, wenn der Klon am Server scheitert
Auf dem Gerät schlug 'git clone' mit 'early EOF' und 'invalid index-pack output'
fehl. Die Eingrenzung zeigt: es liegt weder am Telefon noch am Skript.

  · Derselbe Fehler tritt vom PC aus auf, mit jeder Variante — flach, Protokoll
    v1, ohne Kompression, ein Thread, großer postBuffer.
  · Die Refs liefert der Server anstandslos.
  · Auf eine korrekt aufgebaute upload-pack-Anfrage antwortet er mit 'NAK' und
    dann nichts mehr: 8 Bytes, keine Paketdaten, nicht einmal eine Meldung im
    Fehlerkanal. Genau das sieht der Client als vorzeitiges Dateiende.
  · Ein Archiv derselben Revision lässt sich dagegen fehlerfrei erzeugen.

Die Objekte sind also heil und vollständig; allein das Schnüren des Pakets
scheitert. Das ist serverseitig und von hier weder zu beheben noch zu umgehen.

Damit das Gerät nicht blockiert bleibt, holt init-phone.sh die Quellen im
Fehlerfall über den Archiv-Endpunkt. Der geht nicht über upload-pack und
funktioniert deshalb weiterhin. Das Ergebnis ist ausdrücklich ein Abzug, keine
Arbeitskopie: 'git pull' geht damit nicht, und das Skript sagt das auch so, statt
den Unterschied zu verwischen. Der Weg zurück zur Arbeitskopie steht daneben.

Gegen den echten Server getestet: Archiv gültig, 26 Dateien richtig entpackt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 17:11:19 +02:00
14 changed files with 1120 additions and 65 deletions
+28
View File
@@ -79,6 +79,32 @@ Dadurch liegt dort eine echte Arbeitskopie — spätere Änderungen brauchen kei
cd ~/hpos && git pull
```
**Wenn `git pull` scheitert:**
```bash
rm -rf ~/hpos && bash /sdcard/hpos-init.sh
```
Das Startskript versucht zuerst wieder einen Klon und weicht, falls der Server keine
Pakete ausliefert, auf den Archiv-Endpunkt aus (`/archive/main.tar.gz`). Der geht
nicht über `git upload-pack` und funktioniert deshalb auch dann noch.
Das Ergebnis ist dann allerdings **ein Abzug, keine Arbeitskopie**`git pull` geht
damit nicht mehr, jede Aktualisierung braucht denselben Befehl erneut. Sobald der
Server wieder Pakete liefert, stellt genau dieser Befehl auch die Arbeitskopie
wieder her.
Typische Meldung, wenn es so weit ist:
```
fatal: early EOF
fatal: fetch-pack: invalid index-pack output
```
Das ist ein serverseitiges Problem — `upload-pack` stirbt beim Schnüren des Pakets,
während die Objekte selbst heil sind. Erkennbar daran, dass sich ein Archiv weiterhin
erzeugen lässt.
Details, Stellschrauben und was bei Fehlschlägen zu tun ist:
[`spike/README.md`](spike/README.md).
@@ -102,6 +128,8 @@ und „war morgens weg".
- [Verworfene Entscheidungen](docs/verworfene-entscheidungen.md) — sieben Wege, die
geprüft und begründet verworfen wurden. **Vor jedem "warum eigentlich nicht …" lesen.**
- [Spike-Protokoll](docs/spike-protokoll.md) — Messergebnisse von Phase 0
- [Am Projekt arbeiten](docs/entwicklung.md) — SSH nach Termux, scrcpy, sshfs.
**Nicht auf der Bildschirmtastatur entwickeln.**
## Geräte
+102
View File
@@ -0,0 +1,102 @@
# Am Projekt arbeiten
Die Bildschirmtastatur eines Telefons ist zum Entwickeln unbrauchbar. Zwei Werkzeuge
lösen das, und sie ergänzen sich: **SSH** für alles Textliche, **scrcpy** für alles,
was man sehen muss.
## SSH nach Termux — für Terminalarbeit
Der wichtigere der beiden Wege. Echte Tastatur, Zwischenablage, Verlauf, Rollbalken.
```bash
# Einmalig auf dem PC (provision.sh bietet es von selbst an)
adb push ~/.ssh/id_ed25519.pub /sdcard/hpos-id.pub
# Auf dem Telefon
bash ~/hpos/tools/dev-ssh.sh
# Danach vom PC
ssh -p 8022 <benutzer>@<telefon-ip>
```
Port 8022 statt 22, weil eine gewöhnliche App keine Ports unter 1024 belegen darf.
**Dateien im Editor des Rechners bearbeiten** — kein Hin- und Herkopieren:
```bash
mkdir -p ~/telefon
sshfs -p 8022 <benutzer>@<ip>:/data/data/com.termux/files/home ~/telefon
# lösen mit: fusermount3 -u ~/telefon
```
`sshd` überlebt keinen Neustart von Termux. Danach genügt `sshd` in Termux.
In fremden Netzen abschalten: `pkill sshd` — der Dienst ist im ganzen WLAN erreichbar.
## scrcpy — Bildschirm und Zwischenablage
Für alles Grafische unverzichtbar: Beweis S1 zeigt sich nur auf dem Bildschirm, und
die Kamera in S3 prüft man in einem Browserfenster.
```bash
./tools/install-scrcpy.sh # holt das offizielle Linux-Paket nach ~/.local
scrcpy # nutzt die bestehende adb-Verbindung, auch über WLAN
```
**`apt install scrcpy` funktioniert auf Debian 12 nicht** — dort ist das Paket gar
nicht enthalten, und ab trixie-backports liegt es in `contrib` statt `main`. Das
Projekt liefert aber ein eigenständiges Linux-Paket ohne Systemabhängigkeiten, und
genau das holt das Skript. Kein root, alles unter `~/.local`, Rückbau mit
`--deinstallieren`.
Das Skript legt dabei einen Starter an, der scrcpy **dasselbe adb unterschiebt**, das
auch die Provisioning-Skripte benutzen. Das ist kein Schönheitsfehler: das Paket
bringt eine eigene, oft neuere adb-Fassung mit, und ein abweichendes adb beendet einen
laufenden Server — was mitten in der Arbeit die WLAN-Verbindung zum Telefon kappen
würde.
Was es mitbringt, ohne weitere Einrichtung:
- **Zwischenablage in beide Richtungen.** Auf dem Telefon kopieren, auf dem PC
einfügen und umgekehrt.
- **Tastatur und Maus des Rechners** wirken direkt im Telefon — auch in Termux.
- Läuft über dieselbe WLAN-Verbindung, die schon steht.
Nützliche Schalter:
| Schalter | Wofür |
|---|---|
| `--max-size 1024` | weniger Bandbreite, flüssiger über WLAN |
| `--stay-awake` | Bildschirm bleibt an, solange gespiegelt wird |
| `--turn-screen-off` | Telefonbildschirm aus, spart Akku beim langen Arbeiten |
| `--no-audio` | falls die Tonübertragung stört |
`scrcpy --new-display` kann eine App in ein eigenes virtuelles Display legen. Als
Entwicklerwerkzeug ist das nützlich — als Produktbestandteil wurde es
[verworfen](verworfene-entscheidungen.md#v6--host-apps-als-fenster-im-linux-desktop),
weil es dauerhaft Shell-Rechte bräuchte.
## Der Weg der Änderungen
```
PC (Arbeitskopie) --git push--> Forge --git pull--> Telefon (~/hpos)
```
Auf dem Telefon wird nicht entwickelt, dort wird ausgeführt. Änderungen entstehen auf
dem Rechner, werden gepusht und mit `cd ~/hpos && git pull` geholt. Deshalb kommt per
adb auch nur das Startskript aufs Gerät und nicht das Repo — sonst wäre es eine Kopie
statt einer Arbeitskopie.
Ergebnisse laufen in die Gegenrichtung: `spike/out/` bleibt lokal auf dem Telefon
(bewusst nicht im Repo), die Auswertung wandert von Hand nach
[`spike-protokoll.md`](spike-protokoll.md).
## Kleinigkeiten, die Zeit sparen
- **Protokolle direkt vom Telefon holen**, ohne SSH:
`adb shell run-as com.termux cat …` scheitert meist an fehlender Debuggability.
Einfacher ist der Umweg über die gemeinsame Ablage:
`cp ~/hpos/spike/out/*.log /sdcard/` und dann `adb pull /sdcard/…`
- **Termux offen halten**: Ein Wisch aus den zuletzt verwendeten Apps beendet den
gesamten Stack. Im Task-Manager anpinnen.
- **Mehrere Sitzungen** in Termux mit `Ctrl+Alt+C` — praktisch, um neben einem
laufenden Desktop noch ein Terminal zu haben.
+20 -5
View File
@@ -10,14 +10,29 @@ Fehlschlag ist wertvoller als ein geschöntes Ergebnis.
| | |
|---|---|
| Modell | OnePlus 7 Pro (`guacamole`) |
| SoC / GPU | Snapdragon 855 / Adreno 640 |
| RAM / Speicher | _(ausfüllen)_ |
| ROM | Stock OxygenOS 12 (Android 12) |
| Modell | OnePlus 7 Pro `GM1913` / `OnePlus7Pro` |
| SoC | Snapdragon 855 (`SM8150`, Plattform `msmnile`) |
| GPU | Adreno 640 — **Turnip aktiv**, laut `vulkaninfo`: `Turnip Adreno (TM) 640` |
| RAM | 11 GiB |
| Freier Speicher | ~19,9 GB unter `$PREFIX` |
| ROM | Stock OxygenOS 12 (Android 12, SDK 31) |
| Kernel | 4.14.180-perf+ |
| Rootfs | Debian 13.3 (Trixie) über proot-distro |
| Display | 3120 × 1440, virtuelle Auflösung im Test: _(ausfüllen)_ |
| Akku | 4000 mAh, Baujahr 2019 — getauscht? _(ausfüllen)_ |
| Datum des Tests | _(ausfüllen)_ |
**Bootstrap: bestanden.** Zwei Stolpersteine unterwegs, beide gelöst und im Skript
abgefangen:
- `libmagic` gegen `file 5.47`: Dateikonflikt aus einer Paketaufteilung, aufgelöst
mit `--force-overwrite`.
- `proot-distro` verlangt `python3.13`, installiert ist `python3.14`. Weder
Nachinstallieren noch Vollupgrade haben das behoben — die Paketquelle liefert
hier einen unstimmigen Satz. Behelf: Verweis `python3.13 → python3.14`.
**Vertretbar, weil proot-distro reines Python ist, aber nicht sauber** — beim
nächsten Update von proot-distro kann es erneut auftreten.
## Zusammenfassung
| Beweis | Ergebnis | Konsequenz |
@@ -132,7 +147,7 @@ basiert auf ColorOS mit aggressiver App-Einfrierung unterhalb der Android-Mechan
|---|---|
| `vkcube` läuft | |
| `glmark2` Score | |
| GPU-Treiberpfad (Turnip / Zink / virgl / Software) | |
| GPU-Treiberpfad (Turnip / Zink / virgl / Software) | **Turnip** — bestätigt im Bootstrap |
| Virtuelle Auflösung | |
| Akku: Leerlauf mit laufendem Stack | |
| Akku: Desktop aktiv | |
+120 -4
View File
@@ -46,12 +46,85 @@ kopf "HPOS — Ersteinrichtung auf dem Telefon"
hinweis "Repo: $REPO"
[ "$ANONYM" = 1 ] && hinweis "(Benutzername entfernt — gelesen wird anonym)"
# ── Vollupgrade ──────────────────────────────────────────────────────────────
# Muss VOR jedem anderen Paket laufen. Termux verträgt keine Teilupgrades: die
# Pakete werden gegeneinander gebaut, und ein einzeln nachinstalliertes trifft
# sonst auf veraltete Abhängigkeiten. Genau daran ist proot-distro gescheitert —
# es trug die Shebang-Zeile python3.13, während eine andere Fassung installiert war.
#
# Überspringbar mit HPOS_OHNE_UPGRADE=1, etwa bei knappem Datenvolumen.
kopf "Termux auf einen Stand bringen"
if [ "${HPOS_OHNE_UPGRADE:-0}" = 1 ]; then
warn "Übersprungen (HPOS_OHNE_UPGRADE=1)"
hinweis "Bei Problemen mit proot-distro ist das die erste Stelle zum Nachsehen."
else
hinweis "Das dauert und lädt einiges. Es erspart aber genau die Fehler,"
hinweis "die sonst erst Stunden später als kaputtes proot-distro auffallen."
export DEBIAN_FRONTEND=noninteractive
# Beim Wechsel der Python-Version wirft dpkg dutzende Zeilen
# "unable to delete old directory ... Directory not empty". Das sind
# Warnungen, keine Fehler: in site-packages liegen noch .pyc-Dateien oder
# selbst installierte Module. Sie herauszufiltern ist kein Verstecken —
# sie verdecken sonst die eine Zeile, auf die es ankommt.
rauschfilter() { grep -viE 'unable to delete old directory|^(Unpacking|Preparing|Selecting) '; }
# --force-confold: vorhandene Konfiguration behalten, statt mitten im Lauf
# eine Rückfrage zu stellen, die niemand beantwortet.
AUSGABE=$(mktemp)
pkg update -y >"$AUSGABE" 2>&1; rauschfilter <"$AUSGABE"
upgrade_lauf() {
pkg upgrade -y -o Dpkg::Options::=--force-confold "$@" >"$AUSGABE" 2>&1
local rc=$?
rauschfilter <"$AUSGABE"
return $rc
}
if upgrade_lauf; then
ok "Termux ist aktuell"
else
# Dateikonflikt gezielt behandeln. Er entsteht, wenn ein Paket aufgeteilt
# wurde und beide Teile kurzzeitig dieselbe Datei mitbringen — bei Termux
# etwa libmagic, herausgelöst aus file. dpkg verweigert dann grundsätzlich.
#
# --force-overwrite wird bewusst NUR hier gesetzt, nachdem der Konflikt in
# der Ausgabe nachgewiesen ist. Pauschal mitzugeben hieße, jede künftige
# Dateikollision stillschweigend zu übergehen.
if grep -q 'which is also in package' "$AUSGABE"; then
warn "Dateikonflikt zwischen zwei Paketen — typisch bei einer Paketaufteilung."
grep 'trying to overwrite' "$AUSGABE" | head -3 | sed 's/^/ /'
hinweis "Wiederhole gezielt mit --force-overwrite."
if upgrade_lauf -o Dpkg::Options::=--force-overwrite; then
ok "Konflikt aufgelöst, Upgrade durchgelaufen"
rm -f "$AUSGABE"
AUSGABE=""
fi
fi
if [ -n "$AUSGABE" ] && ! upgrade_lauf; then
warn "Upgrade weiterhin unvollständig — versuche dpkg zu entwirren."
# Nach einem Abbruch steckt dpkg halb fertig fest und blockiert alles.
dpkg --configure -a >>"$AUSGABE" 2>&1
apt-get -f install -y >>"$AUSGABE" 2>&1
if upgrade_lauf -o Dpkg::Options::=--force-overwrite; then
ok "Nach der Reparatur durchgelaufen"
else
warn "Bleibt unvollständig."
hinweis "Speicherplatz prüfen: df -h \$PREFIX (nicht / — das ist die Systempartition)"
hinweis "Fehler ohne Rauschen: pkg upgrade -y 2>&1 | grep -viE 'unable to delete' | tail -20"
hinweis "Weiter geht es trotzdem — aber hier ansetzen, falls später etwas klemmt."
fi
fi
fi
rm -f "$AUSGABE" 2>/dev/null || true
fi
# ── git ──────────────────────────────────────────────────────────────────────
kopf "git"
if command -v git >/dev/null 2>&1; then
ok "bereits vorhanden ($(git --version))"
else
pkg update -y >/dev/null 2>&1
if pkg install -y git; then ok "installiert"
else fehl "Installation von git fehlgeschlagen"; exit 1; fi
fi
@@ -60,6 +133,37 @@ fi
# ein Push von hier wäre weder gewollt noch möglich.
git config --global credential.helper "" 2>/dev/null || true
# ── Rückfallebene: Archiv statt Klon ─────────────────────────────────────────
# Gitea, Forgejo und GitHub bieten den Baum zusätzlich als Tarball an. Der Weg
# dorthin geht nicht über upload-pack und funktioniert deshalb auch dann noch,
# wenn der Klon am Server scheitert. Das Ergebnis ist aber nur ein Abzug: kein
# .git, kein 'git pull'. Es ist eine Notlösung, keine Alternative.
archiv_holen() {
local basis zweig="${HPOS_ZWEIG:-main}" url tmp
basis="${REPO%.git}"
command -v curl >/dev/null 2>&1 || pkg install -y curl >/dev/null 2>&1
command -v tar >/dev/null 2>&1 || pkg install -y tar >/dev/null 2>&1
tmp=$(mktemp -d)
for url in "$basis/archive/$zweig.tar.gz" \
"$basis/archive/refs/heads/$zweig.tar.gz"; do
hinweis "Versuche Archiv: $url"
if curl -sfL -o "$tmp/a.tar.gz" "$url" && tar tzf "$tmp/a.tar.gz" >/dev/null 2>&1; then
mkdir -p "$ZIEL"
# --strip-components=1: die Archive enthalten alles unter einem
# Wurzelverzeichnis, das wir nicht mit übernehmen wollen.
if tar xzf "$tmp/a.tar.gz" -C "$ZIEL" --strip-components=1; then
rm -rf "$tmp"
ok "Archiv entpackt nach $ZIEL"
return 0
fi
fi
done
rm -rf "$tmp"
return 1
}
# ── Holen oder aktualisieren ─────────────────────────────────────────────────
kopf "Repo"
if [ -d "$ZIEL/.git" ]; then
@@ -79,9 +183,21 @@ else
else
fehl "git clone fehlgeschlagen."
hinweis "Adresse war: $REPO"
hinweis "Ist das Repo öffentlich lesbar? Ohne das braucht es Zugangsdaten,"
hinweis "und die gehören nicht aufs Telefon."
exit 1
echo
# Ein scheiternder Klon bei erreichbarem Repo hat fast immer eine
# serverseitige Ursache: upload-pack stirbt beim Schnüren des Pakets,
# während die Objekte selbst heil sind. Erkennbar daran, dass sich ein
# Archiv weiterhin erzeugen lässt — und genau das ist die Rückfallebene.
if archiv_holen; then
warn "Über ein Archiv geholt, NICHT als Arbeitskopie."
hinweis "'git pull' funktioniert damit nicht. Sobald der Server wieder"
hinweis "Pakete ausliefert: rm -rf ~/hpos && bash /sdcard/hpos-init.sh"
else
fehl "Auch die Rückfallebene über das Archiv hat nicht geklappt."
hinweis "Ist das Repo öffentlich lesbar? Ohne das braucht es Zugangsdaten,"
hinweis "und die gehören nicht aufs Telefon."
exit 1
fi
fi
fi
+26
View File
@@ -317,6 +317,32 @@ else
esac
fi
# ── Entwicklerzugang ─────────────────────────────────────────────────────────
# Öffentlichen Schlüssel gleich mitschicken, damit später kein Passwort auf der
# Bildschirmtastatur eingetippt werden muss.
if [ "$TROCKEN" != 1 ]; then
PUBKEY=""
for k in ~/.ssh/id_ed25519.pub ~/.ssh/id_rsa.pub; do
[ -r "$k" ] && { PUBKEY="$k"; break; }
done
if [ -n "$PUBKEY" ]; then
kopf "Entwicklerzugang"
hinweis "Für SSH nach Termux — echte Tastatur statt Bildschirmtastatur."
printf ' Öffentlichen Schlüssel (%s) aufs Telefon legen? [J/n] > ' "$(basename "$PUBKEY")"
read -r SCHLUESSEL </dev/tty || SCHLUESSEL=n
case "${SCHLUESSEL:-j}" in
j|J|"")
if adb push "$PUBKEY" /sdcard/hpos-id.pub >/dev/null 2>&1; then
ok "Schlüssel abgelegt unter /sdcard/hpos-id.pub"
hinweis "Auf dem Telefon einrichten: bash ~/hpos/tools/dev-ssh.sh"
else
warn "Übertragung fehlgeschlagen"
fi
;;
esac
fi
fi
kopf "Fertig"
cat <<TEXT
Nächste Schritte auf dem Telefon, in Termux:
+178 -6
View File
@@ -23,6 +23,58 @@ try pkg update -y
try pkg install -y x11-repo tur-repo
try pkg update -y
# Vollupgrade vor allen Einzelinstallationen. Termux verträgt keine Teilupgrades:
# die Pakete werden gegeneinander gebaut, und ein einzeln nachinstalliertes trifft
# sonst auf veraltete Abhängigkeiten. Genau daran ist proot-distro gescheitert.
# init-phone.sh macht das schon; wer direkt hier einsteigt, bekommt es hier.
section "Termux auf einen Stand bringen"
if [ "${HPOS_OHNE_UPGRADE:-0}" = 1 ]; then
warn "Übersprungen (HPOS_OHNE_UPGRADE=1)"
else
export DEBIAN_FRONTEND=noninteractive
# --force-confold: vorhandene Konfiguration behalten, statt mitten im Lauf
# eine Rückfrage zu stellen, die niemand beantwortet.
if try pkg upgrade -y -o Dpkg::Options::=--force-confold; then
ok "Termux ist aktuell"
else
warn "Upgrade brach ab — versuche zu reparieren."
# Dateikonflikt gezielt behandeln: entsteht, wenn ein Paket aufgeteilt
# wurde und beide Teile kurzzeitig dieselbe Datei mitbringen — bei Termux
# etwa libmagic, herausgelöst aus file. --force-overwrite wird bewusst nur
# dann gesetzt, wenn der Konflikt im Protokoll nachgewiesen ist; pauschal
# hieße es, jede künftige Dateikollision stillschweigend zu übergehen.
if grep -q 'which is also in package' "$LOG" 2>/dev/null; then
warn "Dateikonflikt zwischen zwei Paketen erkannt:"
grep 'trying to overwrite' "$LOG" | tail -3 | sed 's/^/ /' | tee -a "$LOG"
hint "Typisch bei einer Paketaufteilung. Wiederhole mit --force-overwrite."
try pkg upgrade -y -o Dpkg::Options::=--force-confold \
-o Dpkg::Options::=--force-overwrite
fi
# Meist scheitert ein einzelnes Paket beim Auspacken, danach steckt dpkg
# halb fertig fest und blockiert jede weitere Installation.
try dpkg --configure -a
try apt-get -f install -y
if try pkg upgrade -y -o Dpkg::Options::=--force-confold \
-o Dpkg::Options::=--force-overwrite; then
ok "Nach der Reparatur durchgelaufen"
else
warn "Bleibt unvollständig."
hint "Speicherplatz: df -h \$PREFIX"
hint "Freier Platz unter etwa 1 GB ist die häufigste Ursache."
hint "Fehler ohne Rauschen: pkg upgrade -y 2>&1 | grep -viE 'unable to delete' | tail -20"
hint "Die vielen 'unable to delete old directory' sind Warnungen, keine Fehler."
fi
fi
# Platz prüfen, solange es noch nicht weh tut: das Debian-Rootfs samt Plasma
# Mobile braucht mehrere GB, und ein Abbruch mitten im Entpacken ist ärgerlicher
# als eine Warnung vorher.
FREI_KB=$(df -Pk "$PREFIX" 2>/dev/null | awk 'NR==2 {print $4}')
if [ -n "$FREI_KB" ]; then
log " Freier Speicher: $((FREI_KB / 1024)) MB"
[ "$FREI_KB" -lt 2000000 ] && warn "Unter 2 GB frei — für Debian plus Plasma Mobile knapp."
fi
fi
# ── Basispakete ──────────────────────────────────────────────────────────────
# Einzeln installiert und einzeln protokolliert: Paketnamen wandern in Termux
# gelegentlich, und wir wollen wissen, WELCHES Paket fehlt, statt nur dass etwas fehlt.
@@ -38,11 +90,33 @@ done
# zur Laufzeit; das Ergebnis landet im GPU-Profil.
section "GPU-Pakete"
GPU_PKGS=(virglrenderer-android vulkan-loader-android vulkan-tools angle-android
mesa-vulkan-icd-wrapper mesa-zink virglrenderer-mesa-zink)
mesa-zink virglrenderer-mesa-zink)
for p in "${GPU_PKGS[@]}"; do
if try pkg install -y "$p"; then ok "$p"; else warn "$p nicht verfügbar — kein Beinbruch"; fi
done
# Der Vulkan-Treiber ist der einzige GPU-Teil, der wirklich zählt: ohne ihn bleibt
# nur Software-Rendering. Die Paketnamen haben sich in Termux mehrfach geändert,
# deshalb werden mehrere Kandidaten probiert, bis einer greift.
section "Vulkan-Treiber (Turnip)"
ICD_KANDIDATEN=(mesa-vulkan-icd-freedreno mesa-vulkan-icd-wrapper
mesa-vulkan-icd-wrapper-android mesa-vulkan-icd-swrast)
ICD_GEFUNDEN=""
for p in "${ICD_KANDIDATEN[@]}"; do
if pkg install -y "$p" >>"$LOG" 2>&1; then
ICD_GEFUNDEN="$p"
ok "$p installiert"
break
fi
hint "$p — nicht verfügbar"
done
if [ -z "$ICD_GEFUNDEN" ]; then
warn "Kein Vulkan-Treiber installierbar."
hint "Ohne ihn läuft der Desktop im Software-Rendering — S1 wird träge,"
hint "sagt aber trotzdem etwas über die Machbarkeit aus."
hint "Verfügbare Kandidaten prüfen: pkg search mesa-vulkan"
fi
section "GPU erkennen"
PLATFORM="$(getprop ro.board.platform 2>/dev/null)"
HARDWARE="$(getprop ro.hardware 2>/dev/null)"
@@ -118,13 +192,111 @@ try termux-wake-lock && ok "Wake-Lock aktiv" || warn "Wake-Lock nicht gesetzt"
# ── Debian-Rootfs ────────────────────────────────────────────────────────────
section "Debian-Rootfs"
if proot-distro list --installed 2>/dev/null | grep -q "^$DISTRO\b" \
|| [ -d "$PREFIX/var/lib/proot-distro/installed-rootfs/$DISTRO" ]; then
ok "$DISTRO bereits installiert"
# Erst prüfen, ob proot-distro überhaupt LÄUFT. Ein installiertes Paket, dessen
# Shebang-Interpreter fehlt, meldet sich mit "cannot execute: required file not
# found" — eine Meldung, die aussieht als fehle das Programm, obwohl es da ist.
if ! proot-distro list >/dev/null 2>&1; then
fail "proot-distro ist installiert, lässt sich aber nicht ausführen"
PD="$(command -v proot-distro)"
SHEBANG="$(head -1 "$PD" 2>/dev/null)"
INTERPRETER="$(printf '%s' "$SHEBANG" | sed 's|^#!||; s| .*||')"
log " Datei: $PD"
log " Shebang: $SHEBANG"
log " Interpreter: ${INTERPRETER:-keiner}"
if [ -n "$INTERPRETER" ] && [ ! -x "$INTERPRETER" ]; then
fail "Der Interpreter fehlt — nicht proot-distro selbst."
log ""
log " Das ist der klassische Termux-Teilupgrade-Fehler: das Paket wurde gegen"
log " eine Fassung gebaut, installiert ist eine andere. Termux verträgt keine"
log " Teilupgrades — hier hilft nur, alles auf denselben Stand zu bringen."
# Schritt 1: fehlendes Paket nachziehen. Bei python3.x heißt es schlicht python.
case "$(basename "$INTERPRETER")" in
python3*|python) try pkg install -y python ;;
bash) try pkg install -y bash ;;
*) try pkg install -y "$(basename "$INTERPRETER")" ;;
esac
# Schritt 2: Vollupgrade. Das ist die eigentliche Reparatur.
if [ ! -x "$INTERPRETER" ]; then
log ""
warn "Reicht nicht — jetzt das Vollupgrade. Das dauert und lädt einiges."
try pkg update -y
try pkg upgrade -y
try pkg install -y proot proot-distro
fi
# Schritt 3: Notnagel. Nur für Python, und nur weil proot-distro reines
# Python ohne kompilierte Erweiterungen ist — sonst wäre eine Version
# überzustülpen grob fahrlässig.
if [ ! -x "$INTERPRETER" ]; then
case "$(basename "$INTERPRETER")" in
python3.*)
VORHANDEN="$(ls "$PREFIX/bin"/python3.* 2>/dev/null | grep -v config | head -1)"
if [ -n "$VORHANDEN" ]; then
log ""
warn "Notnagel: verweise $(basename "$INTERPRETER") auf $(basename "$VORHANDEN")"
hint "Vertretbar, weil proot-distro reines Python ist."
hint "Sauber wird es erst mit einem vollständigen 'pkg upgrade'."
ln -sf "$VORHANDEN" "$INTERPRETER" && ok "Verweis angelegt"
fi
;;
esac
fi
else
try pkg install -y bash coreutils proot proot-distro
fi
if ! proot-distro list >/dev/null 2>&1; then
fail "Bleibt defekt — ohne proot-distro geht es nicht weiter."
log ""
log " Von Hand versuchen, in dieser Reihenfolge:"
log " pkg update && pkg upgrade -y # Vollupgrade, keine Teilschritte"
log " pkg install -y python proot proot-distro"
log " termux-change-repo # falls der Spiegel unstimmig ist"
exit 1
fi
ok "proot-distro läuft wieder"
fi
# Ob ein Rootfs da ist, wird nicht mehr an Pfaden oder Ausgabeformaten festgemacht:
# proot-distro hat sein Ablageformat gewechselt (erkennbar an der Meldung
# "Migrating legacy container"), und danach lag das Rootfs woanders als erwartet.
# Ein Anmeldeversuch ist die einzige Prüfung, die das nicht betrifft — sie fragt
# nicht, wo etwas liegt, sondern ob es funktioniert.
rootfs_laeuft() { proot-distro login "$DISTRO" -- true >/dev/null 2>&1; }
if rootfs_laeuft; then
ok "$DISTRO bereits installiert und lauffähig"
else
log " Installiere $DISTRO — das dauert einige Minuten."
need proot-distro install "$DISTRO"
ok "$DISTRO installiert"
if proot-distro install "$DISTRO" >>"$LOG" 2>&1; then
ok "$DISTRO installiert"
else
# 'already exists' bzw. 'already installed' ist kein Fehler, sondern ein
# Wettlauf zwischen meiner Prüfung und der Wirklichkeit. Die Wortwahl hat
# sich über die Fassungen geändert (5.5.0 spricht von Containern), deshalb
# werden beide Formulierungen abgedeckt. Entscheidend ist ohnehin allein,
# ob sich danach anmelden lässt.
if grep -qiE 'already (installed|exists)' "$LOG"; then
warn "proot-distro meldet: gibt es bereits"
else
warn "Installation meldete einen Fehler:"
tail -n 12 "$LOG" | sed 's/^/ /'
fi
if rootfs_laeuft; then
ok "Anmeldung funktioniert trotzdem — weiter."
else
fail "Kein lauffähiges $DISTRO-Rootfs."
log ""
log " Von Hand versuchen:"
log " proot-distro list # was ist da?"
log " proot-distro reset $DISTRO # neu aufsetzen, Daten weg"
log " proot-distro remove $DISTRO && proot-distro install $DISTRO"
exit 1
fi
fi
fi
section "Debian-Grundausstattung"
+117 -15
View File
@@ -27,11 +27,54 @@ log "HPOS Phase 0 · S1 — Plasma Mobile in proot"
log "Auflösung: ${BREITE}x${HOEHE} GPU-Profil: $GPU_PROFIL"
log "Protokoll: $LOG"
voraussetzungen_pruefen "$DISTRO" || {
log ""
fail "Abbruch: die Grundlage steht nicht. S1 wäre nicht aussagekräftig."
exit 1
}
# ── Alte Reste einsammeln ────────────────────────────────────────────────────
# Läufe hinterlassen Prozesse, die den nächsten stören: ein zweiter KWin findet
# den Wayland-Socket belegt, ein alter virgl-Server hält seinen Socket, und
# plasmashell hängt sich an die falsche Sitzung. Die proot-Prozesse liegen im
# selben PID-Raum wie Termux und sind von hier aus erreichbar.
#
# Bewusst NICHT mit 'pkill -f': das durchsucht die gesamte Kommandozeile und
# trifft dabei auch den eigenen Aufruf, wenn das Muster darin vorkommt. Beim
# Entwickeln dieses Blocks hat sich der Test genau so selbst abgeschossen.
# Deshalb über pgrep, mit eigener PID und Elternprozess ausgenommen.
section "Aufräumen"
pkill -f termux-x11 2>/dev/null && ok "alten X-Server beendet" || hint "kein alter X-Server"
pkill -f virgl_test_server 2>/dev/null && ok "alten virgl-Server beendet" || true
sleep 1
# Die eigene Ahnenkette. Nicht nur $$ und $PPID: wird das Skript aus einer Shell
# gestartet, deren Kommandozeile eines der Muster enthält, trifft pgrep diese
# Shell — und das Skript beendet seinen eigenen Großelternprozess. Genau das ist
# beim Entwickeln dieses Blocks passiert.
AHNEN=" "
_p=$$
while [ -n "$_p" ] && [ "$_p" != "0" ] && [ "$_p" != "1" ]; do
AHNEN="$AHNEN$_p "
_p=$(awk '{print $4}' "/proc/$_p/stat" 2>/dev/null)
done
beenden() {
local muster="$1" pid anzahl=0
for pid in $(pgrep -f "$muster" 2>/dev/null); do
case "$AHNEN" in *" $pid "*) continue ;; esac
kill "$pid" 2>/dev/null && anzahl=$((anzahl + 1))
done
if [ "$anzahl" -gt 0 ]; then ok "beendet: $muster ($anzahl)"
else hint "nichts zu beenden: $muster"; fi
}
for muster in kwin_wayland plasmashell xdg-desktop-portal \
termux-x11 virgl_test_server; do
beenden "$muster"
done
# Verwaiste Wayland-Sockets aus abgebrochenen Läufen. Bleiben sie liegen, startet
# KWin auf wayland-1 statt wayland-0 — und plasmashell sucht auf wayland-0.
rm -f /tmp/hpos-runtime/wayland-* 2>/dev/null || true
sleep 2
# ── X-Server ─────────────────────────────────────────────────────────────────
section "Termux-X11 starten"
@@ -39,6 +82,24 @@ if ! have termux-x11; then
fail "termux-x11 fehlt. Bitte 00-bootstrap-termux.sh laufen lassen."
exit 1
fi
# Das Termux-Paket und die Termux:X11-APP sind zwei verschiedene Dinge. Fehlt die
# App, startet der X-Server anstandslos — nur sieht man nichts.
#
# Zuverlässig prüfen lässt sich das aber nicht: seit Android 11 sieht eine App
# nur noch die Pakete, die sie in ihrem Manifest anmeldet oder die ihre UID teilen.
# 'pm list packages' liefert aus Termux heraus also eine gefilterte Liste, und ein
# Nichtfinden beweist gar nichts. Deshalb nur ein Hinweis, kein Abbruch — ein
# falscher Abbruch wäre schlimmer als ein überflüssiger Hinweis.
if pm list packages 2>/dev/null | tr -d '\r' | grep -q '^package:com\.termux\.x11$'; then
ok "Termux:X11-App gefunden"
else
warn "Termux:X11-App nicht in der Paketliste — sagt wenig aus."
hint "Android filtert diese Liste seit Version 11; die App kann trotzdem da sein."
hint "Bleibt der Bildschirm später leer, hier ansetzen:"
hint " Vom Rechner: ./provision/install-termux.sh"
hint " Von Hand: https://github.com/termux/termux-x11/releases"
fi
termux-x11 :0 >>"$LOG" 2>&1 &
X11_PID=$!
sleep 3
@@ -50,22 +111,53 @@ else
fi
# Die App muss im Vordergrund sein, sonst sieht man nichts und Android drosselt.
# Hier zeigt sich auch, ob sie überhaupt existiert: 'am start' meldet einen
# Fehler, wenn es die Aktivität nicht gibt — das ist die verlässliche Prüfung,
# anders als die gefilterte Paketliste weiter oben.
log " Hole die Termux:X11-App in den Vordergrund …"
try am start --user 0 -n com.termux.x11/com.termux.x11.MainActivity
if am start --user 0 -n com.termux.x11/com.termux.x11.MainActivity >>"$LOG" 2>&1; then
ok "Termux:X11-App gestartet"
else
fail "Termux:X11-App ließ sich nicht starten — sie fehlt vermutlich."
tail -n 5 "$LOG" | sed 's/^/ /'
hint "Installieren: ./provision/install-termux.sh (vom Rechner)"
exit 1
fi
sleep 2
log " In der App sollte jetzt nicht mehr 'not connected' stehen."
hint "Steht es weiterhin dort, hat der X-Server keine Verbindung bekommen."
# ── GPU-Server ───────────────────────────────────────────────────────────────
section "GPU-Pfad vorbereiten ($GPU_PROFIL)"
if [ "$GPU_PROFIL" = "virgl" ] && have virgl_test_server_android; then
virgl_test_server_android >>"$LOG" 2>&1 &
sleep 1
ok "virgl-Server gestartet"
elif [ "$GPU_PROFIL" = "turnip" ]; then
ok "Turnip greift direkt auf KGSL zu — kein Proxy nötig"
else
warn "Kein GPU-Server aktiv, Software-Rendering wahrscheinlich"
# Wichtige Korrektur gegenüber der ersten Fassung: Turnip lebt in Termux'
# Verzeichnis, nicht im Debian-Rootfs. Im Container GALLIUM_DRIVER=zink zu setzen
# geht deshalb ins Leere — Zink bräuchte dort einen Vulkan-Treiber, und da ist
# keiner. Ergebnis war gar kein OpenGL, und ohne das kann QtQuick nichts zeichnen.
#
# Der tragfähige Weg führt über virgl: der Server läuft in TERMUX und nutzt dort
# den schnellen Pfad (Zink auf Turnip), der Container spricht nur virpipe mit ihm.
# Turnip bleibt also im Spiel — nur eine Ebene tiefer als gedacht.
if ! have virgl_test_server_android; then
fail "virgl_test_server_android fehlt — ohne ihn gibt es im Container kein OpenGL."
hint "Nachinstallieren: pkg install -y virglrenderer-android"
exit 1
fi
pkill -f virgl_test_server 2>/dev/null; sleep 1
if [ "$GPU_PROFIL" = "turnip" ]; then
# Der Server selbst rendert über Zink auf Turnip — dort ist der Treiber ja.
GALLIUM_DRIVER=zink MESA_LOADER_DRIVER_OVERRIDE=zink \
virgl_test_server_android >>"$LOG" 2>&1 &
ok "virgl-Server gestartet, rendert über Zink auf Turnip"
else
virgl_test_server_android >>"$LOG" 2>&1 &
ok "virgl-Server gestartet"
fi
VIRGL_PID=$!
sleep 2
kill -0 "$VIRGL_PID" 2>/dev/null && ok "läuft (PID $VIRGL_PID)" \
|| warn "virgl-Server sofort beendet — siehe Protokoll"
# ── Audio, damit der Desktop nicht schon daran scheitert ─────────────────────
section "PulseAudio"
pulseaudio --kill 2>/dev/null || true
@@ -83,7 +175,17 @@ HPOS_GPU_PROFIL="$GPU_PROFIL" HPOS_BREITE="$BREITE" HPOS_HOEHE="$HOEHE" \
proot-distro login "$DISTRO" --shared-tmp --bind "$REPO_ROOT:/hpos" -- \
bash /hpos/spike/inner/s1-plasma.sh 2>&1 | tee -a "$LOG"
verdict "S1" "Zeigt die Termux:X11-App eine bedienbare Plasma-Mobile-Oberfläche?
Prüfe: reagiert Touch, erscheint die Maliit-Tastatur beim Tippen in ein Feld?"
verdict "S1" "Was zeigt die Termux:X11-App?
log "Aufräumen: pkill -f termux-x11 ; pkill -f virgl_test_server"
[n] Nichts, oder nur 'not connected' — der X-Server kam nicht durch.
[t] Schwarze Fläche mit kleinem X-Cursor oben links.
Das heißt: X11 ist verbunden und der Server läuft, aber es zeichnet
nichts darauf. KWin oder die Plasma-Shell fehlen — halber Erfolg.
[j] Eine Plasma-Mobile-Oberfläche mit Hintergrund und Bedienelementen.
Nur bei [j] weiter prüfen: reagiert Touch auf Wischen und Tippen?
Die Bildschirmtastatur erst dort testen, wo es ein Eingabefeld gibt —
etwa in den Plasma-Einstellungen. Vorher gibt es nichts zu tippen."
log "Alles beenden: pkill -f 'kwin_wayland|plasmashell|termux-x11|virgl_test_server'"
log "(Das Skript macht das beim nächsten Start ohnehin von selbst.)"
+14 -2
View File
@@ -24,20 +24,32 @@ WAV="$OUT_DIR/$WAV_NAME"
log "HPOS Phase 0 · S2 — Mikrofon im Linux"
log "Protokoll: $LOG"
voraussetzungen_pruefen "$DISTRO" || {
log ""
fail "Abbruch: die Grundlage steht nicht. S2 wäre nicht aussagekräftig."
exit 1
}
# ── Berechtigung ─────────────────────────────────────────────────────────────
# Der häufigste Grund für eine formal korrekte, aber stumme Aufnahme: PulseAudio
# läuft IM Termux-Prozess und öffnet das Mikrofon über OpenSL ES von dort aus.
# Eine Berechtigung, die nur Termux:API hat, nützt ihm deshalb nichts.
section "Mikrofonberechtigung von Termux"
BERECHTIGT="unbekannt"
if RECHTE=$(dumpsys package com.termux 2>/dev/null | grep -i 'RECORD_AUDIO'); then
RECHTE=$(dumpsys package com.termux 2>/dev/null | grep -i 'RECORD_AUDIO' || true)
if [ -n "$RECHTE" ]; then
log "$(printf '%s' "$RECHTE" | sed 's/^/ /')"
case "$RECHTE" in
*granted=true*) BERECHTIGT=ja; ok "com.termux darf aufnehmen" ;;
*granted=false*) BERECHTIGT=nein; fail "com.termux darf NICHT aufnehmen" ;;
esac
else
warn "Konnte den Berechtigungsstatus nicht auslesen"
# Erwartbar: dumpsys verlangt die DUMP-Berechtigung, die eine gewöhnliche App
# nicht hat. Kein Grund zur Sorge, aber der Status bleibt hier eben offen.
warn "Status von hier aus nicht auslesbar (dumpsys ist Apps verwehrt)"
hint "Vom PC prüfen: adb shell dumpsys package com.termux | grep RECORD_AUDIO"
hint "Setzen: adb shell pm grant com.termux android.permission.RECORD_AUDIO"
hint "Oder am Gerät: Einstellungen → Apps → Termux → Berechtigungen → Mikrofon"
fi
if [ "$BERECHTIGT" = nein ]; then
+6
View File
@@ -17,6 +17,12 @@ DISTRO=debian
log "HPOS Phase 0 · S3 — Virtuelle PipeWire-Kamera"
log "Protokoll: $LOG"
voraussetzungen_pruefen "$DISTRO" || {
log ""
fail "Abbruch: die Grundlage steht nicht. S3 wäre nicht aussagekräftig."
exit 1
}
log ""
log "Voraussetzung: ein laufender X-Server aus S1."
log "Falls nicht, in einer zweiten Sitzung: termux-x11 :0 &"
+8
View File
@@ -94,11 +94,19 @@ bash spike/04-s4-intent-handoff.sh
```bash
cd ~/hpos && git pull
# Falls das scheitert (Server liefert keine Pakete aus):
rm -rf ~/hpos && bash /sdcard/hpos-init.sh
```
Per adb kommt bewusst nur das Startskript aufs Telefon, nicht das Repo selbst.
Dadurch liegt dort eine echte Arbeitskopie statt einer Kopie.
Der zweite Befehl weicht auf den Archiv-Endpunkt aus, wenn `git clone` am Server
scheitert. Das Ergebnis ist dann ein Abzug ohne `.git``git pull` geht damit nicht
mehr, und jede Aktualisierung braucht denselben Befehl erneut. Sobald der Server
wieder Pakete liefert, stellt er auch die Arbeitskopie wieder her.
Jedes Skript fragt am Ende nach deiner Beurteilung und schreibt sie nach
`spike/out/ergebnisse.tsv`. Rohprotokolle liegen daneben in `spike/out/*.log`.
+209 -33
View File
@@ -22,21 +22,19 @@ export QT_QPA_PLATFORM=wayland
export QT_QUICK_CONTROLS_MOBILE=1
export PLASMA_PLATFORM=phone
export KDE_FULL_SESSION=true
# Ohne gesetztes Cursor-Thema meldet KWin 'Failed to load cursor theme "default"'
# und zeichnet gar keinen Mauszeiger.
export XCURSOR_THEME=breeze_cursors
export XCURSOR_SIZE=24
case "$GPU_PROFIL" in
turnip)
# Zink übersetzt OpenGL auf Vulkan, Turnip liefert Vulkan direkt aus dem
# Adreno-Treiber — zusammen der schnellste Weg ohne Root.
export MESA_LOADER_DRIVER_OVERRIDE=zink
export GALLIUM_DRIVER=zink
export TU_DEBUG=noconform
;;
virgl)
export GALLIUM_DRIVER=virpipe
export MESA_GL_VERSION_OVERRIDE=4.3
;;
esac
echo " GPU-Profil: $GPU_PROFIL"
# Im Container immer virpipe, unabhängig vom Profil. Der Vulkan-Treiber liegt in
# Termux' Verzeichnis und ist hier drinnen nicht erreichbar — Zink hätte also
# nichts, worauf es aufsetzen könnte. Der schnelle Pfad (Zink auf Turnip) wird
# eine Ebene tiefer genutzt, vom virgl-Server auf der Termux-Seite.
export GALLIUM_DRIVER=virpipe
export MESA_GL_VERSION_OVERRIDE=4.3
export MESA_GLES_VERSION_OVERRIDE=3.2
echo " GPU-Profil: $GPU_PROFIL (im Container über virpipe)"
# ── Pakete ───────────────────────────────────────────────────────────────────
# Bewusst ohne empfohlene Pakete: sonst zieht Plasma Mobile eine komplette
@@ -44,8 +42,14 @@ echo " GPU-Profil: $GPU_PROFIL"
if ! command -v kwin_wayland >/dev/null 2>&1; then
echo "── Installiere Plasma Mobile (einmalig, gut 1 GB) ──"
apt-get update -qq
apt-get install -y --no-install-recommends \
plasma-mobile kwin-wayland plasma-workspace maliit-keyboard \
# Bewusst OHNE --no-install-recommends: bei KDE stecken die QML-Module
# traditionell in den Empfehlungen, nicht in den harten Abhängigkeiten.
# Ohne sie ist die Shell formal installiert, aber innen hohl — plasmashell
# lädt sie dann nicht und fällt wortlos auf die Voreinstellung zurück.
# Für einen Machbarkeitsbeweis ist ein paar hundert MB zu viel das kleinere
# Übel gegenüber einem Fehlschlag aus dem falschen Grund.
apt-get install -y \
plasma-mobile plasma-nano kwin-wayland plasma-workspace maliit-keyboard \
xdg-desktop-portal-kde qtwayland5 libqt5waylandclient5 \
|| echo " ! Paketinstallation unvollständig — Details oben"
fi
@@ -55,12 +59,124 @@ for b in kwin_wayland plasmashell; do
else echo "$b fehlt"; fi
done
# ── Welche Shell? ────────────────────────────────────────────────────────────
# Ohne gültiges Shell-Paket startet plasmashell klaglos, meldet nur beiläufig
# "starting invalid corona" und zeichnet dann nichts. Von außen sieht das aus wie
# ein Fehlschlag des Compositors und ist keiner.
#
# In Debian liefert ausgerechnet 'plasma-mobile' KEINE Shell mit, sondern nur
# QML-Module und KWin-Skripte. Die Shell steckt in 'plasma-nano' und heißt
# org.kde.plasma.nano. Der Name org.kde.plasma.mobileshell aus den KDE-Quellen
# existiert in Debian nicht. Deshalb wird nachgesehen statt geraten.
shell_suchen() {
local kandidat
for kandidat in org.kde.plasma.mobileshell org.kde.plasma.phoneshell \
org.kde.plasma.nano org.kde.plasma.desktop; do
if [ -d "/usr/share/plasma/shells/$kandidat" ]; then
printf '%s' "$kandidat"
return 0
fi
done
return 1
}
echo "── Verfügbare Plasma-Shells ──"
if [ -d /usr/share/plasma/shells ]; then
ls -1 /usr/share/plasma/shells/ 2>/dev/null | sed 's/^/ /'
[ -z "$(ls -A /usr/share/plasma/shells/ 2>/dev/null)" ] && echo " (leer)"
else
echo " (Verzeichnis /usr/share/plasma/shells fehlt)"
fi
SHELL_PAKET="$(shell_suchen || true)"
if [ -z "$SHELL_PAKET" ]; then
echo " ! Keine Shell gefunden — versuche plasma-nano nachzuinstallieren."
apt-get install -y --no-install-recommends plasma-nano plasma-mobile 2>&1 \
| tail -5 | sed 's/^/ /'
SHELL_PAKET="$(shell_suchen || true)"
fi
if [ -n "$SHELL_PAKET" ]; then
echo " ✓ Shell: $SHELL_PAKET"
else
echo " ✗ Weiterhin keine Plasma-Shell — plasmashell hat nichts zu zeichnen."
echo " Prüfen: dpkg -L plasma-nano | grep shells"
echo " Ohne Shell ist Versuch A zum Scheitern verurteilt; XFCE zeigt dann,"
echo " ob wenigstens X-Server und GPU tragen."
fi
# ── GPU-Nachweis, bevor der Desktop startet ──────────────────────────────────
# Wenn hier schon nichts kommt, ist ein Fehlschlag von Plasma keine Aussage über
# Plasma, sondern über den GPU-Pfad. Deshalb vorher messen.
echo "── GPU-Nachweis ──"
timeout 20 glxinfo -B 2>&1 | grep -Ei 'OpenGL (renderer|version)|Device' || echo " ! glxinfo lieferte nichts"
timeout 20 vulkaninfo --summary 2>&1 | grep -Ei 'driverName|deviceName' || echo " ! vulkaninfo lieferte nichts"
echo "── Anzeige und GPU prüfen ──"
# Erst der X-Server. Wenn schon der nicht erreichbar ist, sagt jeder GL-Test
# darunter nichts aus.
if timeout 15 xdpyinfo >/tmp/hpos-xdpy.txt 2>&1; then
grep -E '^(name of display|version number|dimensions)' /tmp/hpos-xdpy.txt \
| head -3 | sed 's/^/ /'
echo " ✓ X-Server erreichbar"
else
echo " ✗ X-Server NICHT erreichbar — das wäre die Wurzel allen Übels:"
head -3 /tmp/hpos-xdpy.txt | sed 's/^/ /'
fi
# Jeden GL-Weg einzeln und mit Rückgabewert prüfen. Stürzt glxinfo ab, kommt die
# Meldung von der Shell und nicht vom Programm — sie ginge in einer
# Kommandoersetzung verloren, und die Ausgabe wäre einfach leer. Genau das ist
# vorher passiert und sah aus wie 'kein Treiber gefunden'.
gl_test() {
local name="$1"; shift
local aus rc
aus=$(env "$@" timeout 25 glxinfo -B 2>&1); rc=$?
if printf '%s' "$aus" | grep -qi 'OpenGL renderer'; then
printf '%s\n' "$aus" | grep -Ei 'OpenGL (renderer|version)' | sed 's/^/ /'
echo "$name"
return 0
fi
echo "$name — Rückgabewert $rc$([ "$rc" -gt 128 ] && echo ' (abgestürzt oder abgebrochen)')"
if [ -n "$aus" ]; then
printf '%s\n' "$aus" | head -4 | sed 's/^/ /'
else
echo " (keine Ausgabe)"
fi
return 1
}
# Ist der virgl-Socket überhaupt da? Fehlt er, hängt der Client bis zum Zeitlimit
# (Rückgabewert 124) statt eine Fehlermeldung zu liefern.
if ls /tmp/.virgl_test* >/dev/null 2>&1; then
echo " ✓ virgl-Socket sichtbar: $(ls /tmp/.virgl_test* 2>/dev/null | tr '\n' ' ')"
else
echo " ! Kein virgl-Socket unter /tmp — der Container sieht den Server nicht."
echo " (Termux legt ihn in sein tmp; proot-distro bindet das über --shared-tmp.)"
fi
if gl_test "OpenGL über virgl" GALLIUM_DRIVER=virpipe; then
GL_WEG=virgl
export GALLIUM_DRIVER=virpipe
else
# Hier war ein Denkfehler: schlugen beide Prüfungen fehl, wurde der
# Software-Pfad NICHT gesetzt — ausgerechnet dann nicht, wenn er am nötigsten
# ist. Und glxinfo taugt ohnehin nicht als Richter: unter Termux-X11
# scheitert es an X_GetImage mit BadMatch, weil es Pixel zurücklesen will.
# Das sagt etwas über glxinfo, nichts über OpenGL.
#
# Deshalb: Prüfungen nur zur Information, und im Zweifel immer den
# Software-Pfad. KWin und QtQuick haben je einen, der die GL-Ebene ganz umgeht.
gl_test "OpenGL in Software (llvmpipe)" LIBGL_ALWAYS_SOFTWARE=1 || true
GL_WEG=software
unset GALLIUM_DRIVER
export LIBGL_ALWAYS_SOFTWARE=1
export KWIN_COMPOSE=Q # KWin: QPainter statt OpenGL
export QT_QUICK_BACKEND=software
export QMLSCENE_DEVICE=softwarecontext
echo " → Weiter mit Software-Rendering (KWin QPainter, QtQuick software)."
echo " Träge, aber für S1 ausreichend: die Frage ist, OB es läuft."
echo " Ein scheiterndes glxinfo schließt das nicht aus — es prüft anders."
fi
echo " Verwendeter Weg: $GL_WEG"
# ── D-Bus ────────────────────────────────────────────────────────────────────
if [ ! -e /run/dbus/system_bus_socket ]; then
@@ -73,23 +189,83 @@ fi
# Das ist die eigentliche Wette. In diesem Modus braucht KWin weder DRM noch Seat.
echo ""
echo "── Versuch A: kwin_wayland --x11-display ──"
echo " Umgebung: GL_WEG=$GL_WEG KWIN_COMPOSE=${KWIN_COMPOSE:-nicht gesetzt}"
echo " QT_QUICK_BACKEND=${QT_QUICK_BACKEND:-nicht gesetzt}"
LOGA=/tmp/hpos-s1-versuchA.log
dbus-run-session -- kwin_wayland \
--x11-display "$DISPLAY" \
--width "$BREITE" --height "$HOEHE" \
--xwayland \
-- plasmashell -p org.kde.plasma.phoneshell \
>"$LOGA" 2>&1 &
PID_A=$!
echo " gestartet (PID $PID_A), warte 25 s auf die Oberfläche …"
sleep 25
if kill -0 "$PID_A" 2>/dev/null; then
echo " ✓ Versuch A läuft noch — jetzt in die Termux:X11-App wechseln und schauen."
# Die lange Optionsform statt '-p': kurze Formen ändern sich zwischen den
# Plasma-Fassungen leichter als lange, und eine unbekannte Kurzoption wird still
# ignoriert statt bemängelt. Genau das ist der Verdacht.
#
# Und statt einen Namen zu raten, werden die vorhandenen Shells der Reihe nach
# ausprobiert. 'invalid corona' im Protokoll ist das Abbruchkriterium — es sagt,
# dass plasmashell nichts zeichnen wird, ohne dass man hinschauen muss.
beenden_versuch() {
[ -n "${PID_A:-}" ] && kill "$PID_A" 2>/dev/null
pkill -x kwin_wayland 2>/dev/null
pkill -x plasmashell 2>/dev/null
sleep 3
}
starte_mit_shell() {
local shell="$1"
: >"$LOGA"
dbus-run-session -- kwin_wayland \
--x11-display "$DISPLAY" \
--width "$BREITE" --height "$HOEHE" \
--xwayland \
-- plasmashell ${shell:+--shell-plugin "$shell"} \
>"$LOGA" 2>&1 &
PID_A=$!
sleep 25
kill -0 "$PID_A" 2>/dev/null || return 1
grep -q 'invalid corona' "$LOGA" && return 2
return 0
}
KANDIDATEN="$SHELL_PAKET"
for w in org.kde.plasma.mobileshell org.kde.plasma.nano; do
case " $KANDIDATEN " in *" $w "*) ;; *) KANDIDATEN="$KANDIDATEN $w" ;; esac
done
ERFOLG=0
for shell in $KANDIDATEN; do
[ -d "/usr/share/plasma/shells/$shell" ] || continue
echo ""
echo " Beenden mit: pkill -f kwin_wayland"
echo " Protokoll: $LOGA"
tail -n 25 "$LOGA" 2>/dev/null | sed 's/^/ /'
echo " → Versuche Shell: $shell"
starte_mit_shell "$shell"
case $? in
0) echo " ✓ läuft, und plasmashell meldet keine ungültige Shell."
ERFOLG=1; break ;;
2) echo " ✗ 'invalid corona' — diese Shell trägt nicht."
grep 'invalid corona' "$LOGA" | tail -1 | sed 's/^/ /'
beenden_versuch ;;
*) echo " ✗ Prozess sofort beendet."
tail -n 15 "$LOGA" | sed 's/^/ /' ;;
esac
done
if [ "$ERFOLG" = 1 ] && kill -0 "$PID_A" 2>/dev/null; then
echo ""
echo " ✓ Versuch A läuft — jetzt in die Termux:X11-App wechseln und schauen."
echo " Verwendete Shell: $shell"
echo " Beenden mit: pkill -x kwin_wayland"
echo " Protokoll: $LOGA"
tail -n 20 "$LOGA" 2>/dev/null | sed 's/^/ /'
exit 0
fi
if kill -0 "${PID_A:-0}" 2>/dev/null; then
# Läuft noch, aber keine Shell hat getragen: KWin steht, plasmashell zeichnet
# nichts. Das ist ein Teilerfolg und soll auch so benannt werden — der
# Compositor ist der schwierige Teil, die Shell der austauschbare.
echo ""
echo " ! KWin läuft, aber keine der vorhandenen Shells ließ sich laden."
echo " Der Compositor ist damit bewiesen, die Shell nicht."
echo " Nächster Schritt von Hand, zeigt die eigentliche Ursache:"
echo " plasmashell --shell-plugin org.kde.plasma.mobileshell"
echo " Protokoll: $LOGA"
tail -n 20 "$LOGA" 2>/dev/null | sed 's/^/ /'
exit 0
fi
+49
View File
@@ -65,6 +65,55 @@ require_termux() {
fi
}
# Prüft, ob die Grundlage steht, BEVOR ein Beweis losläuft. Ohne das scheitert ein
# Test mitten im Ablauf und sieht aus wie ein inhaltlicher Fehlschlag, obwohl nur
# die Vorbereitung fehlt — die irreführendste Sorte Fehler.
voraussetzungen_pruefen() {
local distro="${1:-debian}" fehlt=0
section "Voraussetzungen"
if ! have proot-distro; then
fail "proot-distro ist nicht installiert"
hint "Nachholen mit: bash spike/00-bootstrap-termux.sh"
return 1
fi
# 'cannot execute: required file not found' heißt NICHT, dass das Skript fehlt.
# Es heißt, dass der Interpreter aus seiner Shebang-Zeile fehlt. Der Unterschied
# kostet sonst eine halbe Stunde Suche an der falschen Stelle.
local pfad interpreter
pfad="$(command -v proot-distro)"
if ! proot-distro list >/dev/null 2>&1; then
fail "proot-distro lässt sich nicht ausführen"
interpreter="$(head -1 "$pfad" 2>/dev/null | sed 's|^#!||; s| .*||')"
log " Datei: $pfad"
log " Shebang: ${interpreter:-keine}"
if [ -n "$interpreter" ] && [ ! -x "$interpreter" ]; then
fail "Der Interpreter $interpreter fehlt oder ist nicht ausführbar."
hint "Das ist die Ursache, nicht proot-distro selbst."
hint "Reparieren mit: pkg install -y bash coreutils proot proot-distro"
else
hint "Reparieren mit: pkg reinstall -y proot-distro"
fi
return 1
fi
ok "proot-distro ausführbar"
# Anmeldeversuch statt Pfad- oder Ausgabeprüfung: proot-distro hat sein
# Ablageformat gewechselt, und danach lag das Rootfs woanders als erwartet.
# Diese Prüfung fragt nicht, wo etwas liegt, sondern ob es funktioniert.
if proot-distro login "$distro" -- true >/dev/null 2>&1; then
ok "$distro-Rootfs lauffähig"
else
fail "$distro-Rootfs fehlt oder startet nicht"
hint "Nachholen mit: bash spike/00-bootstrap-termux.sh"
fehlt=1
fi
return $fehlt
}
# Fragt am Ende jedes Beweises nach dem, was nur ein Mensch beurteilen kann:
# ob es tatsächlich funktioniert hat. Das Ergebnis geht ins Protokoll.
verdict() {
+102
View File
@@ -0,0 +1,102 @@
#!/data/data/com.termux/files/usr/bin/bash
# HPOS — SSH-Zugang zu Termux einrichten. Reines Entwicklerwerkzeug.
#
# Für die Arbeit am Projekt ist ein SSH-Zugang deutlich angenehmer als die
# Bildschirmtastatur: echte Tastatur, Zwischenablage, Verlauf, und über sshfs
# lassen sich die Dateien auf dem Telefon direkt im Editor des Rechners öffnen.
#
# Termux' sshd lauscht auf Port 8022, nicht 22 — Ports unter 1024 darf eine
# gewöhnliche App nicht belegen.
#
# Aufruf in Termux: bash ~/hpos/tools/dev-ssh.sh
set -uo pipefail
SCHLUESSEL_ABLAGE="/sdcard/hpos-id.pub"
if [ -t 1 ]; then
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'
C_HEAD=$'\033[1;36m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
else
C_OK=""; C_WARN=""; C_ERR=""; C_HEAD=""; C_DIM=""; C_OFF=""
fi
kopf() { printf '\n%s══ %s ══%s\n' "$C_HEAD" "$*" "$C_OFF"; }
ok() { printf '%s ✓%s %s\n' "$C_OK" "$C_OFF" "$*"; }
warn() { printf '%s !%s %s\n' "$C_WARN" "$C_OFF" "$*"; }
fehl() { printf '%s ✗%s %s\n' "$C_ERR" "$C_OFF" "$*"; }
hinweis() { printf '%s %s%s\n' "$C_DIM" "$*" "$C_OFF"; }
case "${PREFIX:-}" in
*com.termux*) : ;;
*) fehl "Dieses Skript läuft in Termux auf dem Telefon."; exit 1 ;;
esac
kopf "OpenSSH"
if command -v sshd >/dev/null 2>&1; then
ok "bereits installiert"
else
pkg install -y openssh || { fehl "Installation fehlgeschlagen"; exit 1; }
ok "installiert"
fi
# ── Anmeldung ────────────────────────────────────────────────────────────────
# Schlüssel vor Passwort: ein Passwort auf der Bildschirmtastatur einzutippen ist
# mühsam, und wer es sich deshalb kurz wählt, hat einen offenen Dienst im WLAN.
kopf "Anmeldung"
mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
if [ -r "$SCHLUESSEL_ABLAGE" ]; then
if grep -qxFf "$SCHLUESSEL_ABLAGE" "$HOME/.ssh/authorized_keys" 2>/dev/null; then
ok "Öffentlicher Schlüssel bereits hinterlegt"
else
cat "$SCHLUESSEL_ABLAGE" >>"$HOME/.ssh/authorized_keys"
chmod 600 "$HOME/.ssh/authorized_keys"
ok "Öffentlichen Schlüssel aus $SCHLUESSEL_ABLAGE übernommen"
fi
elif [ -s "$HOME/.ssh/authorized_keys" ]; then
ok "authorized_keys vorhanden"
else
warn "Kein öffentlicher Schlüssel gefunden."
hinweis "Bequemer Weg — auf dem RECHNER ausführen:"
hinweis " adb push ~/.ssh/id_ed25519.pub $SCHLUESSEL_ABLAGE"
hinweis " (oder ~/.ssh/id_rsa.pub) und dieses Skript erneut aufrufen."
echo
printf ' Stattdessen jetzt ein Passwort setzen? [j/N] > '
read -r MIT_PASSWORT
case "${MIT_PASSWORT:-n}" in
j|J) passwd || { fehl "Passwort nicht gesetzt"; exit 1; } ;;
*) fehl "Ohne Schlüssel oder Passwort ist keine Anmeldung möglich."; exit 1 ;;
esac
fi
# ── Dienst ───────────────────────────────────────────────────────────────────
kopf "sshd"
pkill sshd 2>/dev/null && hinweis "alte Instanz beendet"
sleep 1
if sshd; then ok "läuft auf Port 8022"; else fehl "Start fehlgeschlagen"; exit 1; fi
# ── Adresse ──────────────────────────────────────────────────────────────────
kopf "Verbinden"
IP=$(ip -4 addr show 2>/dev/null | awk '/inet .*(wlan|swlan)/ {print $2}' | cut -d/ -f1 | head -1)
[ -z "$IP" ] && IP=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')
BENUTZER=$(whoami)
cat <<TEXT
Vom Rechner aus:
ssh -p 8022 ${BENUTZER}@${IP:-<telefon-ip>}
Dateien im Editor des Rechners bearbeiten:
mkdir -p ~/telefon
sshfs -p 8022 ${BENUTZER}@${IP:-<telefon-ip>}:/data/data/com.termux/files/home ~/telefon
# lösen mit: fusermount3 -u ~/telefon
Gut zu wissen:
· sshd überlebt keinen Neustart von Termux — dann dieses Skript erneut,
oder schlicht 'sshd' aufrufen.
· Port 8022 statt 22, weil eine gewöhnliche App keine Ports unter 1024
belegen darf.
· Der Dienst ist im ganzen WLAN erreichbar. In fremden Netzen abschalten:
pkill sshd
TEXT
+141
View File
@@ -0,0 +1,141 @@
#!/usr/bin/env bash
# HPOS — scrcpy installieren. Läuft auf dem RECHNER, nicht auf dem Telefon.
#
# scrcpy ist in Debian 12 nicht enthalten; erst ab trixie-backports, und dort in
# contrib statt main. 'apt install scrcpy' scheitert deshalb mit "Paket kann nicht
# gefunden werden". Das Projekt liefert aber ein eigenständiges Linux-Paket, das
# ohne Systemabhängigkeiten und ohne root auskommt.
#
# Installiert wird nach ~/.local — nichts außerhalb des Benutzerverzeichnisses.
#
# Aufruf: ./tools/install-scrcpy.sh [--deinstallieren]
set -uo pipefail
ZIEL="$HOME/.local/opt/scrcpy"
STARTER="$HOME/.local/bin/scrcpy"
if [ -t 1 ]; then
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'
C_HEAD=$'\033[1;36m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
else
C_OK=""; C_WARN=""; C_ERR=""; C_HEAD=""; C_DIM=""; C_OFF=""
fi
kopf() { printf '\n%s══ %s ══%s\n' "$C_HEAD" "$*" "$C_OFF"; }
ok() { printf '%s ✓%s %s\n' "$C_OK" "$C_OFF" "$*"; }
warn() { printf '%s !%s %s\n' "$C_WARN" "$C_OFF" "$*"; }
fehl() { printf '%s ✗%s %s\n' "$C_ERR" "$C_OFF" "$*"; }
hinweis() { printf '%s %s%s\n' "$C_DIM" "$*" "$C_OFF"; }
if [ "${1:-}" = "--deinstallieren" ]; then
rm -rf "$ZIEL" "$STARTER"
ok "Entfernt: $ZIEL und $STARTER"
exit 0
fi
command -v curl >/dev/null 2>&1 || { fehl "curl wird gebraucht."; exit 1; }
command -v tar >/dev/null 2>&1 || { fehl "tar wird gebraucht."; exit 1; }
case "$(uname -m)" in
x86_64|amd64) ARCH=x86_64 ;;
aarch64|arm64) ARCH=aarch64 ;;
*) fehl "Keine fertigen Pakete für $(uname -m). Dann aus den Quellen bauen."; exit 1 ;;
esac
# ── Herunterladen ────────────────────────────────────────────────────────────
kopf "Aktuelle Fassung ermitteln"
URL=$(curl -sfL https://api.github.com/repos/Genymobile/scrcpy/releases/latest \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for a in d['assets']:
if a['name'].startswith('scrcpy-linux-$ARCH') and a['name'].endswith('.tar.gz'):
print(a['browser_download_url'])
break
" 2>/dev/null)
if [ -z "$URL" ]; then
fehl "Kein passendes Paket für linux-$ARCH gefunden."
hinweis "Releases prüfen: https://github.com/Genymobile/scrcpy/releases"
exit 1
fi
ok "$(basename "$URL")"
kopf "Herunterladen"
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
if ! curl -fL --progress-bar -o "$TMP/s.tar.gz" "$URL"; then
fehl "Download fehlgeschlagen."
exit 1
fi
tar xzf "$TMP/s.tar.gz" -C "$TMP" || { fehl "Entpacken fehlgeschlagen."; exit 1; }
QUELLE=$(find "$TMP" -maxdepth 1 -type d -name 'scrcpy-linux-*' | head -1)
[ -n "$QUELLE" ] || { fehl "Unerwarteter Aufbau des Archivs."; exit 1; }
kopf "Installieren"
rm -rf "$ZIEL"
mkdir -p "$(dirname "$ZIEL")" "$(dirname "$STARTER")"
mv "$QUELLE" "$ZIEL"
chmod +x "$ZIEL/scrcpy" "$ZIEL/adb" 2>/dev/null || true
ok "nach $ZIEL"
# ── Starter mit passendem adb ────────────────────────────────────────────────
# Das Paket bringt ein eigenes adb mit, das neuer sein kann als das im System.
# Trifft ein anderes adb auf einen laufenden Server, beendet es ihn — und genau
# das kappt eine bestehende WLAN-Verbindung zum Telefon mitten in der Arbeit.
# Deshalb bekommt scrcpy dasselbe adb wie die übrigen Skripte.
SYSTEM_ADB=""
for k in "${ADB:-}" "$HOME/Android/Sdk/platform-tools/adb" $(command -v adb 2>/dev/null); do
[ -n "$k" ] && [ -x "$k" ] && { SYSTEM_ADB="$k"; break; }
done
if [ -n "$SYSTEM_ADB" ]; then
V_SYS=$("$SYSTEM_ADB" version 2>/dev/null | sed -n 2p)
V_MIT=$("$ZIEL/adb" version 2>/dev/null | sed -n 2p)
hinweis "System-adb: $V_SYS"
hinweis "Mitgeliefert: $V_MIT"
if [ "$V_SYS" != "$V_MIT" ]; then
warn "Verschiedene Fassungen — scrcpy bekommt das System-adb,"
hinweis "sonst würde der adb-Server neu starten und die WLAN-Verbindung abreißen."
fi
fi
cat >"$STARTER" <<STARTER_ENDE
#!/bin/sh
# Erzeugt von tools/install-scrcpy.sh — nicht von Hand ändern.
# Setzt bewusst dasselbe adb wie die Provisioning-Skripte: ein abweichendes
# beendet den laufenden adb-Server und trennt damit das Telefon.
[ -n "\${ADB:-}" ] || ADB="${SYSTEM_ADB:-$ZIEL/adb}"
export ADB
export SCRCPY_SERVER_PATH="$ZIEL/scrcpy-server"
exec "$ZIEL/scrcpy" "\$@"
STARTER_ENDE
chmod +x "$STARTER"
ok "Starter: $STARTER"
# ── Prüfen ───────────────────────────────────────────────────────────────────
kopf "Prüfen"
if FEHLT=$(ldd "$ZIEL/scrcpy" 2>/dev/null | grep 'not found'); then
fehl "Fehlende Bibliotheken:"
printf '%s\n' "$FEHLT" | sed 's/^/ /'
exit 1
fi
ok "Keine fehlenden Bibliotheken"
ok "$("$STARTER" --version 2>&1 | head -1)"
case ":$PATH:" in
*":$HOME/.local/bin:"*) ok "~/.local/bin liegt in PATH" ;;
*) warn "~/.local/bin fehlt in PATH"
hinweis "In ~/.bashrc ergänzen: export PATH=\"\$HOME/.local/bin:\$PATH\"" ;;
esac
kopf "Loslegen"
cat <<'TEXT'
scrcpy Bildschirm spiegeln, Zwischenablage beidseitig
scrcpy --max-size 1024 weniger Bandbreite, flüssiger über WLAN
scrcpy --turn-screen-off Telefonbildschirm aus, spart Akku
scrcpy --stay-awake Bildschirm bleibt an, solange gespiegelt wird
Tastatur und Maus des Rechners wirken direkt im Telefon — auch in Termux.
Deinstallieren: ./tools/install-scrcpy.sh --deinstallieren
TEXT