shell: an owner change is not a visibility event
sm.puri.OSK0 is a name, not a process. Boot, a crash-restart and an osk-switch swap each put a fresh keyboard behind it, and the visibility monitor mirrored the newcomer's opening claim into oskOpen as though the user had asked for a keyboard. Measured: shell up 03:03:38, self-showed at 03:03:43, nobody near the phone. Track the bus owner - gdbus already prints it and the 'Visible' filter was throwing it away - and on a change push oskOpen at the new owner instead of pulling state out of it. A process that just started has no history; the shell is the continuity of intent across the keyboard's lifetime.
This commit is contained in:
parent
bc09480375
commit
d7a04f27d5
1 changed files with 83 additions and 0 deletions
|
|
@ -105,6 +105,63 @@ Scope {
|
||||||
property int maxReasserts: 5
|
property int maxReasserts: 5
|
||||||
property int reassertCount: 0
|
property int reassertCount: 0
|
||||||
|
|
||||||
|
// An owner change is not a visibility event.
|
||||||
|
//
|
||||||
|
// sm.puri.OSK0 is a *name*, not a process. Three different things put a
|
||||||
|
// fresh process behind it: boot, a crash-restart (the keyboards exit
|
||||||
|
// `Error reading events from display: Broken pipe` when their Wayland
|
||||||
|
// connection is perturbed, and both units are Restart=on-failure), and
|
||||||
|
// `osk-switch` swapping squeekboard for stevia at the user's gesture.
|
||||||
|
// Each new process announces its own idea of Visible, and until now this
|
||||||
|
// monitor mirrored that into oskOpen — indistinguishable from the
|
||||||
|
// keyboard deciding to show itself. Measured on 2026-08-05: shell up at
|
||||||
|
// 03:03:38, `[osk] squeekboard self-showed` at 03:03:43, nine seconds
|
||||||
|
// after boot with nobody near the phone.
|
||||||
|
//
|
||||||
|
// A process that just started has no history. It cannot know whether the
|
||||||
|
// user wanted a keyboard. The shell does — oskOpen is the continuity of
|
||||||
|
// that intent across the keyboard's whole lifetime. So on an owner change
|
||||||
|
// we PUSH intent onto the new owner instead of PULLING state out of it,
|
||||||
|
// and we ignore its opening claim while our push is in flight.
|
||||||
|
//
|
||||||
|
// This is also why "the keyboard comes back on wake" needs no compositor
|
||||||
|
// change to stop hurting: wake perturbs the display connection, the OSK
|
||||||
|
// restarts, and it is the mirror — not the compositor and not the
|
||||||
|
// keyboard — that turns that restart into a keyboard on the user's
|
||||||
|
// screen.
|
||||||
|
property string oskOwner: ""
|
||||||
|
property int ownerSettleMs: 2000
|
||||||
|
// NOT a binding on Date.now() — QML bindings do not re-evaluate because
|
||||||
|
// time passed, so a `Date.now() - t < ms` property latches at creation
|
||||||
|
// and never clears. A timer is the only honest way to express "for a
|
||||||
|
// moment after".
|
||||||
|
property bool ownerSettling: false
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: ownerSettleTimer
|
||||||
|
interval: root.ownerSettleMs
|
||||||
|
onTriggered: root.ownerSettling = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function adoptOwner(owner: string): void {
|
||||||
|
if (owner === root.oskOwner) return;
|
||||||
|
const previous = root.oskOwner;
|
||||||
|
root.oskOwner = owner;
|
||||||
|
root.ownerSettling = true;
|
||||||
|
ownerSettleTimer.restart();
|
||||||
|
if (owner === "") {
|
||||||
|
console.log("[osk] keyboard left the bus (was " + previous + ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("[osk] keyboard is now " + owner
|
||||||
|
+ (previous === "" ? "" : " (was " + previous + ")")
|
||||||
|
+ "; re-asserting oskOpen=" + GlobalStates.oskOpen);
|
||||||
|
// Push our intent, not theirs. A fresh keyboard claiming Visible=true
|
||||||
|
// when nobody asked for one gets closed here.
|
||||||
|
if (GlobalStates.oskOpen) root.showOsk();
|
||||||
|
else root.hideOsk();
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: GlobalStates
|
target: GlobalStates
|
||||||
function onOskHoldsChanged() {
|
function onOskHoldsChanged() {
|
||||||
|
|
@ -119,8 +176,34 @@ Scope {
|
||||||
"--dest", "sm.puri.OSK0", "--object-path", "/sm/puri/OSK0"]
|
"--dest", "sm.puri.OSK0", "--object-path", "/sm/puri/OSK0"]
|
||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
onRead: line => {
|
onRead: line => {
|
||||||
|
// gdbus prints the owner of --dest at startup and again on
|
||||||
|
// every NameOwnerChanged. These lines were being dropped by
|
||||||
|
// the 'Visible' filter below; they are the evidence we need.
|
||||||
|
// The name sm.puri.OSK0 is owned by :1.41
|
||||||
|
// The name sm.puri.OSK0 does not have an owner
|
||||||
|
const owned = line.match(/is owned by (\S+)/);
|
||||||
|
if (owned) {
|
||||||
|
root.adoptOwner(owned[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (line.includes("does not have an owner")) {
|
||||||
|
root.adoptOwner("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!line.includes("'Visible'")) return;
|
if (!line.includes("'Visible'")) return;
|
||||||
const vis = line.includes("<true>");
|
const vis = line.includes("<true>");
|
||||||
|
|
||||||
|
// Still inside an owner change: this is the new process
|
||||||
|
// introducing itself, or the echo of the intent we just
|
||||||
|
// pushed at it. Either way it is not the user speaking.
|
||||||
|
if (root.ownerSettling) {
|
||||||
|
if (vis !== GlobalStates.oskOpen)
|
||||||
|
console.log("[osk] ignoring Visible=" + vis
|
||||||
|
+ " from freshly-arrived " + root.oskOwner
|
||||||
|
+ "; oskOpen=" + GlobalStates.oskOpen + " stands");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!vis && GlobalStates.oskHolds.length > 0) {
|
if (!vis && GlobalStates.oskHolds.length > 0) {
|
||||||
if (root.reassertCount >= root.maxReasserts) {
|
if (root.reassertCount >= root.maxReasserts) {
|
||||||
console.log("[osk] squeekboard self-hid under hold ["
|
console.log("[osk] squeekboard self-hid under hold ["
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue