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 }