compositor+evdevd: eliminate remaining disguised stubs
Compositor: - XDG_POPUP_GRAB: read serial from payload (was generating fake serial) - XDG_POPUP_REPOSITION: fix payload parsing, look up positioner state, send configure+repositioned events with correct token - WL_SUBSURFACE_PLACE_ABOVE/BELOW: implement z_index tracking for real subsurface stacking order - Unknown global bind: log warning instead of silent type-0 assignment - DRM page flip: log ioctl errors instead of silently ignoring - handlers.rs: 4 silent message drops replaced with eprintln warnings - Shell(_) ack_configure: clean up code smell (let _ = serial) evdevd: - F_GETFL/F_SETFL: store and return file status flags on Handle - F_GETFD/F_SETFD: store and return fd flags on Handle - EVIOCSCLOCKID: read, validate (0-2), and store clock_id on device handle relibc submodule pointer update (dso.rs catch-alls → log+skip).
This commit is contained in:
@@ -37,6 +37,10 @@ struct TouchSlot {
|
||||
|
||||
struct Handle {
|
||||
kind: HandleKind,
|
||||
/// File status flags from fcntl(F_SETFL) — e.g. O_NONBLOCK, O_APPEND.
|
||||
flags: usize,
|
||||
/// File descriptor flags from fcntl(F_SETFD) — e.g. FD_CLOEXEC.
|
||||
fd_flags: usize,
|
||||
}
|
||||
|
||||
enum HandleKind {
|
||||
@@ -44,6 +48,9 @@ enum HandleKind {
|
||||
Device {
|
||||
device_idx: usize,
|
||||
events: VecDeque<InputEvent>,
|
||||
/// Clock source for event timestamps, set via EVIOCSCLOCKID.
|
||||
/// 0 = CLOCK_REALTIME (default), 1 = CLOCK_MONOTONIC, 2 = CLOCK_BOOTTIME.
|
||||
clock_id: i32,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -80,7 +87,7 @@ impl EvdevScheme {
|
||||
};
|
||||
scheme
|
||||
.handles
|
||||
.insert(ROOT_ID, Handle { kind: HandleKind::Root });
|
||||
.insert(ROOT_ID, Handle { kind: HandleKind::Root, flags: 0, fd_flags: 0 });
|
||||
scheme.devices.push(InputDevice::new_keyboard(0));
|
||||
scheme.devices.push(InputDevice::new_mouse(1));
|
||||
scheme.devices.push(InputDevice::new_touchpad(2));
|
||||
@@ -127,6 +134,7 @@ impl EvdevScheme {
|
||||
if let HandleKind::Device {
|
||||
device_idx: handle_device_idx,
|
||||
events: handle_events,
|
||||
clock_id: _,
|
||||
} = &mut handle.kind
|
||||
{
|
||||
if *handle_device_idx == device_idx {
|
||||
@@ -427,6 +435,7 @@ impl SchemeSync for EvdevScheme {
|
||||
HandleKind::Device {
|
||||
device_idx: idx,
|
||||
events: VecDeque::new(),
|
||||
clock_id: 0,
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
@@ -434,7 +443,7 @@ impl SchemeSync for EvdevScheme {
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle { kind });
|
||||
self.handles.insert(id, Handle { kind, flags: 0, fd_flags: 0 });
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
@@ -536,9 +545,22 @@ impl SchemeSync for EvdevScheme {
|
||||
};
|
||||
|
||||
match cmd_raw {
|
||||
F_GETFL => return Ok(O_RDONLY),
|
||||
F_GETFD => return Ok(0),
|
||||
F_SETFL | F_SETFD => {
|
||||
F_GETFL => {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
return Ok(handle.flags | O_RDONLY);
|
||||
}
|
||||
F_GETFD => {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
return Ok(handle.fd_flags);
|
||||
}
|
||||
F_SETFL => {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.flags = arg;
|
||||
return Ok(0);
|
||||
}
|
||||
F_SETFD => {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.fd_flags = arg;
|
||||
return Ok(0);
|
||||
}
|
||||
_ => {}
|
||||
@@ -569,6 +591,14 @@ impl SchemeSync for EvdevScheme {
|
||||
}
|
||||
|
||||
if cmd_raw == EVIOCSCLOCKID as usize {
|
||||
let clock_id = unsafe { Self::read_value_from_user::<i32>(arg)? };
|
||||
if !matches!(clock_id, 0..=2) {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
match &mut self.handles.get_mut(&id).ok_or(Error::new(EBADF))?.kind {
|
||||
HandleKind::Device { clock_id: ref mut cid, .. } => *cid = clock_id,
|
||||
HandleKind::Root => return Err(Error::new(EINVAL)),
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
let cmd = cmd_raw as u64;
|
||||
|
||||
@@ -434,7 +434,9 @@ mod drm_backend {
|
||||
let mut buf = Vec::with_capacity(12);
|
||||
buf.extend_from_slice(&(DRM_IOCTL_MODE_PAGE_FLIP as u64).to_le_bytes());
|
||||
buf.extend_from_slice(&fb_id.to_le_bytes());
|
||||
let _ = (&self.drm_file).write_all(&buf);
|
||||
if let Err(e) = (&self.drm_file).write_all(&buf) {
|
||||
eprintln!("redbear-compositor: DRM page flip ioctl failed: {}", e);
|
||||
}
|
||||
self.current.store(next, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,9 @@ impl Compositor {
|
||||
shell_surface.class = Some(class);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
eprintln!("redbear-compositor: wl_shell_surface opcode {} dropped (short payload {} bytes)", opcode, payload.len());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(surface) = client.surfaces.get_mut(&shell_surface.surface_id) {
|
||||
@@ -217,7 +219,9 @@ impl Compositor {
|
||||
payload[0], payload[1], payload[2], payload[3],
|
||||
]));
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
eprintln!("redbear-compositor: wl_data_source opcode {} dropped (short payload {} bytes)", opcode, payload.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,7 +270,9 @@ impl Compositor {
|
||||
]);
|
||||
device.selection_source = (source_id != 0).then_some(source_id);
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
eprintln!("redbear-compositor: wl_data_device opcode {} dropped (short payload {} bytes)", opcode, payload.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,10 +376,12 @@ impl Compositor {
|
||||
opcode == protocol::WL_SUBSURFACE_PLACE_ABOVE,
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
eprintln!("redbear-compositor: wl_subsurface opcode {} dropped (short payload {} bytes)", opcode, payload.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((surface_id, sibling_surface_id, place_above)) = restack {
|
||||
|
||||
if let Some((surface_id, sibling_surface_id, place_above)) = restack {
|
||||
Compositor::stack_surface_relative(
|
||||
client,
|
||||
surface_id,
|
||||
|
||||
@@ -971,7 +971,7 @@ impl SurfaceRole {
|
||||
match self {
|
||||
SurfaceRole::Toplevel(s) => s.configure.ack(serial),
|
||||
SurfaceRole::Popup(s) => s.configure.ack(serial),
|
||||
SurfaceRole::Shell(_) => { let _ = serial; }
|
||||
SurfaceRole::Shell(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1017,6 +1017,7 @@ struct SubsurfaceState {
|
||||
x: i32,
|
||||
y: i32,
|
||||
sync: bool,
|
||||
z_index: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@@ -1070,6 +1071,7 @@ struct ClientState {
|
||||
dmabuf_params: HashMap<u32, DmabufParamsState>,
|
||||
viewporters: HashMap<u32, ViewportState>,
|
||||
presentation_feedback: HashMap<u32, PresentationFeedbackState>,
|
||||
regions: HashMap<u32, RegionState>,
|
||||
acked_global_removals: HashSet<u32>,
|
||||
_next_id: u32,
|
||||
}
|
||||
@@ -1107,6 +1109,59 @@ struct PresentationFeedbackState {
|
||||
last_feedback_serial: Option<u32>,
|
||||
}
|
||||
|
||||
/// Wayland `wl_region` payload: x, y, width, height in surface-local coords.
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
struct Rect {
|
||||
x: i32,
|
||||
y: i32,
|
||||
w: i32,
|
||||
h: i32,
|
||||
}
|
||||
|
||||
/// wl_region add/subtract rectangle lists. The effective region is the union
|
||||
/// of `added` minus the union of `subtracted` (Wayland protocol semantics).
|
||||
#[derive(Clone, Default)]
|
||||
struct RegionState {
|
||||
added: Vec<Rect>,
|
||||
subtracted: Vec<Rect>,
|
||||
}
|
||||
|
||||
impl RegionState {
|
||||
/// Point-in-region test: inside any added rect AND not inside any subtracted rect.
|
||||
fn contains_point(&self, px: i32, py: i32) -> bool {
|
||||
let in_added = self.added.iter().any(|r| {
|
||||
px >= r.x && px < r.x + r.w && py >= r.y && py < r.y + r.h
|
||||
});
|
||||
if !in_added {
|
||||
return false;
|
||||
}
|
||||
!self.subtracted.iter().any(|r| {
|
||||
px >= r.x && px < r.x + r.w && py >= r.y && py < r.y + r.h
|
||||
})
|
||||
}
|
||||
|
||||
fn bounding_box(&self) -> Option<Rect> {
|
||||
let mut min_x = i32::MAX;
|
||||
let mut min_y = i32::MAX;
|
||||
let mut max_x = i32::MIN;
|
||||
let mut max_y = i32::MIN;
|
||||
for r in self.added.iter().filter(|r| r.w > 0 && r.h > 0) {
|
||||
min_x = min_x.min(r.x);
|
||||
min_y = min_y.min(r.y);
|
||||
max_x = max_x.max(r.x + r.w);
|
||||
max_y = max_y.max(r.y + r.h);
|
||||
}
|
||||
if max_x <= min_x || max_y <= min_y {
|
||||
return None;
|
||||
}
|
||||
Some(Rect { x: min_x, y: min_y, w: max_x - min_x, h: max_y - min_y })
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.added.iter().all(|r| r.w <= 0 || r.h <= 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Compositor {
|
||||
listener: UnixListener,
|
||||
next_id: AtomicU32,
|
||||
@@ -1287,6 +1342,7 @@ impl Compositor {
|
||||
dmabuf_params: HashMap::new(),
|
||||
viewporters: HashMap::new(),
|
||||
presentation_feedback: HashMap::new(),
|
||||
regions: HashMap::new(),
|
||||
acked_global_removals: HashSet::new(),
|
||||
_next_id: 1,
|
||||
},
|
||||
@@ -1491,7 +1547,13 @@ impl Compositor {
|
||||
"zwp_linux_dmabuf_v1" => OBJECT_TYPE_ZWP_LINUX_DMABUF_V1,
|
||||
"wp_viewporter" => OBJECT_TYPE_WP_VIEWPORTER,
|
||||
"wp_presentation" => OBJECT_TYPE_WP_PRESENTATION,
|
||||
_ => 0,
|
||||
_ => {
|
||||
eprintln!(
|
||||
"redbear-compositor: unknown global interface '{}' bound to object {}",
|
||||
iface, new_id
|
||||
);
|
||||
0
|
||||
}
|
||||
};
|
||||
client.objects.insert(new_id, type_id);
|
||||
client.object_versions.insert(new_id, object_version);
|
||||
@@ -1552,6 +1614,7 @@ impl Compositor {
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
client.objects.insert(region_id, OBJECT_TYPE_WL_REGION);
|
||||
client.regions.insert(region_id, RegionState::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2291,9 +2354,9 @@ impl Compositor {
|
||||
self.send_delete_id(stream, object_id);
|
||||
}
|
||||
XDG_POPUP_GRAB => {
|
||||
if let Some(seat_id) = read_payload_u32(payload, 0) {
|
||||
let _ = seat_id;
|
||||
let serial = self.next_serial();
|
||||
if payload.len() >= 8 {
|
||||
let _seat_id = read_payload_u32(payload, 0).unwrap_or(0);
|
||||
let serial = read_payload_u32(payload, 1).unwrap_or(0);
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
for surface in client.surfaces.values_mut() {
|
||||
@@ -2308,26 +2371,28 @@ impl Compositor {
|
||||
}
|
||||
}
|
||||
XDG_POPUP_REPOSITION => {
|
||||
if let Some(token) = read_payload_u32(payload, 0) {
|
||||
let _ = token;
|
||||
// Reposition request: re-evaluate the popup position using the
|
||||
// stored positioner state and send xdg_popup.repositioned(token).
|
||||
// Cross-referenced with wlroots xdg-shell.c: handle_popup_reposition().
|
||||
let serial = self.next_serial();
|
||||
if payload.len() >= 8 {
|
||||
let positioner_id = read_payload_u32(payload, 0).unwrap_or(0);
|
||||
let token = read_payload_u32(payload, 1).unwrap_or(0);
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
let pos_state = client.positioners.get(&positioner_id).cloned();
|
||||
if let Some(ps) = client.surfaces.values_mut()
|
||||
.filter_map(|s| match &s.role {
|
||||
.filter_map(|s| match &mut s.role {
|
||||
Some(SurfaceRole::Popup(p)) if p.object_id == object_id => Some(p),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
{
|
||||
let _ = ps;
|
||||
ps.positioner_id = Some(positioner_id);
|
||||
if let Some(ref pos) = pos_state {
|
||||
if let Some((w, h)) = pos.size {
|
||||
self.send_xdg_surface_configure(stream, object_id, self.next_serial());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(clients);
|
||||
// Send repositioned event back to client
|
||||
self.send_xdg_popup_repositioned(stream, object_id, token);
|
||||
}
|
||||
}
|
||||
@@ -2385,7 +2450,7 @@ impl Compositor {
|
||||
drop(clients);
|
||||
self.send_delete_id(stream, object_id);
|
||||
}
|
||||
WL_KEYBOARD_KEY | WL_KEYBOARD_ENTER | WL_KEYBOARD_LEAVE | WL_KEYBOARD_MODIFIERS | WL_KEYBOARD_REPEAT_INFO | WL_KEYBOARD_KEYMAP => {
|
||||
WL_KEYBOARD_KEY | WL_KEYBOARD_ENTER | WL_KEYBOARD_LEAVE | WL_KEYBOARD_MODIFIERS | WL_KEYBOARD_REPEAT_INFO => {
|
||||
// Keyboard events dispatched to focused client surface when input daemon is wired.
|
||||
let mut keyboard_state = self.keyboard_state.lock().unwrap();
|
||||
if opcode == WL_KEYBOARD_KEY {
|
||||
@@ -2620,7 +2685,6 @@ impl Compositor {
|
||||
self.send_linux_dmabuf_format(stream, object_id, DRM_FORMAT_ARGB8888);
|
||||
}
|
||||
}
|
||||
ZWP_LINUX_DMABUF_V1_FORMAT | ZWP_LINUX_DMABUF_V1_MODIFIER => {}
|
||||
_ => {
|
||||
eprintln!(
|
||||
"redbear-compositor: unhandled zwp_linux_dmabuf_v1 opcode {} on object {}",
|
||||
@@ -2864,12 +2928,32 @@ impl Compositor {
|
||||
}
|
||||
}
|
||||
WL_SUBSURFACE_PLACE_ABOVE => {
|
||||
// Accepted for protocol compliance. Stacking order requires
|
||||
// full Z-order tracking in the compositor's render pipeline.
|
||||
let _ = read_payload_u32(payload, 0);
|
||||
if let Some(sibling_id) = read_payload_u32(payload, 0) {
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
let sibling_z = client.subsurfaces.values()
|
||||
.find(|ss| ss.surface_id == sibling_id)
|
||||
.map(|ss| ss.z_index)
|
||||
.unwrap_or(0);
|
||||
if let Some(ss) = client.subsurfaces.get_mut(&object_id) {
|
||||
ss.z_index = sibling_z + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WL_SUBSURFACE_PLACE_BELOW => {
|
||||
let _ = read_payload_u32(payload, 0);
|
||||
if let Some(sibling_id) = read_payload_u32(payload, 0) {
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
let sibling_z = client.subsurfaces.values()
|
||||
.find(|ss| ss.surface_id == sibling_id)
|
||||
.map(|ss| ss.z_index)
|
||||
.unwrap_or(0);
|
||||
if let Some(ss) = client.subsurfaces.get_mut(&object_id) {
|
||||
ss.z_index = sibling_z - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WL_SUBSURFACE_SET_SYNC => {
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
@@ -2899,11 +2983,40 @@ impl Compositor {
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
client.objects.remove(&object_id);
|
||||
client.regions.remove(&object_id);
|
||||
}
|
||||
drop(clients);
|
||||
self.send_delete_id(stream, object_id);
|
||||
}
|
||||
WL_REGION_ADD | WL_REGION_SUBTRACT => {}
|
||||
WL_REGION_ADD | WL_REGION_SUBTRACT => {
|
||||
if payload.len() >= 16 {
|
||||
let x = i32::from_le_bytes([
|
||||
payload[0], payload[1], payload[2], payload[3],
|
||||
]);
|
||||
let y = i32::from_le_bytes([
|
||||
payload[4], payload[5], payload[6], payload[7],
|
||||
]);
|
||||
let w = i32::from_le_bytes([
|
||||
payload[8], payload[9], payload[10], payload[11],
|
||||
]);
|
||||
let h = i32::from_le_bytes([
|
||||
payload[12], payload[13], payload[14], payload[15],
|
||||
]);
|
||||
let rect = Rect { x, y, w, h };
|
||||
let mut clients = self.clients.lock().unwrap();
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
let region = client
|
||||
.regions
|
||||
.entry(object_id)
|
||||
.or_insert_with(RegionState::default);
|
||||
match opcode {
|
||||
WL_REGION_ADD => region.added.push(rect),
|
||||
WL_REGION_SUBTRACT => region.subtracted.push(rect),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
eprintln!(
|
||||
"redbear-compositor: unhandled wl_region opcode {} on object {}",
|
||||
|
||||
@@ -115,9 +115,7 @@ impl SurfaceRole {
|
||||
match self {
|
||||
Self::Toplevel(state) => state.configure.ack(serial),
|
||||
Self::Popup(state) => state.configure.ack(serial),
|
||||
Self::Shell(_) => {
|
||||
let _ = serial;
|
||||
}
|
||||
Self::Shell(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user