Explain missing X display when the focus guard refuses Type

This commit is contained in:
baketnk committed 2026-10-04 21:48:06 -04:00
1 parent b3a9d39482
commit 0681d4941a
6 files changed
+22 -3

No files matched your search

+6
View File
@@ -44,3 +44,9 @@ SDL3 and OpenVR SDK sources, builds, tests, stages and packages. The release
commit sets `RELEASE_VERSION` in `scripts/install_payload.py` (then
`python3 scripts/sync-installer.py`); upload the archive, its `.sha256` and
`install.sh` to the GitHub release.
When starting over SSH, supply the verified Gamescope Xwayland `DISPLAY` as well
as `XDG_RUNTIME_DIR` from the user's session. Do not assume `:0` is always the
text destination. Without `DISPLAY`, the focus guard refuses Type and preserves
the review; the status line reports `Input unavailable: DISPLAY missing`.
Launch from Steam/the desktop menu to inherit the normal session environment.
+5 -2
View File
@@ -89,7 +89,9 @@ void Controller::delivery_detail(DeliveryResult result, bool submit) {
}
std::shared_ptr<ControllerFocus> Controller::manual_focus() {
auto focus = focus_factory_();
if (!focus || !focus->arm()) throw std::runtime_error("Current target not verified; review kept");
if (!focus || !focus->arm())
throw std::runtime_error(std::string("Current target not verified: ") +
(focus ? focus->failure() : "focus unavailable") + "; review kept");
return std::shared_ptr<ControllerFocus>(std::move(focus));
}
void Controller::queue_paced(std::string text, bool enter, bool review,
@@ -370,7 +372,8 @@ void Controller::action(UiAction action) {
} catch (const std::exception& e) {
if (session_.state() == State::Recording || session_.state() == State::Transcribing) {
audio_.close(); session_.fail(); armed_focus_.reset(); status_note_.clear();
} else status_note_ = "Input unavailable - check destination";
} else status_note_ = std::string(e.what()).find("DISPLAY missing") != std::string::npos ?
"Input unavailable: DISPLAY missing" : "Input unavailable - check destination";
detail_ = e.what();
}
}
+1
View File
@@ -36,6 +36,7 @@ public:
virtual ~ControllerFocus() = default;
virtual bool arm() = 0;
virtual bool valid() = 0;
virtual const char* failure() const { return "focus unavailable"; }
};
using FocusFactory = std::function<std::unique_ptr<ControllerFocus>()>;
+3 -1
View File
@@ -167,9 +167,11 @@ bool FocusGuard::arm() {
auto& p = *impl_;
if (p.attempted || p.dead) return false;
p.attempted = true;
const char* display = std::getenv("DISPLAY");
if (!display || !*display) { p.reason = "DISPLAY missing"; p.dead = true; return false; }
int screen_number = 0;
p.connection = xcb_connect(nullptr, &screen_number);
if (!p.connection || xcb_connection_has_error(p.connection)) { p.dead = true; return false; }
if (!p.connection || xcb_connection_has_error(p.connection)) { p.reason = "X display unavailable"; p.dead = true; return false; }
const xcb_setup_t* setup = xcb_get_setup(p.connection);
auto iter = xcb_setup_roots_iterator(setup);
for (int i = 0; i < screen_number && iter.rem; ++i) xcb_screen_next(&iter);
+1
View File
@@ -106,6 +106,7 @@ public:
trace_(std::string("focus invalid: ") + guard_.failure());
return false;
}
const char* failure() const override { return guard_.failure(); }
private:
FocusGuard guard_;
DeliveryTrace& trace_;
+6
View File
@@ -33,6 +33,12 @@ private:
int main() {
try {
DisplaySetting isolated_display;
unsetenv("DISPLAY");
FocusGuard missing_display;
CHECK(!missing_display.arm());
CHECK(std::string(missing_display.failure()) == "DISPLAY missing");
CHECK(!missing_display.arm());
setenv("DISPLAY", "frameyap-no-such-display:9876", 1);
FocusGuard guard; // Construction must not connect to X.
CHECK(!guard.valid());
CHECK(!guard.arm()); // No server: fail closed.