mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-13 12:23:38 +01:00
fix(e2e-agent): read the displays of the macOS guest in a process of their own
NSScreen keeps the visible frame it had when the process started: turning the run loop does not refresh it, and the agent, which waits in accept, is up for the whole run. The Dock of the guest shrinks its icons once the first test windows are open (1280 points are not wide enough for the persistent apps and the windows), its height going from 74 to 65 points, so the work area gains 9 points at the bottom, and the agent kept saying y 30, height 696 while a fresh process said 705. The daemon reads the screens as it starts, so a shared check that took the work area from the agent held the daemon to an area that was not the system's, and the mirror checks of layout_flip were off by the difference. The verb now runs the agent again with --print-displays, which prints what a new process sees and exits, and answers in process only if that fails.
This commit is contained in:
1 parent
55339cfcb3
commit
fb5f23a67a
1 file changed
+31
-1
@@ -213,6 +213,28 @@ fn displays(mtm: MainThreadMarker) -> Vec<Value> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The displays as a process that has just started sees them.
|
||||
///
|
||||
/// `NSScreen` caches the visible frame in the process, and turning the run loop does not refresh
|
||||
/// it: an agent that has been up for a while keeps answering with the work area of the time it
|
||||
/// started. The Dock of a guest is that kind of change: with the room it has on a display of 1280
|
||||
/// points it shrinks its icons, and the work area gains 9 points, once the first test windows have
|
||||
/// opened. The daemon reads the screens as it starts, so the work area it is held to is the one
|
||||
/// that the system has now, and only a process of its own can say what that is.
|
||||
fn fresh_displays(mtm: MainThreadMarker) -> Vec<Value> {
|
||||
let asked = std::env::current_exe()
|
||||
.and_then(|exe| {
|
||||
Command::new(exe)
|
||||
.arg(DISPLAYS_ARG)
|
||||
.stdin(Stdio::null())
|
||||
.output()
|
||||
})
|
||||
.ok()
|
||||
.filter(|output| output.status.success())
|
||||
.and_then(|output| serde_json::from_slice::<Vec<Value>>(&output.stdout).ok());
|
||||
asked.unwrap_or_else(|| displays(mtm))
|
||||
}
|
||||
|
||||
fn activate(title: &str) -> Result<Value, String> {
|
||||
let window = find(title)?;
|
||||
if bool_attribute(&window.element, "AXMinimized") {
|
||||
@@ -285,7 +307,7 @@ impl Verbs for MacosVerbs {
|
||||
fn handle(&mut self, request: &Request) -> Option<Result<Value, String>> {
|
||||
Some(match request.verb.as_str() {
|
||||
"os_windows" => Ok(Value::Array(windows().iter().map(describe).collect())),
|
||||
"displays" => Ok(Value::Array(displays(self.mtm))),
|
||||
"displays" => Ok(Value::Array(fresh_displays(self.mtm))),
|
||||
"activate" => activate(&request.title),
|
||||
"minimize" => find(&request.title)
|
||||
.and_then(|w| set_bool(&w.element, "AXMinimized", true))
|
||||
@@ -297,12 +319,20 @@ impl Verbs for MacosVerbs {
|
||||
}
|
||||
}
|
||||
|
||||
/// The argument that makes the agent print its displays and exit, as `fresh_displays` asks.
|
||||
const DISPLAYS_ARG: &str = "--print-displays";
|
||||
|
||||
pub fn run() -> std::io::Result<()> {
|
||||
let mtm = MainThreadMarker::new().expect("main runs on the main thread");
|
||||
// the window server connection that NSScreen needs, with no Dock icon and no window
|
||||
let application = NSApplication::sharedApplication(mtm);
|
||||
application.setActivationPolicy(NSApplicationActivationPolicy::Accessory);
|
||||
|
||||
if std::env::args().nth(1).as_deref() == Some(DISPLAYS_ARG) {
|
||||
println!("{}", Value::Array(displays(mtm)));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let listener = TcpListener::bind(("0.0.0.0", crate::protocol::port()))?;
|
||||
let mut verbs = MacosVerbs { mtm };
|
||||
for stream in listener.incoming() {
|
||||
|
||||
Reference in new issue
Block a user