Significant render opts
This commit is contained in:
parent
d1c993e295
commit
da9a49ea8d
4 changed files with 169 additions and 154 deletions
6
nohup.out
Normal file
6
nohup.out
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Discord 0.0.128
|
||||||
|
[7453:0309/220751.877593:ERROR:ui/ozone/platform/x11/ozone_platform_x11.cc:250] Missing X server or $DISPLAY
|
||||||
|
[7453:0309/220751.877647:ERROR:ui/aura/env.cc:257] The platform failed to initialize. Exiting.
|
||||||
|
Discord 0.0.128
|
||||||
|
[7931:0309/220902.615647:ERROR:ui/ozone/platform/x11/ozone_platform_x11.cc:250] Missing X server or $DISPLAY
|
||||||
|
[7931:0309/220902.615700:ERROR:ui/aura/env.cc:257] The platform failed to initialize. Exiting.
|
||||||
|
|
@ -155,12 +155,15 @@ impl WoState {
|
||||||
let delta = (event.delta_x(), event.delta_y());
|
let delta = (event.delta_x(), event.delta_y());
|
||||||
|
|
||||||
let current = self.pointer_location;
|
let current = self.pointer_location;
|
||||||
self.pointer_location = (
|
let new_pos: smithay::utils::Point<f64, smithay::utils::Logical> = (
|
||||||
(current.x + delta.0).clamp(0.0, self.output_size.0 as f64),
|
(current.x + delta.0).clamp(0.0, self.output_size.0 as f64),
|
||||||
(current.y + delta.1).clamp(0.0, self.output_size.1 as f64),
|
(current.y + delta.1).clamp(0.0, self.output_size.1 as f64),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
// Hardware is the sole source of truth for pointer position.
|
||||||
|
self.pointer_location = new_pos;
|
||||||
|
|
||||||
// Process active grab (interactive move/resize) before normal dispatch
|
// Process active grab (interactive move/resize) before normal dispatch
|
||||||
if let Some(ref grab) = self.grab_state.clone() {
|
if let Some(ref grab) = self.grab_state.clone() {
|
||||||
match grab {
|
match grab {
|
||||||
|
|
@ -232,10 +235,13 @@ impl WoState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_pointer_motion_abs<B: InputBackend>(&mut self, event: B::PointerMotionAbsoluteEvent) {
|
fn on_pointer_motion_abs<B: InputBackend>(&mut self, event: B::PointerMotionAbsoluteEvent) {
|
||||||
self.pointer_location = event
|
let new_pos: smithay::utils::Point<f64, smithay::utils::Logical> = event
|
||||||
.position_transformed((self.output_size.0 as i32, self.output_size.1 as i32).into())
|
.position_transformed((self.output_size.0 as i32, self.output_size.1 as i32).into())
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
// Hardware is the sole source of truth for pointer position.
|
||||||
|
self.pointer_location = new_pos;
|
||||||
|
|
||||||
// Process active grab (interactive move/resize) before normal dispatch
|
// Process active grab (interactive move/resize) before normal dispatch
|
||||||
if let Some(ref grab) = self.grab_state.clone() {
|
if let Some(ref grab) = self.grab_state.clone() {
|
||||||
match grab {
|
match grab {
|
||||||
|
|
@ -342,20 +348,10 @@ impl WoState {
|
||||||
};
|
};
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
|
||||||
// When the cursor is over an Electron-managed window, the compositor
|
// When the cursor is over an Electron-managed window, the client
|
||||||
// must NOT touch Smithay's pointer state at all. Electron/comraw owns
|
// (comraw) owns event dispatching. The compositor sends the position
|
||||||
// the cursor: it renders the cursor sprite, tracks position inside its
|
// via IPC (above) but does NOT call pointer.motion() — the client's
|
||||||
// canvas, and — when the cursor is over an embedded native Wayland/X11
|
// forwarded pointer_motion action handles that.
|
||||||
// window — sends a ForwardedPointer IPC back to the compositor. That
|
|
||||||
// forwarded path (handle_forwarded_pointer_event) is the ONLY code that
|
|
||||||
// should call pointer.motion() for native windows.
|
|
||||||
//
|
|
||||||
// Previously this block called pointer.motion(None) to "clear" native
|
|
||||||
// focus, but Smithay interprets that as a real motion event and sends
|
|
||||||
// wl_pointer.leave to any focused client. When Electron's forwarded
|
|
||||||
// event arrives a moment later and calls pointer.motion(Some(focus)),
|
|
||||||
// Smithay sends wl_pointer.enter — so the client sees
|
|
||||||
// leave→enter→leave→enter every frame, causing cursor rubberbanding.
|
|
||||||
if under_electron.is_some() && self.electron_ipc.is_some() {
|
if under_electron.is_some() && self.electron_ipc.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -515,8 +511,12 @@ impl WoState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forward a pointer motion event received from the Electron web UI to the
|
/// Forward a pointer motion event from the client (comraw/Electron) to the
|
||||||
/// Wayland surface under the given window-local coordinates.
|
/// Wayland surface under the given window-local coordinates.
|
||||||
|
///
|
||||||
|
/// The client controls event dispatch to windows it renders. However, the
|
||||||
|
/// compositor remains the sole authority on `pointer_location` (hardware
|
||||||
|
/// input) — this function does NOT overwrite it.
|
||||||
pub fn handle_forwarded_pointer_event(&mut self, window_name: &str, x: f64, y: f64) {
|
pub fn handle_forwarded_pointer_event(&mut self, window_name: &str, x: f64, y: f64) {
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::WindowSurfaceType,
|
desktop::WindowSurfaceType,
|
||||||
|
|
@ -535,18 +535,16 @@ impl WoState {
|
||||||
};
|
};
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
|
||||||
// (x, y) from the web UI are relative to the rendered surface buffer
|
|
||||||
// origin (includes CSD decorations/shadows).
|
|
||||||
//
|
|
||||||
// Derive a consistent pointer_location from the window's compositor-
|
|
||||||
// space position so that focus math works correctly. The hardware
|
|
||||||
// pointer_location is meaningless here because it lives in the host
|
|
||||||
// compositor's coordinate space (nested mode) while our Space uses
|
|
||||||
// its own internal coordinates.
|
|
||||||
let win_loc = self.space.element_location(&window).unwrap_or_default();
|
let win_loc = self.space.element_location(&window).unwrap_or_default();
|
||||||
let global_x = win_loc.x as f64 + x;
|
|
||||||
let global_y = win_loc.y as f64 + y;
|
// Compute a synthetic global location from the client-provided
|
||||||
self.pointer_location = Point::from((global_x, global_y));
|
// surface-local coords. This is used ONLY for the MotionEvent so
|
||||||
|
// Smithay derives the correct wl_pointer surface-local coordinates.
|
||||||
|
// pointer_location itself is NOT modified.
|
||||||
|
let dispatch_location: Point<f64, Logical> = (
|
||||||
|
win_loc.x as f64 + x,
|
||||||
|
win_loc.y as f64 + y,
|
||||||
|
).into();
|
||||||
|
|
||||||
let focus = window
|
let focus = window
|
||||||
.surface_under(Point::<f64, Logical>::from((x, y)), WindowSurfaceType::ALL)
|
.surface_under(Point::<f64, Logical>::from((x, y)), WindowSurfaceType::ALL)
|
||||||
|
|
@ -562,7 +560,7 @@ impl WoState {
|
||||||
self,
|
self,
|
||||||
focus,
|
focus,
|
||||||
&MotionEvent {
|
&MotionEvent {
|
||||||
location: self.pointer_location,
|
location: dispatch_location,
|
||||||
serial,
|
serial,
|
||||||
time: 0,
|
time: 0,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
249
src/main.rs
249
src/main.rs
|
|
@ -106,6 +106,10 @@ struct EventLoopData {
|
||||||
/// confirmation. Without this, the CRTC's active scanout buffer could be
|
/// confirmation. Without this, the CRTC's active scanout buffer could be
|
||||||
/// freed prematurely, causing every-other-frame flicker.
|
/// freed prematurely, causing every-other-frame flicker.
|
||||||
pending_slot: Option<Slot<smithay::backend::allocator::gbm::GbmBuffer>>,
|
pending_slot: Option<Slot<smithay::backend::allocator::gbm::GbmBuffer>>,
|
||||||
|
/// Clone of the libinput context for suspend/resume on VT switch.
|
||||||
|
/// The original is consumed by `LibinputInputBackend`; this clone lets us
|
||||||
|
/// call `suspend()` / `resume()` from the session notifier callback.
|
||||||
|
libinput_context: Libinput,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Newtype wrapper around `Option<EventLoopData>` for the DRM calloop event
|
/// Newtype wrapper around `Option<EventLoopData>` for the DRM calloop event
|
||||||
|
|
@ -517,31 +521,34 @@ fn render_frame(data: &mut EventLoopData) {
|
||||||
|
|
||||||
let renderer: &mut GlesRenderer = &mut renderer_guard;
|
let renderer: &mut GlesRenderer = &mut renderer_guard;
|
||||||
|
|
||||||
// Prepare Wayland surface elements before starting the frame
|
// Prepare Wayland surface elements before starting the frame.
|
||||||
|
// When Electron IPC is active, surfaces are exported via SHM/DMABUF
|
||||||
|
// to the client — skip the expensive GL import here.
|
||||||
use smithay::backend::renderer::element::{surface::render_elements_from_surface_tree, surface::WaylandSurfaceRenderElement, Kind, RenderElement};
|
use smithay::backend::renderer::element::{surface::render_elements_from_surface_tree, surface::WaylandSurfaceRenderElement, Kind, RenderElement};
|
||||||
use smithay::utils::Scale as ScaleF;
|
use smithay::utils::Scale as ScaleF;
|
||||||
|
|
||||||
let scale = ScaleF::from(1.0);
|
let scale = ScaleF::from(1.0);
|
||||||
|
let has_electron = data.state.electron_ipc.is_some();
|
||||||
let mut wayland_elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = Vec::new();
|
let mut wayland_elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = Vec::new();
|
||||||
|
|
||||||
for window in data.state.space.elements() {
|
if !has_electron {
|
||||||
if let Some(location) = data.state.space.element_location(window) {
|
for window in data.state.space.elements() {
|
||||||
let surface = window.toplevel()
|
if let Some(location) = data.state.space.element_location(window) {
|
||||||
.map(|t| t.wl_surface().clone())
|
let surface = window.toplevel()
|
||||||
.or_else(|| window.x11_surface().and_then(|x| x.wl_surface().clone()));
|
.map(|t| t.wl_surface().clone())
|
||||||
|
.or_else(|| window.x11_surface().and_then(|x| x.wl_surface().clone()));
|
||||||
if let Some(surface) = surface {
|
|
||||||
let elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = render_elements_from_surface_tree(
|
|
||||||
renderer,
|
|
||||||
&surface,
|
|
||||||
(location.x, location.y),
|
|
||||||
scale,
|
|
||||||
1.0,
|
|
||||||
Kind::Unspecified,
|
|
||||||
);
|
|
||||||
|
|
||||||
for element in elements {
|
if let Some(surface) = surface {
|
||||||
wayland_elements.push(element);
|
let elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = render_elements_from_surface_tree(
|
||||||
|
renderer,
|
||||||
|
&surface,
|
||||||
|
(location.x, location.y),
|
||||||
|
scale,
|
||||||
|
1.0,
|
||||||
|
Kind::Unspecified,
|
||||||
|
);
|
||||||
|
|
||||||
|
wayland_elements.extend(elements);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -722,16 +729,19 @@ fn render_frame(data: &mut EventLoopData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export dirty Wayland surfaces to Electron clients via DMABUF/SHM.
|
// Export dirty Wayland surfaces to Electron clients via DMABUF/SHM.
|
||||||
// Uses Wayland damage rects to skip unchanged surfaces entirely and to
|
// SHM surfaces are written directly to memfd (ping-pong double-buffered)
|
||||||
// avoid full-buffer reads when only a small region changed.
|
// to avoid an intermediate Vec<u8> copy. When usable damage rects exist,
|
||||||
|
// only the affected rows are written for further savings.
|
||||||
if data.state.electron_ipc.is_some() {
|
if data.state.electron_ipc.is_some() {
|
||||||
use smithay::backend::renderer::utils::RendererSurfaceStateUserData;
|
use smithay::backend::renderer::utils::RendererSurfaceStateUserData;
|
||||||
use smithay::reexports::wayland_server::Resource;
|
use smithay::reexports::wayland_server::Resource;
|
||||||
use smithay::wayland::compositor::{with_states, Damage, SurfaceAttributes};
|
use smithay::wayland::compositor::{with_states, Damage, SurfaceAttributes};
|
||||||
use smithay::wayland::shm::with_buffer_contents;
|
use smithay::wayland::shm::with_buffer_contents;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use nix::sys::memfd::MFdFlags;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
let mut exported_pixels: Vec<crate::state::ShmExport> = Vec::new();
|
let pid = std::process::id();
|
||||||
|
|
||||||
for surface in &dirty_surfaces {
|
for surface in &dirty_surfaces {
|
||||||
let surface_id = surface.id();
|
let surface_id = surface.id();
|
||||||
|
|
@ -772,7 +782,6 @@ fn render_frame(data: &mut EventLoopData) {
|
||||||
let Some(_geo) = window_geo else { continue };
|
let Some(_geo) = window_geo else { continue };
|
||||||
|
|
||||||
// Extract Wayland damage rects for partial-update optimization.
|
// Extract Wayland damage rects for partial-update optimization.
|
||||||
// Empty damage = full frame (surfaces in dirty_surfaces always get exported).
|
|
||||||
let damage_rects: Vec<crate::state::DamageRect> = with_states(surface, |states| {
|
let damage_rects: Vec<crate::state::DamageRect> = with_states(surface, |states| {
|
||||||
let mut attrs = states.cached_state.get::<SurfaceAttributes>();
|
let mut attrs = states.cached_state.get::<SurfaceAttributes>();
|
||||||
let current = attrs.current();
|
let current = attrs.current();
|
||||||
|
|
@ -785,7 +794,6 @@ fn render_frame(data: &mut EventLoopData) {
|
||||||
height: rect.size.h,
|
height: rect.size.h,
|
||||||
},
|
},
|
||||||
Damage::Surface(rect) => {
|
Damage::Surface(rect) => {
|
||||||
// Surface-coordinate damage; apply buffer_scale
|
|
||||||
let scale = current.buffer_scale;
|
let scale = current.buffer_scale;
|
||||||
crate::state::DamageRect {
|
crate::state::DamageRect {
|
||||||
x: rect.loc.x * scale,
|
x: rect.loc.x * scale,
|
||||||
|
|
@ -798,40 +806,97 @@ fn render_frame(data: &mut EventLoopData) {
|
||||||
}).collect()
|
}).collect()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Try SHM buffer first (software-rendered clients)
|
// Prepare memfd pair for this window (ping-pong double buffer)
|
||||||
let pixels_opt: Option<(u32, u32, u32, Vec<u8>)> =
|
let pair = data.state.window_shm_buffers
|
||||||
with_states(surface, |states| {
|
.entry(window_name.clone())
|
||||||
let rss_data = states.data_map.get::<RendererSurfaceStateUserData>()?;
|
.or_insert_with(|| {
|
||||||
let rss = rss_data.lock().ok()?;
|
let a = nix::sys::memfd::memfd_create(
|
||||||
let buffer = rss.buffer()?;
|
std::ffi::CStr::from_bytes_with_nul(b"wayland_window\0").unwrap(),
|
||||||
|
MFdFlags::MFD_CLOEXEC,
|
||||||
|
).expect("creating memfd A");
|
||||||
|
let b = nix::sys::memfd::memfd_create(
|
||||||
|
std::ffi::CStr::from_bytes_with_nul(b"wayland_window\0").unwrap(),
|
||||||
|
MFdFlags::MFD_CLOEXEC,
|
||||||
|
).expect("creating memfd B");
|
||||||
|
[a, b]
|
||||||
|
});
|
||||||
|
let idx = data.state.window_shm_write_idx
|
||||||
|
.entry(window_name.clone())
|
||||||
|
.or_insert(0);
|
||||||
|
let memfd_fd = &pair[*idx];
|
||||||
|
|
||||||
let mut result: Option<(u32, u32, u32, Vec<u8>)> = None;
|
// Try SHM buffer: write directly to memfd (no intermediate Vec)
|
||||||
let _ = with_buffer_contents(buffer, |ptr, len, spec| {
|
let shm_meta: Option<(u32, u32, u32)> = with_states(surface, |states| {
|
||||||
let pixel_bytes =
|
let rss_data = states.data_map.get::<RendererSurfaceStateUserData>()?;
|
||||||
(spec.stride.max(0) * spec.height.max(0)) as usize;
|
let rss = rss_data.lock().ok()?;
|
||||||
if pixel_bytes > 0 && pixel_bytes <= len {
|
let buffer = rss.buffer()?;
|
||||||
let slice =
|
|
||||||
unsafe { std::slice::from_raw_parts(ptr, pixel_bytes) };
|
let mut result: Option<(u32, u32, u32)> = None;
|
||||||
result = Some((
|
let _ = with_buffer_contents(buffer, |ptr, len, spec| {
|
||||||
spec.width as u32,
|
let pixel_bytes =
|
||||||
spec.height as u32,
|
(spec.stride.max(0) * spec.height.max(0)) as usize;
|
||||||
spec.stride as u32,
|
if pixel_bytes == 0 || pixel_bytes > len { return; }
|
||||||
slice.to_vec(),
|
|
||||||
));
|
let slice =
|
||||||
|
unsafe { std::slice::from_raw_parts(ptr, pixel_bytes) };
|
||||||
|
|
||||||
|
// Ensure memfd is correctly sized
|
||||||
|
let size = pixel_bytes as i64;
|
||||||
|
let cur_size = nix::sys::stat::fstat(memfd_fd)
|
||||||
|
.map(|s| s.st_size).unwrap_or(0);
|
||||||
|
if cur_size != size {
|
||||||
|
let _ = nix::unistd::ftruncate(memfd_fd, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial-damage write: when damage rects cover a small
|
||||||
|
// area and the memfd was already the correct size (meaning
|
||||||
|
// the previous full frame is still valid for undamaged
|
||||||
|
// regions), write only the damaged rows.
|
||||||
|
let usable_damage = !damage_rects.is_empty()
|
||||||
|
&& damage_rects.len() < 64
|
||||||
|
&& cur_size == size;
|
||||||
|
|
||||||
|
if usable_damage {
|
||||||
|
let stride = spec.stride as usize;
|
||||||
|
let height = spec.height as usize;
|
||||||
|
for rect in &damage_rects {
|
||||||
|
let y0 = (rect.y.max(0) as usize).min(height);
|
||||||
|
let y1 = ((rect.y + rect.height).max(0) as usize).min(height);
|
||||||
|
if y0 >= y1 { continue; }
|
||||||
|
let byte_start = y0 * stride;
|
||||||
|
let byte_end = y1 * stride;
|
||||||
|
let _ = nix::unistd::lseek(
|
||||||
|
memfd_fd, byte_start as i64,
|
||||||
|
nix::unistd::Whence::SeekSet,
|
||||||
|
);
|
||||||
|
let _ = nix::unistd::write(memfd_fd, &slice[byte_start..byte_end]);
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
result
|
let _ = nix::unistd::lseek(memfd_fd, 0, nix::unistd::Whence::SeekSet);
|
||||||
});
|
let _ = nix::unistd::write(memfd_fd, slice);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some((bw, bh, stride, pixels)) = pixels_opt {
|
result = Some((
|
||||||
exported_pixels.push(crate::state::ShmExport {
|
spec.width as u32,
|
||||||
window_name,
|
spec.height as u32,
|
||||||
width: bw,
|
spec.stride as u32,
|
||||||
height: bh,
|
));
|
||||||
stride,
|
|
||||||
pixels,
|
|
||||||
damage_rects,
|
|
||||||
});
|
});
|
||||||
|
result
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((bw, bh, stride)) = shm_meta {
|
||||||
|
if let Some(ref ipc) = data.state.electron_ipc {
|
||||||
|
if let Err(e) = ipc.broadcast_shm_buffer(
|
||||||
|
&window_name, bw, bh, stride,
|
||||||
|
pid, memfd_fd.as_raw_fd() as i32,
|
||||||
|
&damage_rects,
|
||||||
|
) {
|
||||||
|
debug!("DRM shm buffer broadcast failed for {}: {}", window_name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Flip to the other buffer for next frame
|
||||||
|
*idx = 1 - *idx;
|
||||||
} else if let Some(ref ipc) = data.state.electron_ipc {
|
} else if let Some(ref ipc) = data.state.electron_ipc {
|
||||||
// Try DMABUF (hardware-rendered clients / games)
|
// Try DMABUF (hardware-rendered clients / games)
|
||||||
let dmabuf_opt = with_states(surface, |states| {
|
let dmabuf_opt = with_states(surface, |states| {
|
||||||
|
|
@ -848,8 +913,6 @@ fn render_frame(data: &mut EventLoopData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.state.pending_shm_exports = exported_pixels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send frame callbacks for the cursor surface so the client keeps
|
// Send frame callbacks for the cursor surface so the client keeps
|
||||||
|
|
@ -906,68 +969,8 @@ fn render_frame(data: &mut EventLoopData) {
|
||||||
data.state.damaged_windows.clear();
|
data.state.damaged_windows.clear();
|
||||||
data.state.dirty_surfaces.clear();
|
data.state.dirty_surfaces.clear();
|
||||||
|
|
||||||
// Write exported SHM pixel buffers to memfd and broadcast to Electron.
|
// SHM surface exports are now written directly to memfd during the
|
||||||
// Uses ping-pong double buffering: write to one memfd while Electron
|
// render pass (above), so no post-render copy is needed.
|
||||||
// reads from the other, eliminating the read/write race condition.
|
|
||||||
{
|
|
||||||
use std::os::unix::io::AsRawFd;
|
|
||||||
use nix::sys::memfd::MFdFlags;
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
let exports = std::mem::take(&mut data.state.pending_shm_exports);
|
|
||||||
for export in exports {
|
|
||||||
let pair = data.state
|
|
||||||
.window_shm_buffers
|
|
||||||
.entry(export.window_name.clone())
|
|
||||||
.or_insert_with(|| {
|
|
||||||
let a = nix::sys::memfd::memfd_create(
|
|
||||||
std::ffi::CStr::from_bytes_with_nul(b"wayland_window\0").unwrap(),
|
|
||||||
MFdFlags::MFD_CLOEXEC,
|
|
||||||
)
|
|
||||||
.expect("creating memfd A");
|
|
||||||
let b = nix::sys::memfd::memfd_create(
|
|
||||||
std::ffi::CStr::from_bytes_with_nul(b"wayland_window\0").unwrap(),
|
|
||||||
MFdFlags::MFD_CLOEXEC,
|
|
||||||
)
|
|
||||||
.expect("creating memfd B");
|
|
||||||
[a, b]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Pick the write slot (alternates each frame)
|
|
||||||
let idx = data.state.window_shm_write_idx
|
|
||||||
.entry(export.window_name.clone())
|
|
||||||
.or_insert(0);
|
|
||||||
let memfd_fd = &pair[*idx];
|
|
||||||
|
|
||||||
let size = export.pixels.len() as i64;
|
|
||||||
let stat = nix::sys::stat::fstat(memfd_fd).unwrap();
|
|
||||||
if stat.st_size != size {
|
|
||||||
nix::unistd::ftruncate(memfd_fd, size).expect("truncating memfd");
|
|
||||||
}
|
|
||||||
|
|
||||||
nix::unistd::lseek(memfd_fd, 0, nix::unistd::Whence::SeekSet)
|
|
||||||
.expect("seeking memfd");
|
|
||||||
nix::unistd::write(memfd_fd, &export.pixels).expect("writing to memfd");
|
|
||||||
|
|
||||||
if let Some(ref ipc) = data.state.electron_ipc {
|
|
||||||
let pid = std::process::id();
|
|
||||||
if let Err(e) = ipc.broadcast_shm_buffer(
|
|
||||||
&export.window_name,
|
|
||||||
export.width,
|
|
||||||
export.height,
|
|
||||||
export.stride,
|
|
||||||
pid,
|
|
||||||
memfd_fd.as_raw_fd() as i32,
|
|
||||||
&export.damage_rects,
|
|
||||||
) {
|
|
||||||
debug!("DRM shm buffer broadcast failed for {}: {}", export.window_name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flip to the other buffer for next frame
|
|
||||||
*idx = 1 - *idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get or create the DRM framebuffer for this slot.
|
// Get or create the DRM framebuffer for this slot.
|
||||||
// Caching in the slot's userdata avoids a create/destroy cycle every
|
// Caching in the slot's userdata avoids a create/destroy cycle every
|
||||||
|
|
@ -1133,14 +1136,15 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> {
|
||||||
};
|
};
|
||||||
match event {
|
match event {
|
||||||
Event::PauseSession => {
|
Event::PauseSession => {
|
||||||
info!("Session paused (VT switched away). Pausing DRM device and halting rendering.");
|
info!("Session paused (VT switched away). Pausing DRM device and suspending libinput.");
|
||||||
data.active = false;
|
data.active = false;
|
||||||
data.vblank_pending = false;
|
data.vblank_pending = false;
|
||||||
data.vblank_since = None;
|
data.vblank_since = None;
|
||||||
|
data.libinput_context.suspend();
|
||||||
data.drm.pause();
|
data.drm.pause();
|
||||||
}
|
}
|
||||||
Event::ActivateSession => {
|
Event::ActivateSession => {
|
||||||
info!("Session activated (VT switched back). Activating DRM device and resuming rendering.");
|
info!("Session activated (VT switched back). Activating DRM device and resuming libinput.");
|
||||||
if let Err(e) = data.drm.activate(false) {
|
if let Err(e) = data.drm.activate(false) {
|
||||||
error!("Failed to activate DRM device on session resume: {e}");
|
error!("Failed to activate DRM device on session resume: {e}");
|
||||||
data.active = false;
|
data.active = false;
|
||||||
|
|
@ -1148,6 +1152,10 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> {
|
||||||
data.vblank_since = None;
|
data.vblank_since = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if let Err(()) = data.libinput_context.resume() {
|
||||||
|
error!("Failed to resume libinput context on session resume");
|
||||||
|
// Continue anyway — DRM is active, input may recover on next event
|
||||||
|
}
|
||||||
data.active = true;
|
data.active = true;
|
||||||
data.vblank_pending = false;
|
data.vblank_pending = false;
|
||||||
data.vblank_since = None;
|
data.vblank_since = None;
|
||||||
|
|
@ -1627,6 +1635,8 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> {
|
||||||
libinput_context
|
libinput_context
|
||||||
.udev_assign_seat(&session.seat())
|
.udev_assign_seat(&session.seat())
|
||||||
.map_err(|()| anyhow::anyhow!("Failed to assign seat to libinput"))?;
|
.map_err(|()| anyhow::anyhow!("Failed to assign seat to libinput"))?;
|
||||||
|
// Keep a clone for suspend/resume on VT switch; the original is consumed by the backend.
|
||||||
|
let libinput_context_for_vt = libinput_context.clone();
|
||||||
let libinput_backend = LibinputInputBackend::new(libinput_context);
|
let libinput_backend = LibinputInputBackend::new(libinput_context);
|
||||||
|
|
||||||
// Prepare frame pacing using the active DRM mode refresh when available.
|
// Prepare frame pacing using the active DRM mode refresh when available.
|
||||||
|
|
@ -1727,6 +1737,7 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> {
|
||||||
total_flips: 0,
|
total_flips: 0,
|
||||||
current_slot: None,
|
current_slot: None,
|
||||||
pending_slot: None,
|
pending_slot: None,
|
||||||
|
libinput_context: libinput_context_for_vt,
|
||||||
};
|
};
|
||||||
|
|
||||||
let signal_for_handler = loop_signal.clone();
|
let signal_for_handler = loop_signal.clone();
|
||||||
|
|
|
||||||
|
|
@ -1400,11 +1400,11 @@ fn process_electron_message(
|
||||||
.and_then(|v| v.as_f64())
|
.and_then(|v| v.as_f64())
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
|
|
||||||
// Derive pointer_location from the window's
|
// Client dictates event dispatch; compositor
|
||||||
// compositor-space position so focus math is
|
// owns pointer_location (hardware). Compute
|
||||||
// consistent (the hardware pointer lives in host
|
// a synthetic location for Smithay without
|
||||||
// compositor space and is not meaningful here).
|
// overwriting pointer_location.
|
||||||
state.pointer_location = (
|
let dispatch_location: Point<f64, Logical> = (
|
||||||
loc.x as f64 + px,
|
loc.x as f64 + px,
|
||||||
loc.y as f64 + py,
|
loc.y as f64 + py,
|
||||||
).into();
|
).into();
|
||||||
|
|
@ -1428,7 +1428,7 @@ fn process_electron_message(
|
||||||
state,
|
state,
|
||||||
focus,
|
focus,
|
||||||
&smithay::input::pointer::MotionEvent {
|
&smithay::input::pointer::MotionEvent {
|
||||||
location: state.pointer_location,
|
location: dispatch_location,
|
||||||
serial,
|
serial,
|
||||||
time,
|
time,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue