Add XWayland support (#16)

* save dev state

* fix xwayland-satellite supervisor lifecycle, readiness and cleanup

* remove wayland scanner protocol generated files

* default xwayland enabled with path

* remove jargons

* address x11 review: park unconfirmed cleanup, never signal a reaped group, fix storm test and notify dir mode

* fix resize tests: never build Size values from negative dimensions

* add min-width/min-height window rules that replace client minimum sizes

* rename effective_size_constraints to effective_minimum_size and minor cleanups
This commit is contained in:
Samuel Onoja authored and GitHub committed 2026-10-06 19:40:22 +01:00
1 parent ed23949a95
commit a6f83689f8
42 files changed
+7162 -1333

No files matched your search

+19 -1
View File
@@ -11,6 +11,7 @@ mod xdg_shell;
use smithay::desktop::{PopupManager, layer_map_for_output};
use smithay::input::pointer::{CursorImageStatus, PointerHandle};
use smithay::input::{Seat, SeatHandler, SeatState};
use smithay::reexports::wayland_protocols::xdg::shell::server::{xdg_popup, xdg_positioner, xdg_surface, xdg_wm_base};
use smithay::reexports::wayland_server::Resource;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::wayland::compositor::{TraversalAction, with_states, with_surface_tree_downward};
@@ -30,6 +31,9 @@ use smithay::wayland::selection::data_device::{
use smithay::wayland::selection::primary_selection::{
PrimarySelectionHandler, PrimarySelectionState, set_primary_focus,
};
use smithay::wayland::shell::xdg::{
XdgPositionerUserData, XdgShellState, XdgShellSurfaceUserData, XdgSurfaceUserData, XdgWmBaseUserData,
};
use smithay::wayland::tablet_manager::TabletSeatHandler;
use smithay::wayland::xdg_foreign::{XdgForeignHandler, XdgForeignState};
use smithay::wayland::xdg_toplevel_icon::XdgToplevelIconHandler;
@@ -338,7 +342,21 @@ smithay::delegate_viewporter!(Ferese);
smithay::delegate_xdg_activation!(Ferese);
smithay::delegate_xdg_decoration!(Ferese);
smithay::delegate_xdg_foreign!(Ferese);
smithay::delegate_xdg_shell!(Ferese);
smithay::reexports::wayland_server::delegate_global_dispatch!(
Ferese: [xdg_wm_base::XdgWmBase: ()] => XdgShellState
);
smithay::reexports::wayland_server::delegate_dispatch!(
Ferese: [xdg_wm_base::XdgWmBase: XdgWmBaseUserData] => XdgShellState
);
smithay::reexports::wayland_server::delegate_dispatch!(
Ferese: [xdg_positioner::XdgPositioner: XdgPositionerUserData] => XdgShellState
);
smithay::reexports::wayland_server::delegate_dispatch!(
Ferese: [xdg_popup::XdgPopup: XdgShellSurfaceUserData] => XdgShellState
);
smithay::reexports::wayland_server::delegate_dispatch!(
Ferese: [xdg_surface::XdgSurface: XdgSurfaceUserData] => XdgShellState
);
smithay::delegate_xdg_toplevel_icon!(Ferese);
smithay::delegate_data_control!(Ferese);
+5 -3
View File
@@ -54,9 +54,8 @@ impl Worker {
fn run(receiver: Receiver<Job>, results: channel::SyncSender<Encoded>) {
while let Ok(job) = receiver.recv() {
let result = encode(&job);
let request = job.request;
drop(job);
let result = encode(job);
// This send cannot block. Each request produces exactly one Encode, so at
// most QUEUE_CAPACITY jobs can be queued while one more is being encoded,
// and RESULT_QUEUE_CAPACITY is asserted to exceed that. Blocking here
@@ -99,8 +98,10 @@ pub(crate) fn sweep_stale_files() {
}
}
fn encode(job: &Job) -> Result<PathBuf, String> {
fn encode(job: Job) -> Result<PathBuf, String> {
let canvas = compose(&job.frames)?;
drop(job);
let png = encode_png(&canvas)?;
write_private_file(&png)
}
@@ -114,6 +115,7 @@ fn encode_png(canvas: &Canvas) -> Result<Vec<u8>, String> {
}
let image = image::ImageBuffer::<image::Rgba<u8>, _>::from_raw(width, height, canvas.pixels.as_slice())
.ok_or("Screenshot buffer does not match its dimensions")?;
let mut out = Vec::new();
let encoder = image::codecs::png::PngEncoder::new_with_quality(
std::io::Cursor::new(&mut out),
+70 -2
View File
@@ -5,15 +5,17 @@ use smithay::desktop::{
use smithay::input::Seat;
use smithay::input::pointer::{Focus, GrabStartData};
use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::State as ToplevelState;
use smithay::reexports::wayland_server::Resource;
use smithay::reexports::wayland_server::protocol::wl_seat;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::reexports::wayland_server::{Client, DataInit, Dispatch, DisplayHandle, Resource, backend::ClientId};
use smithay::utils::{Logical, Point, Rectangle, Serial};
use smithay::wayland::compositor::with_states;
use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;
use smithay::wayland::shell::xdg::{
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState, XdgToplevelSurfaceData,
PopupSurface, PositionerState, SurfaceCachedState, ToplevelSurface, XdgShellHandler, XdgShellState,
XdgShellSurfaceUserData, XdgToplevelSurfaceData,
};
use crate::Ferese;
@@ -467,6 +469,72 @@ fn popup_constraint_target(
output
}
impl Dispatch<xdg_toplevel::XdgToplevel, XdgShellSurfaceUserData> for Ferese {
fn request(
state: &mut Self,
client: &Client,
resource: &xdg_toplevel::XdgToplevel,
request: xdg_toplevel::Request,
data: &XdgShellSurfaceUserData,
dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, Self>,
) {
let unusable = match &request {
xdg_toplevel::Request::SetMinSize { width, height } => {
!size_limits_usable(state, resource, (*width, *height), true)
}
xdg_toplevel::Request::SetMaxSize { width, height } => {
!size_limits_usable(state, resource, (*width, *height), false)
}
_ => false,
};
if unusable {
resource.post_error(xdg_toplevel::Error::InvalidSize, "size limits are unusable");
return;
}
<XdgShellState as Dispatch<xdg_toplevel::XdgToplevel, XdgShellSurfaceUserData, Ferese>>::request(
state, client, resource, request, data, dhandle, data_init,
);
}
fn destroyed(
state: &mut Self,
client: ClientId,
resource: &xdg_toplevel::XdgToplevel,
data: &XdgShellSurfaceUserData,
) {
<XdgShellState as Dispatch<xdg_toplevel::XdgToplevel, XdgShellSurfaceUserData, Ferese>>::destroyed(
state, client, resource, data,
);
}
}
fn size_limits_usable(
state: &Ferese,
toplevel: &xdg_toplevel::XdgToplevel,
proposed: (i32, i32),
minimum: bool,
) -> bool {
if proposed.0 < 0 || proposed.1 < 0 {
return false;
}
let Some(surface) = state.xdg_shell_state.get_toplevel(toplevel) else {
return true;
};
let (min, max) = with_states(surface.wl_surface(), |states| {
let mut cached = states.cached_state.get::<SurfaceCachedState>();
let pending = cached.pending();
(
(pending.min_size.w, pending.min_size.h),
(pending.max_size.w, pending.max_size.h),
)
});
let (min, max) = if minimum { (proposed, max) } else { (min, proposed) };
(max.0 == 0 || min.0 <= max.0) && (max.1 == 0 || min.1 <= max.1)
}
#[cfg(test)]
mod tests {
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_positioner;