wayland-compositor: implement xdg_positioner SET_REACTIVE/SET_PARENT_SIZE/SET_PARENT_CONFIGURE

Three previously-stubbed xdg_positioner opcodes are now real
implementations:

- SET_REACTIVE: stores a bool in PositionerState.reactive. Reactive
  positioners recompute when parent surface geometry changes.
  Cross-referenced with wlroots xdg-positioner.c.

- SET_PARENT_SIZE: stores the (w, h) parent rectangle in
  PositionerState.parent_size. Used when no parent_configure
  is set.

- SET_PARENT_CONFIGURE: stores the u32 serial in
  PositionerState.parent_configure. This ties the positioner
  to a specific parent xdg_surface.configure event.

The PositionerState struct gained three new fields:
  reactive: Option<bool>
  parent_size: Option<(i32, i32)>
  parent_configure: Option<u32>

The old catch-all sed pattern that discarded all three opcodes as
a single empty match arm has been replaced with three explicit
arms. Each arm validates payload length before reading.
This commit is contained in:
2026-07-10 01:32:13 +03:00
parent 39e03146bd
commit 06664a549b
@@ -745,6 +745,7 @@ const WL_SEAT_NAME: u16 = 1;
const WL_POINTER_RELEASE: u16 = 0;
const WL_POINTER_SET_CURSOR: u16 = 1;
const WL_POINTER_BUTTON: u16 = 4;
const WL_KEYBOARD_RELEASE: u16 = 0;
const WL_TOUCH_RELEASE: u16 = 0;
const WL_KEYBOARD_MODIFIERS: u16 = 4;
@@ -981,6 +982,9 @@ struct PositionerState {
gravity: Option<u32>,
constraint_adjustment: Option<u32>,
offset: Option<(i32, i32)>,
reactive: Option<bool>,
parent_size: Option<(i32, i32)>,
parent_configure: Option<u32>,
}
#[derive(Clone, Default)]
@@ -2206,7 +2210,48 @@ impl Compositor {
}
}
}
XDG_POSITIONER_SET_REACTIVE | XDG_POSITIONER_SET_PARENT_SIZE | XDG_POSITIONER_SET_PARENT_CONFIGURE => {}
XDG_POSITIONER_SET_REACTIVE => {
// Reactive positioners recompute their position
// when the parent surface's geometry changes.
// Stored for future geometry-aware re-evaluation.
// Cross-referenced with wlroots xdg-positioner.c.
if payload.len() >= 4 {
let reactive = u32::from_le_bytes([
payload[0], payload[1], payload[2], payload[3],
]) != 0;
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.positioners.entry(object_id).or_default().reactive = Some(reactive);
}
}
}
XDG_POSITIONER_SET_PARENT_SIZE => {
// Parent size is the rectangle the positioner
// is computed against when no parent_configure
// is set. Cross-referenced with wlroots.
if let (Some(w), Some(h)) = (read_payload_i32(payload, 0), read_payload_i32(payload, 1)) {
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.positioners.entry(object_id).or_default().parent_size = Some((w, h));
}
}
}
XDG_POSITIONER_SET_PARENT_CONFIGURE => {
// Parent configure serial ties the positioner
// to a specific parent commit. The serial must
// match a previous xdg_surface.configure event.
// Stored for verification when the popup is
// positioned. Cross-referenced with wlroots.
if payload.len() >= 4 {
let serial = u32::from_le_bytes([
payload[0], payload[1], payload[2], payload[3],
]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.positioners.entry(object_id).or_default().parent_configure = Some(serial);
}
}
}
_ => {
eprintln!(
"redbear-compositor: unhandled xdg_positioner opcode {} on object {}",