From a71b7f8baa609ac0b5501aa2a34ac42dfd5b7d53 Mon Sep 17 00:00:00 2001 From: vasilito Date: Fri, 10 Jul 2026 13:47:09 +0300 Subject: [PATCH] compositor+evdevd: eliminate remaining disguised stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- .../system/evdevd/source/src/scheme.rs | 40 ++++- .../source/src/display_backend.rs | 4 +- .../redbear-compositor/source/src/handlers.rs | 20 ++- .../redbear-compositor/source/src/main.rs | 155 +++++++++++++++--- .../redbear-compositor/source/src/state.rs | 4 +- 5 files changed, 187 insertions(+), 36 deletions(-) diff --git a/local/recipes/system/evdevd/source/src/scheme.rs b/local/recipes/system/evdevd/source/src/scheme.rs index 0f9ce33fa1..b1b98e04d6 100644 --- a/local/recipes/system/evdevd/source/src/scheme.rs +++ b/local/recipes/system/evdevd/source/src/scheme.rs @@ -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, + /// 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::(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; diff --git a/local/recipes/wayland/redbear-compositor/source/src/display_backend.rs b/local/recipes/wayland/redbear-compositor/source/src/display_backend.rs index c39fa718f8..8581d61a8e 100644 --- a/local/recipes/wayland/redbear-compositor/source/src/display_backend.rs +++ b/local/recipes/wayland/redbear-compositor/source/src/display_backend.rs @@ -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); } diff --git a/local/recipes/wayland/redbear-compositor/source/src/handlers.rs b/local/recipes/wayland/redbear-compositor/source/src/handlers.rs index 157102bdbc..2e1fadbb1f 100644 --- a/local/recipes/wayland/redbear-compositor/source/src/handlers.rs +++ b/local/recipes/wayland/redbear-compositor/source/src/handlers.rs @@ -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, diff --git a/local/recipes/wayland/redbear-compositor/source/src/main.rs b/local/recipes/wayland/redbear-compositor/source/src/main.rs index bfab1774e0..bba77b07ce 100644 --- a/local/recipes/wayland/redbear-compositor/source/src/main.rs +++ b/local/recipes/wayland/redbear-compositor/source/src/main.rs @@ -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, viewporters: HashMap, presentation_feedback: HashMap, + regions: HashMap, acked_global_removals: HashSet, _next_id: u32, } @@ -1107,6 +1109,59 @@ struct PresentationFeedbackState { last_feedback_serial: Option, } +/// 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, + subtracted: Vec, +} + +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 { + 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 {}", diff --git a/local/recipes/wayland/redbear-compositor/source/src/state.rs b/local/recipes/wayland/redbear-compositor/source/src/state.rs index e560a58e56..2a54ba8404 100644 --- a/local/recipes/wayland/redbear-compositor/source/src/state.rs +++ b/local/recipes/wayland/redbear-compositor/source/src/state.rs @@ -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(_) => {} } } }