git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

verify/clients/src/bin/or_flip.rs (2.7K)

 1 // or-flip — an X11 window that stops being override-redirect between maps.
 2 //
 3 // Maps a window with override_redirect set, unmaps it, clears the flag and
 4 // maps it again, then keeps it up for a while. The second MapNotify carries
 5 // the changed flag, so wlroots emits `set_override_redirect` and the
 6 // compositor turns its override-redirect record into a managed window. An
 7 // XEmbed tray handing an icon back to the root window makes Wine do exactly
 8 // this, which is how the compositor crashed on 2026-09-26: the record was
 9 // freed but left in `wm.override_redirects`, and the next frame read it.
10 //
11 // Prints one line per step. Args: --hold SECS (default 3), --cycles N
12 // (default 1; each cycle flips OR on and off again).
13 
14 use std::time::Duration;
15 
16 use x11rb::connection::Connection;
17 use x11rb::protocol::xproto::*;
18 use x11rb::wrapper::ConnectionExt as _;
19 
20 fn main() {
21     let mut hold = 3.0f64;
22     let mut cycles = 1u32;
23     let mut args = std::env::args().skip(1);
24     while let Some(a) = args.next() {
25         match a.as_str() {
26             "--hold" => hold = args.next().unwrap().parse().unwrap(),
27             "--cycles" => cycles = args.next().unwrap().parse().unwrap(),
28             other => panic!("unknown arg {other}"),
29         }
30     }
31     let (conn, screen_num) = x11rb::connect(None).expect("X connection");
32     let screen = conn.setup().roots[screen_num].clone();
33     let win = conn.generate_id().unwrap();
34     conn.create_window(
35         screen.root_depth,
36         win,
37         screen.root,
38         50,
39         50,
40         120,
41         90,
42         0,
43         WindowClass::INPUT_OUTPUT,
44         screen.root_visual,
45         &CreateWindowAux::new().background_pixel(screen.white_pixel).override_redirect(1),
46     )
47     .unwrap();
48     conn.change_property8(PropMode::REPLACE, win, AtomEnum::WM_NAME, AtomEnum::STRING, b"or-flip").unwrap();
49     let pause = |s: f64| std::thread::sleep(Duration::from_secs_f64(s));
50     for cycle in 0..cycles {
51         conn.change_window_attributes(win, &ChangeWindowAttributesAux::new().override_redirect(1)).unwrap();
52         conn.map_window(win).unwrap();
53         conn.flush().unwrap();
54         println!("cycle {cycle}: mapped override-redirect");
55         pause(0.5);
56         conn.unmap_window(win).unwrap();
57         conn.change_window_attributes(win, &ChangeWindowAttributesAux::new().override_redirect(0)).unwrap();
58         conn.map_window(win).unwrap();
59         conn.flush().unwrap();
60         println!("cycle {cycle}: remapped managed");
61         pause(0.5);
62         conn.unmap_window(win).unwrap();
63         conn.flush().unwrap();
64         pause(0.2);
65     }
66     conn.map_window(win).unwrap();
67     conn.flush().unwrap();
68     pause(hold);
69     println!("done");
70 }