Upgrade Smithay to pinned upstream Git

Pin 8a270232f1bfc416c781273c98a3f52a812a0bba and adapt protocol dispatch, input timestamps, configure state and rendering APIs. Preserve custom lock and toplevel validation, compositor-owned key repeats, and valid layer remapping sequences.

Add native icon-buffer lifecycle regressions for the GoLand and Spotify launch failure.
This commit is contained in:
Samuel Onoja committed 2026-10-20 15:40:34 +01:00
1 parent 17df20089b
commit 90879145b2
44 files changed
+1069 -634

No files matched your search

+5 -4
View File
@@ -474,18 +474,18 @@ fn configure_libinput_device(state: &Ferese, device: &mut LibinputDevice) {
if device.config_tap_finger_count() > 0
&& let Err(error) = device.config_tap_set_enabled(settings.tap)
{
tracing::warn!(device = device.name(), ?error, "failed to configure tap-to-click");
tracing::warn!(device = %device.name(), ?error, "failed to configure tap-to-click");
}
if device.config_scroll_has_natural_scroll()
&& let Err(error) = device.config_scroll_set_natural_scroll_enabled(settings.natural_scroll)
{
tracing::warn!(device = device.name(), ?error, "failed to configure natural scrolling");
tracing::warn!(device = %device.name(), ?error, "failed to configure natural scrolling");
}
if device.config_dwt_is_available()
&& let Err(error) = device.config_dwt_set_enabled(settings.disable_while_typing)
{
tracing::warn!(
device = device.name(),
device = %device.name(),
?error,
"failed to configure disable-while-typing"
);
@@ -2264,7 +2264,7 @@ fn create_direct_output(
drm_surface,
None,
allocator,
GbmFramebufferExporter::new(gbm.clone(), Some(render_node)),
GbmFramebufferExporter::new(gbm.clone(), Some(render_node).into()),
[Fourcc::Argb8888, Fourcc::Abgr8888],
renderer.dmabuf_formats(),
drm.cursor_size(),
@@ -2341,6 +2341,7 @@ fn create_output(
let output = Output::new(
name,
PhysicalProperties {
serial_number: String::new(),
size: (physical_size.0 as i32, physical_size.1 as i32).into(),
subpixel: Subpixel::from(connector.subpixel()),
make: edid_identity
@@ -175,6 +175,7 @@ mod tests {
let output = Output::new(
"capture".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -273,6 +274,7 @@ mod failure_tests {
let output = Output::new(
"capture-failure".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -292,6 +294,7 @@ mod failure_tests {
let other_output = Output::new(
"other-capture".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
+7 -2
View File
@@ -73,7 +73,7 @@ impl OutputFeedback {
// without ALLOW_PRIMARY_PLANE_SCANOUT_ANY Smithay compares the full
// format with the swapchain. Renderer-only formats remain in fallback.
match DmabufFeedbackBuilder::new(self.render_device, self.render_formats.iter().copied())
.add_preference_tranche(self.scanout_device, Some(TrancheFlags::Scanout), [format])
.add_preference_tranche(self.scanout_device, TrancheFlags::Scanout, [format], 4..=6)
.build()
{
Ok(feedback) => self.preferred = Some(feedback),
@@ -260,6 +260,7 @@ mod tests {
states.states.insert(
id.clone(),
RenderElementState {
needs_capture: false,
visible_area: 100,
presentation_state: RenderElementPresentationState::Rendering { reason: None },
},
@@ -287,7 +288,7 @@ mod tests {
assert!(o.preferred.is_none());
o.observe_format(Some(linear));
let expected = DmabufFeedbackBuilder::new(10, [linear, other])
.add_preference_tranche(20, Some(TrancheFlags::Scanout), [linear])
.add_preference_tranche(20, TrancheFlags::Scanout, [linear], 4..=6)
.build()
.unwrap();
assert_eq!(o.preferred.as_ref(), Some(&expected));
@@ -413,6 +414,7 @@ mod tests {
for (state, expected) in [
(
RenderElementState {
needs_capture: false,
visible_area: 100,
presentation_state: RenderElementPresentationState::Rendering {
reason: Some(RenderingReason::FormatUnsupported),
@@ -422,6 +424,7 @@ mod tests {
),
(
RenderElementState {
needs_capture: false,
visible_area: 100,
presentation_state: RenderElementPresentationState::Rendering {
reason: Some(RenderingReason::ScanoutFailed),
@@ -431,6 +434,7 @@ mod tests {
),
(
RenderElementState {
needs_capture: false,
visible_area: 0,
presentation_state: RenderElementPresentationState::Skipped,
},
@@ -438,6 +442,7 @@ mod tests {
),
(
RenderElementState {
needs_capture: false,
visible_area: 100,
presentation_state: RenderElementPresentationState::ZeroCopy,
},
+2
View File
@@ -28,6 +28,7 @@ pub(super) fn elements(
Output::new(
"mirror-canvas".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "Ferese".into(),
@@ -121,6 +122,7 @@ mod tests {
Output::new(
name.into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+4 -3
View File
@@ -279,6 +279,7 @@ mod tests {
let output = Output::new(
format!("cursor-test-{index}"),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -313,7 +314,7 @@ mod tests {
&MotionEvent {
location: (x, 500.0).into(),
serial: SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
let current = [
@@ -337,7 +338,7 @@ mod tests {
&MotionEvent {
location: (1919.0, 500.0).into(),
serial: SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
assert!(cursor_on_output(&state, &outputs[0]));
@@ -351,7 +352,7 @@ mod tests {
&MotionEvent {
location: (x, 500.0).into(),
serial: SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
assert!(
@@ -83,7 +83,7 @@ pub(super) fn validate_device(device: &DirectDevice, scan: &OutputScan) -> Resul
let mut buffers = Vec::new();
let mut framebuffers = Vec::new();
let mut allocator = GbmAllocator::new(device.gbm.clone(), GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT);
let exporter = GbmFramebufferExporter::new(device.gbm.clone(), Some(device.render_node));
let exporter = GbmFramebufferExporter::new(device.gbm.clone(), Some(device.render_node).into());
let mut used_planes = HashSet::new();
let desired_crtcs = scan
.selections
+2 -1
View File
@@ -133,7 +133,7 @@ fn delayed_loading_does_not_block_cursor_callbacks_or_pointer_dispatch() {
&MotionEvent {
location: (10.0, 20.0).into(),
serial: SERIAL_COUNTER.next_serial(),
time: 1,
time: smithay::backend::input::InputTime::from_millis(1),
},
);
state.cursor_image(&seat, CursorImageStatus::Named(CursorIcon::Text));
@@ -162,6 +162,7 @@ fn cursor_animation_advances_and_cancels_on_hide_and_shape_change() {
let output = Output::new(
"cursor-test".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+2
View File
@@ -76,6 +76,7 @@ mod tests {
Output::new(
name.into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -89,6 +90,7 @@ mod tests {
states.states.insert(
id.clone(),
RenderElementState {
needs_capture: false,
visible_area: 20,
presentation_state: RenderElementPresentationState::ZeroCopy,
},
+2 -1
View File
@@ -66,6 +66,7 @@ fn subscriptions_update_deduplicate_and_retire_device_preferences() {
let output = Output::new(
"feedback-test".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -89,7 +90,7 @@ fn subscriptions_update_deduplicate_and_retire_device_preferences() {
};
let fallback = DmabufFeedbackBuilder::new(10, [f]).build().unwrap();
let preferred = DmabufFeedbackBuilder::new(10, [f])
.add_preference_tranche(20, Some(TrancheFlags::Scanout), [f])
.add_preference_tranche(20, TrancheFlags::Scanout, [f], 4..=6)
.build()
.unwrap();
let global = state
+5 -1
View File
@@ -234,7 +234,11 @@ mod tests {
use smithay::reexports::wayland_server::protocol::wl_compositor::WlCompositor;
use smithay::wayland::shell::xdg::XdgWmBaseUserData;
let compositor = recipient
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let shell = recipient
.create_resource::<XdgWmBase, XdgWmBaseUserData, Ferese>(&state.display_handle, 6, Default::default())
+134
View File
@@ -0,0 +1,134 @@
use crate::Ferese;
use smithay::reexports::wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, backend::ClientId, protocol as wl,
};
use smithay::reexports::{
wayland_protocols as protocols, wayland_protocols_misc as misc, wayland_protocols_wlr as wlr,
};
use smithay::wayland::{Dispatch2, GlobalDispatch2};
// Forward stock protocol interfaces through Smithay's current dispatch API.
// Keep xdg_toplevel and ext_session_lock_v1 explicit: Ferese validates their
// requests before delegation, including ownership of the active session lock.
macro_rules! delegate_protocols {
($($interface:path),* $(,)?) => {
$(
impl<U: Dispatch2<$interface, Ferese>> Dispatch<$interface, U> for Ferese {
fn request(
state: &mut Self,
client: &Client,
resource: &$interface,
request: <$interface as Resource>::Request,
data: &U,
display: &DisplayHandle,
init: &mut DataInit<'_, Self>,
) {
data.request(state, client, resource, request, display, init);
}
fn destroyed(state: &mut Self, client: ClientId, resource: &$interface, data: &U) {
data.destroyed(state, client, resource);
}
}
impl<U: GlobalDispatch2<$interface, Ferese>> GlobalDispatch<$interface, U> for Ferese {
fn bind(
state: &mut Self,
display: &DisplayHandle,
client: &Client,
resource: New<$interface>,
data: &U,
init: &mut DataInit<'_, Self>,
) {
data.bind(state, display, client, resource, init);
}
fn can_view(client: Client, data: &U) -> bool {
data.can_view(&client)
}
}
)*
};
}
delegate_protocols!(
protocols::wp::idle_inhibit::zv1::server::zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1,
protocols::wp::idle_inhibit::zv1::server::zwp_idle_inhibitor_v1::ZwpIdleInhibitorV1,
protocols::ext::data_control::v1::server::ext_data_control_device_v1::ExtDataControlDeviceV1,
protocols::ext::data_control::v1::server::ext_data_control_manager_v1::ExtDataControlManagerV1,
protocols::ext::data_control::v1::server::ext_data_control_source_v1::ExtDataControlSourceV1,
protocols::ext::idle_notify::v1::server::ext_idle_notification_v1::ExtIdleNotificationV1,
protocols::ext::idle_notify::v1::server::ext_idle_notifier_v1::ExtIdleNotifierV1,
protocols::ext::session_lock::v1::server::ext_session_lock_manager_v1::ExtSessionLockManagerV1,
protocols::ext::session_lock::v1::server::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
protocols::wp::alpha_modifier::v1::server::wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1,
protocols::wp::alpha_modifier::v1::server::wp_alpha_modifier_v1::WpAlphaModifierV1,
protocols::wp::cursor_shape::v1::server::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1,
protocols::wp::cursor_shape::v1::server::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1,
protocols::wp::fractional_scale::v1::server::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
protocols::wp::fractional_scale::v1::server::wp_fractional_scale_v1::WpFractionalScaleV1,
protocols::wp::keyboard_shortcuts_inhibit::zv1::server::zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1,
protocols::wp::keyboard_shortcuts_inhibit::zv1::server::zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1,
protocols::wp::linux_dmabuf::zv1::server::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
protocols::wp::pointer_constraints::zv1::server::zwp_confined_pointer_v1::ZwpConfinedPointerV1,
protocols::wp::pointer_constraints::zv1::server::zwp_locked_pointer_v1::ZwpLockedPointerV1,
protocols::wp::pointer_constraints::zv1::server::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
protocols::wp::presentation_time::server::wp_presentation::WpPresentation,
protocols::wp::presentation_time::server::wp_presentation_feedback::WpPresentationFeedback,
protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1,
protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
protocols::wp::primary_selection::zv1::server::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_v1::ZwpRelativePointerV1,
protocols::wp::security_context::v1::server::wp_security_context_manager_v1::WpSecurityContextManagerV1,
protocols::wp::security_context::v1::server::wp_security_context_v1::WpSecurityContextV1,
protocols::wp::single_pixel_buffer::v1::server::wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1,
protocols::wp::text_input::zv3::server::zwp_text_input_manager_v3::ZwpTextInputManagerV3,
protocols::wp::text_input::zv3::server::zwp_text_input_v3::ZwpTextInputV3,
protocols::wp::viewporter::server::wp_viewport::WpViewport,
protocols::wp::viewporter::server::wp_viewporter::WpViewporter,
protocols::xdg::activation::v1::server::xdg_activation_token_v1::XdgActivationTokenV1,
protocols::xdg::activation::v1::server::xdg_activation_v1::XdgActivationV1,
protocols::xdg::decoration::zv1::server::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
protocols::xdg::foreign::zv2::server::zxdg_exported_v2::ZxdgExportedV2,
protocols::xdg::foreign::zv2::server::zxdg_exporter_v2::ZxdgExporterV2,
protocols::xdg::foreign::zv2::server::zxdg_imported_v2::ZxdgImportedV2,
protocols::xdg::foreign::zv2::server::zxdg_importer_v2::ZxdgImporterV2,
protocols::xdg::shell::server::xdg_popup::XdgPopup,
protocols::xdg::shell::server::xdg_positioner::XdgPositioner,
protocols::xdg::shell::server::xdg_surface::XdgSurface,
protocols::xdg::shell::server::xdg_wm_base::XdgWmBase,
protocols::xdg::toplevel_icon::v1::server::xdg_toplevel_icon_manager_v1::XdgToplevelIconManagerV1,
protocols::xdg::toplevel_icon::v1::server::xdg_toplevel_icon_v1::XdgToplevelIconV1,
protocols::xdg::xdg_output::zv1::server::zxdg_output_manager_v1::ZxdgOutputManagerV1,
protocols::xdg::xdg_output::zv1::server::zxdg_output_v1::ZxdgOutputV1,
misc::zwp_input_method_v2::server::zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2,
misc::zwp_input_method_v2::server::zwp_input_method_manager_v2::ZwpInputMethodManagerV2,
misc::zwp_input_method_v2::server::zwp_input_method_v2::ZwpInputMethodV2,
misc::zwp_input_method_v2::server::zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2,
wlr::data_control::v1::server::zwlr_data_control_device_v1::ZwlrDataControlDeviceV1,
wlr::data_control::v1::server::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1,
wlr::data_control::v1::server::zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
wlr::layer_shell::v1::server::zwlr_layer_shell_v1::ZwlrLayerShellV1,
wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
wl::wl_buffer::WlBuffer,
wl::wl_callback::WlCallback,
wl::wl_compositor::WlCompositor,
wl::wl_data_device::WlDataDevice,
wl::wl_data_device_manager::WlDataDeviceManager,
wl::wl_data_source::WlDataSource,
wl::wl_keyboard::WlKeyboard,
wl::wl_output::WlOutput,
wl::wl_pointer::WlPointer,
wl::wl_region::WlRegion,
wl::wl_seat::WlSeat,
wl::wl_shm::WlShm,
wl::wl_shm_pool::WlShmPool,
wl::wl_subcompositor::WlSubcompositor,
wl::wl_subsurface::WlSubsurface,
wl::wl_surface::WlSurface,
wl::wl_touch::WlTouch,
);
@@ -19,6 +19,7 @@ fn output(state: &mut Ferese, name: &str, x: i32) -> Output {
let output = Output::new(
name.into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -78,10 +79,18 @@ impl Client {
.insert_client(server, Arc::new(ClientState::default()))
.unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let shell = client
.create_resource::<ZwlrLayerShellV1, (), Ferese>(&state.display_handle, 4, ())
.create_resource::<ZwlrLayerShellV1, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
4,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, 1, 1, &[2], None); // Registry.
request(&mut wire, compositor.id().protocol_id(), 0, &[3], None);
@@ -281,6 +290,7 @@ fn unregistered_output_is_not_a_creation_fallback() {
let ghost = Output::new(
"ghost".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+52 -61
View File
@@ -1,5 +1,6 @@
pub(crate) mod activation;
mod compositor;
mod dispatch;
pub(crate) mod gamma_control;
mod input_method;
mod layer_shell;
@@ -7,13 +8,15 @@ pub(crate) mod screencopy;
pub(crate) mod screenshot;
pub(crate) mod screenshot_worker;
pub(crate) mod security_context;
#[cfg(test)]
mod toplevel_icon_tests;
pub(crate) mod window_capture;
mod xdg_shell;
use smithay::desktop::{PopupManager, layer_map_for_output};
use smithay::input::pointer::{CursorImageStatus, PointerHandle};
use smithay::input::tablet::TabletSeatHandler;
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};
@@ -28,15 +31,11 @@ use smithay::wayland::output::OutputHandler;
use smithay::wayland::pointer_constraints::{PointerConstraint, PointerConstraintsHandler, with_pointer_constraint};
use smithay::wayland::selection::SelectionHandler;
use smithay::wayland::selection::data_device::{
ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler, set_data_device_focus,
DataDeviceHandler, DataDeviceState, WaylandDndGrabHandler, set_data_device_focus,
};
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;
@@ -99,6 +98,7 @@ impl SeatHandler for Ferese {
}
impl OutputHandler for Ferese {}
impl smithay::wayland::text_input::TextInputActivation for Ferese {}
impl FractionalScaleHandler for Ferese {
fn new_fractional_scale(&mut self, surface: WlSurface) {
@@ -285,31 +285,66 @@ impl SelectionHandler for Ferese {
type SelectionUserData = ();
}
impl ClientDndGrabHandler for Ferese {}
impl ServerDndGrabHandler for Ferese {}
impl TabletSeatHandler for Ferese {}
impl smithay::input::dnd::DndGrabHandler for Ferese {}
impl WaylandDndGrabHandler for Ferese {
fn dnd_requested<S: smithay::input::dnd::Source>(
&mut self,
source: S,
_icon: Option<WlSurface>,
seat: Seat<Self>,
serial: smithay::utils::Serial,
type_: smithay::input::dnd::GrabType,
) {
use smithay::input::dnd::{DnDGrab, GrabType};
match type_ {
GrabType::Pointer => {
let pointer = seat.get_pointer().expect("validated pointer drag");
let start = pointer.grab_start_data().expect("validated pointer grab");
pointer.set_grab(
self,
DnDGrab::new_pointer(&self.display_handle, start, source, seat),
serial,
smithay::input::pointer::Focus::Keep,
);
}
GrabType::Touch => {
let touch = seat.get_touch().expect("validated touch drag");
let start = touch.grab_start_data().expect("validated touch grab");
touch.set_grab(
self,
DnDGrab::new_touch(&self.display_handle, start, source, seat),
serial,
);
}
}
}
}
impl TabletSeatHandler for Ferese {
type ToolFocus = WlSurface;
}
impl DataDeviceHandler for Ferese {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
fn data_device_state(&mut self) -> &mut DataDeviceState {
&mut self.data_device_state
}
}
impl PrimarySelectionHandler for Ferese {
fn primary_selection_state(&self) -> &PrimarySelectionState {
&self.primary_selection_state
fn primary_selection_state(&mut self) -> &mut PrimarySelectionState {
&mut self.primary_selection_state
}
}
impl smithay::wayland::selection::wlr_data_control::DataControlHandler for Ferese {
fn data_control_state(&self) -> &smithay::wayland::selection::wlr_data_control::DataControlState {
&self.wlr_data_control_state
fn data_control_state(&mut self) -> &mut smithay::wayland::selection::wlr_data_control::DataControlState {
&mut self.wlr_data_control_state
}
}
impl smithay::wayland::selection::ext_data_control::DataControlHandler for Ferese {
fn data_control_state(&self) -> &smithay::wayland::selection::ext_data_control::DataControlState {
&self.ext_data_control_state
fn data_control_state(&mut self) -> &mut smithay::wayland::selection::ext_data_control::DataControlState {
&mut self.ext_data_control_state
}
}
@@ -320,47 +355,3 @@ impl XdgForeignHandler for Ferese {
}
impl XdgToplevelIconHandler for Ferese {}
smithay::delegate_compositor!(Ferese);
smithay::delegate_alpha_modifier!(Ferese);
smithay::delegate_cursor_shape!(Ferese);
smithay::delegate_data_device!(Ferese);
smithay::delegate_dmabuf!(Ferese);
smithay::delegate_fractional_scale!(Ferese);
smithay::delegate_idle_inhibit!(Ferese);
smithay::delegate_idle_notify!(Ferese);
smithay::delegate_input_method_manager!(Ferese);
smithay::delegate_keyboard_shortcuts_inhibit!(Ferese);
smithay::delegate_layer_shell!(Ferese);
smithay::delegate_output!(Ferese);
smithay::delegate_pointer_constraints!(Ferese);
smithay::delegate_presentation!(Ferese);
smithay::delegate_primary_selection!(Ferese);
smithay::delegate_relative_pointer!(Ferese);
smithay::delegate_seat!(Ferese);
smithay::delegate_shm!(Ferese);
smithay::delegate_single_pixel_buffer!(Ferese);
smithay::delegate_text_input_manager!(Ferese);
smithay::delegate_viewporter!(Ferese);
smithay::delegate_xdg_activation!(Ferese);
smithay::delegate_xdg_decoration!(Ferese);
smithay::delegate_xdg_foreign!(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);
smithay::delegate_ext_data_control!(Ferese);
@@ -27,5 +27,3 @@ impl SecurityContextHandler for Ferese {
}
}
}
smithay::delegate_security_context!(Ferese);
@@ -0,0 +1,109 @@
use std::io::{Read, Write};
use std::os::fd::AsRawFd;
use std::os::unix::net::UnixStream;
use std::sync::Arc;
use smithay::reexports::calloop::EventLoop;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_wm_base::XdgWmBase;
use smithay::reexports::wayland_protocols::xdg::toplevel_icon::v1::server::xdg_toplevel_icon_manager_v1::XdgToplevelIconManagerV1;
use smithay::reexports::wayland_server::Resource;
use smithay::reexports::wayland_server::protocol::{wl_compositor::WlCompositor, wl_shm::WlShm};
use smithay::wayland::GlobalData;
use smithay::wayland::shell::xdg::XdgWmBaseUserData;
use crate::Ferese;
use crate::state::{ClientState, dispatch_test_client, test_client_request as request};
// Replay the native JetBrains lifecycle: assign an icon, destroy the icon
// object, then free its SHM buffer. The next sync must still reach the client.
fn icon_cleanup_messages(destroy_icon: bool) -> Vec<(u32, u16, Vec<u32>)> {
let mut events = EventLoop::try_new().unwrap();
let mut state = crate::startup_tests::state(&mut events);
let (server, mut wire) = UnixStream::pair().unwrap();
let client = state
.display_handle
.insert_client(server, Arc::new(ClientState::default()))
.unwrap();
let compositor = client
.create_resource::<WlCompositor, GlobalData, Ferese>(&state.display_handle, 6, GlobalData)
.unwrap();
let shell = client
.create_resource::<XdgWmBase, XdgWmBaseUserData, Ferese>(&state.display_handle, 6, Default::default())
.unwrap();
let shm = client
.create_resource::<WlShm, GlobalData, Ferese>(&state.display_handle, 1, GlobalData)
.unwrap();
let manager = client
.create_resource::<XdgToplevelIconManagerV1, GlobalData, Ferese>(&state.display_handle, 1, GlobalData)
.unwrap();
request(&mut wire, compositor.id().protocol_id(), 0, &[2], None);
request(&mut wire, shell.id().protocol_id(), 2, &[3, 2], None);
request(&mut wire, 3, 1, &[4], None);
let mut file = tempfile::tempfile().unwrap();
file.write_all(&0xff112233u32.to_ne_bytes().repeat(32 * 32)).unwrap();
request(
&mut wire,
shm.id().protocol_id(),
0,
&[5, 32 * 32 * 4],
Some(file.as_raw_fd()),
);
request(&mut wire, 5, 0, &[6, 0, 32, 32, 128, 0], None);
request(&mut wire, manager.id().protocol_id(), 1, &[7], None);
request(&mut wire, 7, 2, &[6, 1], None);
request(&mut wire, manager.id().protocol_id(), 2, &[4, 7], None);
if destroy_icon {
request(&mut wire, 7, 0, &[], None);
}
request(&mut wire, 6, 0, &[], None);
request(&mut wire, 1, 0, &[8], None);
dispatch_test_client(&mut events, &mut state);
wire.set_nonblocking(true).unwrap();
let mut bytes = Vec::new();
let _ = wire.read_to_end(&mut bytes);
let mut messages = Vec::new();
let mut offset = 0;
while offset < bytes.len() {
let object = u32::from_ne_bytes(bytes[offset..offset + 4].try_into().unwrap());
let header = u32::from_ne_bytes(bytes[offset + 4..offset + 8].try_into().unwrap());
let size = (header >> 16) as usize;
let arguments = bytes[offset + 8..offset + size]
.chunks_exact(4)
.map(|word| u32::from_ne_bytes(word.try_into().unwrap()))
.collect();
messages.push((object, header as u16, arguments));
offset += size;
}
messages
}
#[test]
fn destroying_an_assigned_icon_before_its_buffer_keeps_the_client_connected() {
if !crate::startup_tests::private_runtime(
"handlers::toplevel_icon_tests::destroying_an_assigned_icon_before_its_buffer_keeps_the_client_connected",
) {
return;
}
let messages = icon_cleanup_messages(true);
assert!(!messages.iter().any(|(object, opcode, _)| *object == 1 && *opcode == 0));
assert!(
messages.iter().any(|(object, opcode, _)| *object == 8 && *opcode == 0),
"valid icon cleanup disconnected the client before its sync reply: {messages:?}"
);
}
#[test]
fn destroying_a_buffer_while_its_icon_is_alive_still_reports_no_buffer() {
if !crate::startup_tests::private_runtime(
"handlers::toplevel_icon_tests::destroying_a_buffer_while_its_icon_is_alive_still_reports_no_buffer",
) {
return;
}
let messages = icon_cleanup_messages(false);
assert!(
messages.iter().any(|(object, opcode, args)| {
*object == 1 && *opcode == 0 && args.first() == Some(&7) && args.get(1) == Some(&3)
}),
"destroying a live icon's buffer must report the protocol error: {messages:?}"
);
}
+2 -6
View File
@@ -490,9 +490,7 @@ impl Dispatch<xdg_toplevel::XdgToplevel, XdgShellSurfaceUserData> for Ferese {
return;
}
<XdgShellState as Dispatch<xdg_toplevel::XdgToplevel, XdgShellSurfaceUserData, Ferese>>::request(
state, client, resource, request, data, dhandle, data_init,
);
smithay::wayland::Dispatch2::request(data, state, client, resource, request, dhandle, data_init);
}
fn destroyed(
@@ -501,9 +499,7 @@ impl Dispatch<xdg_toplevel::XdgToplevel, XdgShellSurfaceUserData> for Ferese {
resource: &xdg_toplevel::XdgToplevel,
data: &XdgShellSurfaceUserData,
) {
<XdgShellState as Dispatch<xdg_toplevel::XdgToplevel, XdgShellSurfaceUserData, Ferese>>::destroyed(
state, client, resource, data,
);
smithay::wayland::Dispatch2::destroyed(data, state, client, resource);
}
}
+89 -54
View File
@@ -180,7 +180,8 @@ impl Ferese {
self.input_settings.touchpad.swipe_threshold,
);
self.swipe.start_time(std::time::Duration::from_micros(event.time()));
self.swipe
.start_time(std::time::Duration::from_micros(event.time().micros()));
if self.swipe.active() {
self.focus_output_at(pointer.current_location());
@@ -189,7 +190,7 @@ impl Ferese {
self,
&GestureSwipeBeginEvent {
serial: SERIAL_COUNTER.next_serial(),
time: event.time_msec(),
time: event.time(),
fingers: event.fingers(),
},
);
@@ -200,7 +201,7 @@ impl Ferese {
self.swipe.update_at(
event.delta_x(),
event.delta_y(),
std::time::Duration::from_micros(event.time()),
std::time::Duration::from_micros(event.time().micros()),
);
if self.swipe_navigation_blocked() {
@@ -230,7 +231,7 @@ impl Ferese {
seat.get_pointer().expect("seat has a pointer").gesture_swipe_update(
self,
&GestureSwipeUpdateEvent {
time: event.time_msec(),
time: event.time(),
delta: event.delta(),
},
);
@@ -242,7 +243,7 @@ impl Ferese {
let cancelled = event.cancelled() || self.swipe_navigation_blocked();
let direction = self
.swipe
.finish_at(cancelled, std::time::Duration::from_micros(event.time()));
.finish_at(cancelled, std::time::Duration::from_micros(event.time().micros()));
let workspace_handled = self.finish_workspace_swipe(direction);
let focus_handled = self.finish_focus_swipe(direction);
let handled = workspace_handled || focus_handled || self.swipe.preview_started();
@@ -269,7 +270,7 @@ impl Ferese {
self,
&GestureSwipeEndEvent {
serial: SERIAL_COUNTER.next_serial(),
time: event.time_msec(),
time: event.time(),
cancelled: event.cancelled(),
},
);
@@ -291,6 +292,13 @@ impl Ferese {
}
}
// Smithay filters duplicate pressed transitions before calling
// the binding filter. Repeat only the compositor-owned action;
// clients receive their initial press and repeat it themselves.
if state == KeyState::Pressed && self.binding_mode_pressed.contains(&keycode) {
self.repeat_intercepted_key(keycode);
return;
}
let serial = SERIAL_COUNTER.next_serial();
let interaction_focus = keyboard.current_focus();
let client_grab = keyboard
@@ -301,7 +309,7 @@ impl Ferese {
keycode,
state,
serial,
event.time_msec(),
event.time(),
|data, modifiers, keysym| {
if data.focus_cycle.is_some() {
if data.session_lock.active() || data.input_capture.captures(1) {
@@ -314,7 +322,7 @@ impl Ferese {
if state == KeyState::Released {
data.binding_mode_pressed.remove(&keycode);
data.binding_mode_repeats.remove(&keycode);
data.portal_shortcuts.release(keycode, Event::time(&event));
data.portal_shortcuts.release(keycode, Event::time(&event).micros());
if data.intercepted_keys.remove(&keycode) {
return FilterResult::Intercept(());
}
@@ -326,12 +334,8 @@ impl Ferese {
return FilterResult::Forward;
}
let raw_symbols = keysym.raw_syms().into_iter().map(|symbol| symbol.raw()).collect::<Vec<_>>();
let first_press = data.binding_mode_pressed.insert(keycode);
data.binding_mode_pressed.insert(keycode);
if data.session_lock.active() {
// Preserve ownership of keys held across the lock transition.
if !first_press && !data.intercepted_keys.contains(&keycode) {
return FilterResult::Forward;
}
if let Some(step) = locked_brightness_step(keysym.modified_sym().raw()) {
data.intercepted_keys.insert(keycode);
// Never execute configurable commands while locked.
@@ -350,29 +354,6 @@ impl Ferese {
FilterResult::Forward
};
}
if !first_press {
// A held key belongs to the recipient of its first
// press, even when a mode or configuration changes.
return if data.intercepted_keys.contains(&keycode) {
if !data.session_lock.active() && !data.input_capture.active()
&& !data.seat.keyboard_shortcuts_inhibited()
&& let Some(binding) = data.binding_mode_repeats.get(&keycode)
.filter(|binding| binding.matches(keycode, &raw_symbols,
modifiers.logo, modifiers.ctrl, modifiers.alt, modifiers.shift))
.cloned()
{
let action = binding.action;
if let BindingAction::FocusMru(reverse) = action {
data.cycle_focus(reverse, modifiers.alt);
} else {
data.execute_binding(action);
}
}
FilterResult::Intercept(())
} else {
FilterResult::Forward
};
}
let symbol = keysym.modified_sym().raw();
if data.binding_modes.active().is_some() && symbol == keysyms::KEY_Escape {
if state == KeyState::Pressed {
@@ -480,7 +461,7 @@ impl Ferese {
keycode,
&raw_symbols,
modifiers,
Event::time(&event),
Event::time(&event).micros(),
)
{
data.intercepted_keys.insert(keycode);
@@ -515,7 +496,7 @@ impl Ferese {
}
}
InputEvent::PointerMotionAbsolute { event, .. } => {
self.last_pointer_time = event.time_msec();
self.last_pointer_time = event.time();
let Some(position) = self.absolute_event_position(&event) else {
return;
};
@@ -532,7 +513,7 @@ impl Ferese {
&MotionEvent {
location: position,
serial: SERIAL_COUNTER.next_serial(),
time: event.time_msec(),
time: event.time(),
},
);
pointer.frame(self);
@@ -541,7 +522,7 @@ impl Ferese {
crate::backends::direct::render_cursor(self);
}
InputEvent::PointerMotion { event, .. } => {
self.last_pointer_time = event.time_msec();
self.last_pointer_time = event.time();
let pointer = self.seat.get_pointer().expect("seat has a pointer");
let position = pointer.current_location();
let focus = self.surface_under(position);
@@ -564,7 +545,7 @@ impl Ferese {
&RelativeMotionEvent {
delta: (delta.x / scale_x, delta.y / scale_y).into(),
delta_unaccel: (delta_unaccel.x / scale_x, delta_unaccel.y / scale_y).into(),
utime: event.time(),
time: event.time(),
},
);
let requested = self.clamp_pointer_position(position + delta);
@@ -577,7 +558,7 @@ impl Ferese {
&MotionEvent {
location,
serial: SERIAL_COUNTER.next_serial(),
time: event.time_msec(),
time: event.time(),
},
);
pointer.frame(self);
@@ -586,7 +567,7 @@ impl Ferese {
crate::backends::direct::render_cursor(self);
}
InputEvent::PointerButton { event, .. } => {
self.last_pointer_time = event.time_msec();
self.last_pointer_time = event.time();
if self.input_capture.active() {
if self.input_capture.captures(2) {
self.input_capture.push(serde_json::json!({"type":"button", "button":event.button_code(), "pressed":event.state() == ButtonState::Pressed}));
@@ -609,7 +590,7 @@ impl Ferese {
&MotionEvent {
location: pointer.current_location(),
serial,
time: event.time_msec(),
time: event.time(),
},
);
}
@@ -620,7 +601,7 @@ impl Ferese {
button: event.button_code(),
state: event.state(),
serial,
time: event.time_msec(),
time: event.time(),
},
);
pointer.frame(self);
@@ -666,7 +647,7 @@ impl Ferese {
popup,
crate::focus_effect::BoundedFade::new(f64::from(opacity)),
));
pointer.unset_grab(self, serial, event.time_msec());
pointer.unset_grab(self, serial, event.time());
self.focus_window_at(pointer.current_location(), serial, true);
crate::backends::direct::render_surface(self, &root);
}
@@ -684,7 +665,7 @@ impl Ferese {
button: event.button_code(),
state: event.state(),
serial,
time: event.time_msec(),
time: event.time(),
},
);
pointer.frame(self);
@@ -717,7 +698,7 @@ impl Ferese {
button: event.button_code(),
state: event.state(),
serial,
time: event.time_msec(),
time: event.time(),
},
);
pointer.frame(self);
@@ -744,13 +725,13 @@ impl Ferese {
button: event.button_code(),
state: event.state(),
serial,
time: event.time_msec(),
time: event.time(),
},
);
pointer.frame(self);
}
InputEvent::PointerAxis { event, .. } => {
self.last_pointer_time = event.time_msec();
self.last_pointer_time = event.time();
let source = event.source();
let horizontal = event
.amount(Axis::Horizontal)
@@ -777,7 +758,7 @@ impl Ferese {
) {
return;
}
let mut frame = AxisFrame::new(event.time_msec()).source(source);
let mut frame = AxisFrame::new(event.time()).source(source);
if horizontal != 0.0 {
frame = frame.value(Axis::Horizontal, horizontal);
if let Some(value) = event.amount_v120(Axis::Horizontal) {
@@ -829,7 +810,7 @@ impl Ferese {
slot: event.slot(),
location,
serial,
time: event.time_msec(),
time: event.time(),
},
);
}
@@ -848,7 +829,7 @@ impl Ferese {
&TouchMotionEvent {
slot: event.slot(),
location,
time: event.time_msec(),
time: event.time(),
},
);
}
@@ -862,7 +843,7 @@ impl Ferese {
&UpEvent {
slot: event.slot(),
serial: SERIAL_COUNTER.next_serial(),
time: event.time_msec(),
time: event.time(),
},
);
}
@@ -1012,6 +993,60 @@ impl Ferese {
candidates.get(neighbor).copied()
}
fn repeat_intercepted_key(&mut self, keycode: smithay::input::keyboard::Keycode) {
if !self.intercepted_keys.contains(&keycode) {
return;
}
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
let modifiers = keyboard.modifier_state();
let raw_symbols = keyboard.with_xkb_state(self, |context| {
let xkb = context.xkb().lock().unwrap();
xkb.raw_syms_for_key_in_layout(keycode, xkb.active_layout())
.iter()
.map(|symbol| symbol.raw())
.collect::<Vec<_>>()
});
if self.session_lock.active() {
// Repeats retain press ownership and never run configurable commands
// on the lock screen, including keys held before locking.
if let Some(step) = raw_symbols.iter().find_map(|symbol| locked_brightness_step(*symbol))
&& let Some(child) = spawn_client(
self,
["brightnessctl", "set", step],
crate::private_client::ClientCapabilities::default(),
)
{
crate::process::watch_client_exit(&self.loop_handle, child);
}
return;
}
if self.input_capture.active() || self.seat.keyboard_shortcuts_inhibited() {
return;
}
let Some(binding) = self
.binding_mode_repeats
.get(&keycode)
.filter(|binding| {
binding.matches(
keycode,
&raw_symbols,
modifiers.logo,
modifiers.ctrl,
modifiers.alt,
modifiers.shift,
)
})
.cloned()
else {
return;
};
if let BindingAction::FocusMru(reverse) = binding.action {
self.cycle_focus(reverse, modifiers.alt);
} else {
self.execute_binding(binding.action);
}
}
fn focus_window_at(&mut self, position: Point<f64, Logical>, serial: Serial, raise: bool) {
self.pending_layer_focus = None;
if self.input_capture.captures(1) {
+2 -2
View File
@@ -38,8 +38,8 @@ impl InputBackend for TestInput {
#[derive(Debug)]
struct TestKey(Keycode, KeyState);
impl Event<TestInput> for TestKey {
fn time(&self) -> u64 {
1_000
fn time(&self) -> smithay::backend::input::InputTime {
smithay::backend::input::InputTime::from_micros(1_000)
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
+61 -28
View File
@@ -29,13 +29,25 @@ fn layer_with_policy(
.insert_client(server, Arc::new(ClientState::default()))
.unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let layers = client
.create_resource::<ZwlrLayerShellV1, (), Ferese>(&state.display_handle, 4, ())
.create_resource::<ZwlrLayerShellV1, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
4,
smithay::wayland::GlobalData,
)
.unwrap();
let shm = client
.create_resource::<WlShm, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<WlShm, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, compositor.id().protocol_id(), 0, &[2], None);
request(
@@ -126,6 +138,7 @@ fn on_demand_layer_focus_follows_opening_and_outside_clicks() {
let output = Output::new(
"focus".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -153,7 +166,7 @@ fn on_demand_layer_focus_follows_opening_and_outside_clicks() {
&MotionEvent {
location: inside,
serial: SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
// The opening click arrives while the collapsed panel is noninteractive.
@@ -181,7 +194,7 @@ fn on_demand_layer_focus_follows_opening_and_outside_clicks() {
&MotionEvent {
location: outside,
serial: SERIAL_COUNTER.next_serial(),
time: 1,
time: smithay::backend::input::InputTime::from_millis(1),
},
);
state.focus_window_at(outside, SERIAL_COUNTER.next_serial(), true);
@@ -263,6 +276,7 @@ fn fixture_with_animations(animations: bool) -> (Ferese, EventLoop<'static, Fere
let output = Output::new(
"layers".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -289,6 +303,27 @@ fn commit_policy(state: &mut Ferese, events: &mut EventLoop<'static, Ferese>, wi
dispatch(events, state);
}
// Unmapping resets the layer's pending state. Reapply its policy and complete
// the initial bufferless configure/ack exchange before attaching again.
fn remap_layer(
state: &mut Ferese,
events: &mut EventLoop<'static, Ferese>,
wire: &mut UnixStream,
keyboard: u32,
level: u32,
) {
request(wire, 3, 0, &[65, 48], None);
request(wire, 3, 1, &[1 | 4], None);
request(wire, 3, 4, &[keyboard], None);
request(wire, 3, 8, &[level], None);
request(wire, 2, 6, &[], None);
dispatch(events, state);
ack_latest_configure(wire);
request(wire, 2, 1, &[5, 0, 0], None);
request(wire, 2, 6, &[], None);
dispatch(events, state);
}
#[test]
fn null_buffer_commit_revokes_layer_focus_without_destroying_role() {
if !crate::startup_tests::private_runtime(
@@ -328,6 +363,12 @@ fn null_buffer_commit_revokes_layer_focus_without_destroying_role() {
fallback,
"null commit must revoke keyboard focus for policy {interactivity}"
);
// An unmapped surface is back in its initial state. Keep this commit
// valid while asserting that keyboard policy alone cannot remap it.
request(&mut wire, 3, 0, &[64, 48], None);
request(&mut wire, 3, 1, &[1 | 4], None);
request(&mut wire, 3, 4, &[interactivity], None);
request(&mut wire, 3, 8, &[2], None);
request(&mut wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
assert_eq!(
@@ -335,16 +376,7 @@ fn null_buffer_commit_revokes_layer_focus_without_destroying_role() {
fallback,
"unmapped commits must not reclaim focus"
);
// Reconfigure with a changed size before mapping again. This follows
// the protocol's bufferless-commit/configure/ack sequence, including
// with Smithay 0.7 which retains configure state on null commits.
request(&mut wire, 3, 0, &[65, 48], None);
request(&mut wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
ack_latest_configure(&mut wire);
request(&mut wire, 2, 1, &[5, 0, 0], None);
request(&mut wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
remap_layer(&mut state, &mut events, &mut wire, interactivity, 2);
if interactivity == 2 {
assert_eq!(
keyboard.current_focus(),
@@ -443,7 +475,11 @@ fn layer_mapping_commits_do_not_override_session_lock() {
let (panel, mut wire) = layer_with_policy(&mut state, &mut events, &output, 1, 3);
let client = panel.wl_surface().client().unwrap();
let manager = client
.create_resource::<ExtSessionLockManagerV1, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<ExtSessionLockManagerV1, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, manager.id().protocol_id(), 1, &[6], None);
dispatch(&mut events, &mut state);
@@ -458,9 +494,7 @@ fn layer_mapping_commits_do_not_override_session_lock() {
request(&mut wire, 2, 1, &[0, 0, 0], None);
request(&mut wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
request(&mut wire, 2, 1, &[5, 0, 0], None);
request(&mut wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
remap_layer(&mut state, &mut events, &mut wire, 1, 3);
assert_eq!(keyboard.current_focus(), None, "layer remapping cannot bypass the lock");
request(&mut wire, 3, 7, &[], None);
dispatch(&mut events, &mut state);
@@ -532,13 +566,7 @@ fn fullscreen_hidden_top_commits_cannot_steal_keyboard_focus() {
request(&mut top_wire, 2, 1, &[0, 0, 0], None);
request(&mut top_wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
request(&mut top_wire, 3, 0, &[65, 48], None);
request(&mut top_wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
ack_latest_configure(&mut top_wire);
request(&mut top_wire, 2, 1, &[5, 0, 0], None);
request(&mut top_wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
remap_layer(&mut state, &mut events, &mut top_wire, 1, 2);
assert_eq!(
keyboard.current_focus(),
fallback,
@@ -555,7 +583,7 @@ fn fullscreen_hidden_top_commits_cannot_steal_keyboard_focus() {
&MotionEvent {
location: (20., 20.).into(),
serial: SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
state.set_window_fullscreen(id, true);
@@ -596,7 +624,11 @@ fn valid_layer_popup_grab_survives_commits_but_not_parent_unmapping() {
state.focus_window_at((20., 20.).into(), SERIAL_COUNTER.next_serial(), true);
let client = top.wl_surface().client().unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let shell = client
.create_resource::<XdgWmBase, XdgWmBaseUserData, Ferese>(&state.display_handle, 6, Default::default())
@@ -684,6 +716,7 @@ fn fullscreen_layer_eligibility_is_per_output_and_respects_overview() {
let other = Output::new(
"other".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+2 -2
View File
@@ -459,7 +459,7 @@ impl crate::Ferese {
&smithay::input::pointer::MotionEvent {
location,
serial: smithay::utils::SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
pointer.frame(self);
@@ -499,7 +499,7 @@ impl crate::Ferese {
&smithay::input::pointer::MotionEvent {
location: from,
serial: smithay::utils::SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
pointer.frame(self);
+1
View File
@@ -234,6 +234,7 @@ mod tests {
output: Output::new(
name.into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+8 -2
View File
@@ -101,7 +101,7 @@ pub(crate) fn take_output_feedback(
let visible_output =
|surface: &WlSurface, _: &SurfaceData| rendered_feedback_output(output, surface.into(), rendered);
let surface_flags = |surface: &WlSurface, _: &SurfaceData| {
flags | surface_presentation_feedback_flags_from_states(surface, rendered)
flags | surface_presentation_feedback_flags_from_states(surface, None, rendered)
};
for window in state.space.elements().filter(|window| {
@@ -288,8 +288,9 @@ impl RenderElement<GlesRenderer> for PhysicalShaderElement {
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque: &[Rectangle<i32, Physical>],
cache: Option<&smithay::utils::user_data::UserDataMap>,
) -> Result<(), GlesError> {
self.inner.draw(frame, src, dst, damage, opaque)
self.inner.draw(frame, src, dst, damage, opaque, cache)
}
}
@@ -349,6 +350,7 @@ impl RenderElement<GlesRenderer> for NativeTextureElement {
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque: &[Rectangle<i32, Physical>],
_cache: Option<&smithay::utils::user_data::UserDataMap>,
) -> Result<(), GlesError> {
frame.render_texture_from_to(
&self.texture,
@@ -532,6 +534,7 @@ mod tests {
let output = Output::new(
"owner".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -543,10 +546,12 @@ mod tests {
assert!(rendered_feedback_output(&output, id.clone(), &states).is_none());
for state in [
RenderElementState {
needs_capture: false,
visible_area: 0,
presentation_state: RenderElementPresentationState::Skipped,
},
RenderElementState {
needs_capture: false,
visible_area: 0,
presentation_state: RenderElementPresentationState::Rendering { reason: None },
},
@@ -561,6 +566,7 @@ mod tests {
states.states.insert(
id.clone(),
RenderElementState {
needs_capture: false,
visible_area: 32,
presentation_state,
},
+2
View File
@@ -832,6 +832,7 @@ fn window_tint_does_not_inherit_panel_geometry() {
geometry,
&[Rectangle::from_size(geometry.size)],
&[],
None,
)
.unwrap();
let _ = frame.finish().unwrap();
@@ -845,6 +846,7 @@ fn window_tint_does_not_inherit_panel_geometry() {
let output = Output::new(
"tint-test".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+4 -2
View File
@@ -243,6 +243,7 @@ impl RenderElement<GlesRenderer> for BlurRenderElement {
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
_cache: Option<&smithay::utils::user_data::UserDataMap>,
) -> Result<(), GlesError> {
let texture = self.texture.tex_id();
let [x, y, width, height] = self.capture_rect;
@@ -558,6 +559,7 @@ impl RenderElement<GlesRenderer> for RoundedSurfaceRenderElement {
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
_cache: Option<&smithay::utils::user_data::UserDataMap>,
) -> Result<(), GlesError> {
match self.inner.texture() {
WaylandSurfaceTexture::Texture(texture) => frame.render_texture_from_to(
@@ -1036,8 +1038,8 @@ mod tests {
for y in [geometry.loc.y, geometry.loc.y + geometry.size.h] {
let nx = 2.0 * x as f32 / element_size.w as f32 - 1.0;
let ny = 1.0 - 2.0 * y as f32 / element_size.h as f32;
let tx = matrix[0][0] * nx + matrix[1][0] * ny;
let ty = matrix[0][1] * nx + matrix[1][1] * ny;
let tx = matrix.matrix2.x_axis.x * nx + matrix.matrix2.y_axis.x * ny;
let ty = matrix.matrix2.x_axis.y * nx + matrix.matrix2.y_axis.y * ny;
xs.push((tx + 1.0) * output_size.w as f32 / 2.0);
ys.push((1.0 - ty) * output_size.h as f32 / 2.0);
}
+7 -1
View File
@@ -351,7 +351,13 @@ fn scene_elements(
elements.push(shadow.into());
}
} else {
elements.extend(constrain_space_element::<GlesRenderer, _, AnimatedWindowRenderElement>(
elements.extend(constrain_space_element::<
GlesRenderer,
_,
AnimatedWindowRenderElement,
_,
_,
>(
renderer,
&window,
constrain.loc,
+4 -1
View File
@@ -107,6 +107,7 @@ impl RenderElement<GlesRenderer> for SharedPixelShaderElement {
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
_opaque: &[Rectangle<i32, Physical>],
_cache: Option<&smithay::utils::user_data::UserDataMap>,
) -> Result<(), GlesError> {
frame.render_pixel_shader_to(
&self.shader,
@@ -143,7 +144,9 @@ mod tests {
.render(&mut target, (32, 32).into(), Transform::Normal)
.unwrap();
frame.clear(Color32F::TRANSPARENT, &[rect]).unwrap();
element.draw(&mut frame, element.src(), rect, &[rect], &[]).unwrap();
element
.draw(&mut frame, element.src(), rect, &[rect], &[], None)
.unwrap();
let _ = frame.finish().unwrap();
}
+1
View File
@@ -67,6 +67,7 @@ mod tests {
let output = Output::new(
"snapshot-test".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+9 -13
View File
@@ -4,8 +4,8 @@ use std::time::{Duration, Instant};
use smithay::output::Output;
use smithay::reexports::calloop::RegistrationToken;
use smithay::reexports::calloop::timer::{TimeoutAction, Timer};
#[cfg(test)]
use smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_manager_v1::ExtSessionLockManagerV1;
use smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1;
use smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_v1;
use smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1;
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
@@ -457,14 +457,14 @@ impl SessionLockHandler for Ferese {
self.set_overview_active(false);
let serial = SERIAL_COUNTER.next_serial();
if let Some(pointer) = self.seat.get_pointer() {
pointer.unset_grab(self, serial, 0);
pointer.unset_grab(self, serial, smithay::backend::input::InputTime::now());
pointer.motion(
self,
None,
&smithay::input::pointer::MotionEvent {
location: pointer.current_location(),
serial,
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
},
);
pointer.frame(self);
@@ -512,8 +512,8 @@ impl SessionLockHandler for Ferese {
}
}
// Smithay delegates unlock even after posting InvalidUnlock. Check ownership
// and confirmation first so a rejected/unfinished lock cannot unlock the owner.
// Tie requests to Ferese's confirmed lock owner before delegating to Smithay.
// Rejected lockers cannot create lock surfaces or release another client's lock.
impl Dispatch<ExtSessionLockV1, SessionLockState> for Ferese {
fn request(
state: &mut Self,
@@ -538,17 +538,16 @@ impl Dispatch<ExtSessionLockV1, SessionLockState> for Ferese {
lock.post_error(ext_session_lock_v1::Error::InvalidUnlock, "not the active lock owner");
return;
}
<SessionLockManagerState as Dispatch<ExtSessionLockV1, SessionLockState, Ferese>>::request(
state, client, lock, request, data, display, data_init,
);
smithay::wayland::Dispatch2::request(data, state, client, lock, request, display, data_init);
}
fn destroyed(
state: &mut Self,
_: smithay::reexports::wayland_server::backend::ClientId,
client: smithay::reexports::wayland_server::backend::ClientId,
lock: &ExtSessionLockV1,
_: &SessionLockState,
data: &SessionLockState,
) {
smithay::wayland::Dispatch2::destroyed(data, state, client, lock);
if state.session_lock.lifecycle.owner() == Some(lock) {
state.session_lock.lifecycle = Lifecycle::Orphaned;
state.session_lock.surfaces.clear();
@@ -557,9 +556,6 @@ impl Dispatch<ExtSessionLockV1, SessionLockState> for Ferese {
}
}
}
smithay::reexports::wayland_server::delegate_global_dispatch!(Ferese: [ExtSessionLockManagerV1: smithay::wayland::session_lock::SessionLockManagerGlobalData] => SessionLockManagerState);
smithay::reexports::wayland_server::delegate_dispatch!(Ferese: [ExtSessionLockManagerV1: ()] => SessionLockManagerState);
smithay::reexports::wayland_server::delegate_dispatch!(Ferese: [ExtSessionLockSurfaceV1: smithay::wayland::session_lock::ExtLockSurfaceUserData] => SessionLockManagerState);
#[cfg(test)]
mod tests;
+17 -6
View File
@@ -71,6 +71,7 @@ fn mirror_must_present_protected_frame_before_lock_is_ready() {
Output::new(
name.into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -106,6 +107,7 @@ fn protected_fallback_frames_allow_idle_after_early_locker_crash() {
let output = Output::new(
"test".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -221,6 +223,7 @@ impl Fixture {
let output = Output::new(
name.into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -249,7 +252,11 @@ impl Fixture {
.insert_client(server, Arc::new(crate::state::ClientState::default()))
.unwrap();
let manager = client
.create_resource::<ExtSessionLockManagerV1, (), Ferese>(&self.state.display_handle, 1, ())
.create_resource::<ExtSessionLockManagerV1, smithay::wayland::GlobalData, Ferese>(
&self.state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, manager.id().protocol_id(), 1, &[2]);
self.dispatch();
@@ -318,7 +325,11 @@ impl Locker {
request(&mut self.wire, registry, 0, &args);
let compositor = self
.client
.create_resource::<WlCompositor, (), Ferese>(&fixture.state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&fixture.state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let surface_id = self.id();
request(&mut self.wire, compositor.id().protocol_id(), 0, &[surface_id]);
@@ -694,7 +705,7 @@ fn lock_input_preserves_owner_click_grabs_and_rejects_retired_surfaces() {
&smithay::input::pointer::MotionEvent {
location: (10.0, 10.0).into(),
serial,
time: 1,
time: smithay::backend::input::InputTime::from_millis(1),
},
);
pointer.button(
@@ -703,7 +714,7 @@ fn lock_input_preserves_owner_click_grabs_and_rejects_retired_surfaces() {
button: 0x110,
state: smithay::backend::input::ButtonState::Pressed,
serial,
time: 2,
time: smithay::backend::input::InputTime::from_millis(2),
},
);
assert!(pointer.is_grabbed());
@@ -718,7 +729,7 @@ fn lock_input_preserves_owner_click_grabs_and_rejects_retired_surfaces() {
button: 0x110,
state: smithay::backend::input::ButtonState::Released,
serial: SERIAL_COUNTER.next_serial(),
time: 3,
time: smithay::backend::input::InputTime::from_millis(3),
},
);
assert!(!pointer.is_grabbed());
@@ -728,7 +739,7 @@ fn lock_input_preserves_owner_click_grabs_and_rejects_retired_surfaces() {
button: 0x110,
state: smithay::backend::input::ButtonState::Pressed,
serial: SERIAL_COUNTER.next_serial(),
time: 4,
time: smithay::backend::input::InputTime::from_millis(4),
},
);
assert!(pointer.is_grabbed());
+1
View File
@@ -795,6 +795,7 @@ mod tests {
let output = Output::new(
"snapshot-test".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
+7 -2
View File
@@ -112,6 +112,7 @@ fn nested_instances_share_a_runtime_and_route_launched_clients_to_their_own_ipc(
let output = Output::new(
name.into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -294,7 +295,11 @@ fn deferred_cursor_redraw_runs_after_input_lock_is_released() {
.insert_client(server, Arc::new(crate::state::ClientState::default()))
.unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
// wl_compositor.create_surface(new_id=2).
for word in [compositor.id().protocol_id(), 12u32 << 16, 2] {
@@ -314,7 +319,7 @@ fn deferred_cursor_redraw_runs_after_input_lock_is_released() {
let event = MotionEvent {
location: (10.0, 20.0).into(),
serial: SERIAL_COUNTER.next_serial(),
time: 0,
time: smithay::backend::input::InputTime::from_millis(0),
};
pointer.motion(state, Some((surface.clone(), (0.0, 0.0).into())), &event);
// Smithay invokes Ferese::cursor_image while holding its real pointer lock.
+2 -2
View File
@@ -500,7 +500,7 @@ pub struct Ferese {
pub(crate) cursor_redraw_pending: bool,
pub(crate) output_redraw_pending: Vec<Output>,
pub(crate) locked_pointer_hint: Option<LockedPointerHint>,
pub(crate) last_pointer_time: u32,
pub(crate) last_pointer_time: smithay::backend::input::InputTime,
pub(crate) cursor_loader: crate::cursor::Loader,
pub(crate) cursor_animation: crate::cursor::Animation,
pub(crate) named_cursors: HashMap<CursorIcon, NamedCursor>,
@@ -814,7 +814,7 @@ impl Ferese {
cursor_redraw_pending: false,
output_redraw_pending: Vec::new(),
locked_pointer_hint: None,
last_pointer_time: 0,
last_pointer_time: smithay::backend::input::InputTime::from_millis(0),
cursor_loader,
cursor_animation: crate::cursor::Animation::default(),
named_cursors,
+1
View File
@@ -850,6 +850,7 @@ mod tests {
let output = Output::new(
"pause-test".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
+44 -8
View File
@@ -86,13 +86,21 @@ pub(crate) fn window(state: &mut Ferese, events: &mut EventLoop<'static, Ferese>
.insert_client(server, Arc::new(ClientState::default()))
.unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let shell = client
.create_resource::<XdgWmBase, XdgWmBaseUserData, Ferese>(&state.display_handle, 6, Default::default())
.unwrap();
let shm = client
.create_resource::<WlShm, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<WlShm, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, compositor.id().protocol_id(), 0, &[2], None);
request(&mut wire, shell.id().protocol_id(), 2, &[3, 2], None);
@@ -141,10 +149,18 @@ fn attach_children(
use smithay::reexports::wayland_server::protocol::wl_subcompositor::WlSubcompositor;
let client = window.toplevel().unwrap().wl_surface().client().unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let subcompositor = client
.create_resource::<WlSubcompositor, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<WlSubcompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
request(wire, compositor.id().protocol_id(), 0, &[7], None);
request(wire, subcompositor.id().protocol_id(), 1, &[8, 7, 2], None);
@@ -242,6 +258,7 @@ fn lock_scene_draws_the_pointer_and_delivers_client_cursor_frames() {
let output = Output::new(
"lock-cursor".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -266,7 +283,11 @@ fn lock_scene_draws_the_pointer_and_delivers_client_cursor_frames() {
let (desktop, mut wire) = window(&mut state, &mut events, 0xffff0000);
let client = desktop.toplevel().unwrap().wl_surface().client().unwrap();
let manager = client
.create_resource::<ExtSessionLockManagerV1, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<ExtSessionLockManagerV1, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, manager.id().protocol_id(), 1, &[7], None);
let deadline = std::time::Instant::now() + Duration::from_secs(1);
@@ -311,7 +332,11 @@ fn lock_scene_draws_the_pointer_and_delivers_client_cursor_frames() {
}
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, compositor.id().protocol_id(), 0, &[8], None);
request(&mut wire, 8, 1, &[6, 0, 0], None);
@@ -388,6 +413,7 @@ fn capture_privacy_pixels_and_policy_transitions() {
let output = Output::new(
"privacy".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -563,6 +589,7 @@ fn capture_privacy_pixels_and_policy_transitions() {
let second = Output::new(
"second".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -649,7 +676,11 @@ fn capture_privacy_pixels_and_policy_transitions() {
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
let client = secret.toplevel().unwrap().wl_surface().client().unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut secret_wire, compositor.id().protocol_id(), 0, &[13], None);
request(&mut secret_wire, 13, 1, &[6, 0, 0], None);
@@ -756,6 +787,7 @@ fn resize_dependencies_protocol_and_pixels() {
let output = Output::new(
"resize".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -944,7 +976,11 @@ fn resize_dependencies_protocol_and_pixels() {
assert_ne!(state.viewports[&workspace].motion().current, frozen);
let client = b.toplevel().unwrap().wl_surface().client().unwrap();
let shm = client
.create_resource::<WlShm, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<WlShm, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
let mut resized = tempfile::tempfile().unwrap();
resized.write_all(&0xff00ff00u32.to_ne_bytes().repeat(32 * 48)).unwrap();
+1
View File
@@ -150,6 +150,7 @@ mod tests {
let output = Output::new(
"focus".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
+4 -3
View File
@@ -216,8 +216,8 @@ mod tests {
struct TestButton(ButtonState);
impl Event<TestInput> for TestButton {
fn time(&self) -> u64 {
1_000
fn time(&self) -> smithay::backend::input::InputTime {
smithay::backend::input::InputTime::from_micros(1_000)
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
@@ -241,7 +241,7 @@ mod tests {
&MotionEvent {
location: position,
serial: SERIAL_COUNTER.next_serial(),
time: 1,
time: smithay::backend::input::InputTime::from_millis(1),
},
);
pointer.frame(state);
@@ -268,6 +268,7 @@ mod tests {
let output = Output::new(
name.into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
+11 -2
View File
@@ -33,6 +33,7 @@ fn every_panel_style_owns_clearance_at_its_reserved_edge() {
let output = Output::new(
"clearance".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -60,10 +61,18 @@ fn every_panel_style_owns_clearance_at_its_reserved_edge() {
.insert_client(server, Arc::new(ClientState::default()))
.unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let layers = client
.create_resource::<ZwlrLayerShellV1, (), Ferese>(&state.display_handle, 4, ())
.create_resource::<ZwlrLayerShellV1, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
4,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut wire, compositor.id().protocol_id(), 0, &[2]);
let mut namespace = b"ferese-shell-top-bar\0".to_vec();
+5
View File
@@ -427,6 +427,7 @@ mod tests {
let output = Output::new(
"resize-idle".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -548,6 +549,7 @@ mod tests {
let output = Output::new(
format!("focus-{index}"),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -657,6 +659,7 @@ mod tests {
let output = Output::new(
"overview-slide-test".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -780,6 +783,7 @@ mod tests {
let output = Output::new(
"forecast-test".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -829,6 +833,7 @@ mod tests {
let other = Output::new(
"other-forecast".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
+11 -2
View File
@@ -214,6 +214,7 @@ mod tests {
output: Output::new(
name.into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -250,7 +251,11 @@ mod tests {
.insert_client(server, Arc::new(ClientState::default()))
.unwrap();
let compositor = client
.create_resource::<WlCompositor, (), Ferese>(&state.display_handle, 6, ())
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
6,
smithay::wayland::GlobalData,
)
.unwrap();
let shell = client
.create_resource::<XdgWmBase, XdgWmBaseUserData, Ferese>(&state.display_handle, 6, Default::default())
@@ -519,7 +524,11 @@ mod tests {
.insert_client(server, Arc::new(ClientState::default()))
.unwrap();
let lock = client
.create_resource::<ExtSessionLockManagerV1, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<ExtSessionLockManagerV1, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
request(&mut lock_wire, lock.id().protocol_id(), 1, &[2]);
event_loop.dispatch(Duration::from_millis(10), &mut state).unwrap();
+20 -20
View File
@@ -11,6 +11,7 @@ fn fixture() -> (smithay::reexports::calloop::EventLoop<'static, Ferese>, Ferese
let output = Output::new(
"test".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
@@ -778,7 +779,11 @@ fn floating_maximize_and_restore_work_in_both_layouts() {
use std::os::fd::AsRawFd;
let client = window.toplevel().unwrap().wl_surface().client().unwrap();
let shm = client
.create_resource::<WlShm, (), Ferese>(&state.display_handle, 1, ())
.create_resource::<WlShm, smithay::wayland::GlobalData, Ferese>(
&state.display_handle,
1,
smithay::wayland::GlobalData,
)
.unwrap();
let (width, height) = (maximized.width.round() as u32, maximized.height.round() as u32);
let mut file = tempfile::tempfile().unwrap();
@@ -803,14 +808,9 @@ fn floating_maximize_and_restore_work_in_both_layouts() {
next_object += 2;
request(&mut wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
assert!(
window
.toplevel()
.unwrap()
.current_state()
.states
.contains(xdg_toplevel::State::Maximized)
);
assert!(window.toplevel().unwrap().with_committed_state(|state| {
state.is_some_and(|state| state.states.contains(xdg_toplevel::State::Maximized))
}));
state.set_window_maximized(id, false);
assert_eq!(state.windows.geometry(&id).unwrap().logical, normal, "{mode:?}");
@@ -820,14 +820,9 @@ fn floating_maximize_and_restore_work_in_both_layouts() {
request(&mut wire, 2, 1, &[6, 0, 0], None);
request(&mut wire, 2, 6, &[], None);
dispatch(&mut events, &mut state);
assert!(
!window
.toplevel()
.unwrap()
.current_state()
.states
.contains(xdg_toplevel::State::Maximized)
);
assert!(!window.toplevel().unwrap().with_committed_state(|state| {
state.is_some_and(|state| state.states.contains(xdg_toplevel::State::Maximized))
}));
assert_eq!(
state.workspaces.placement(id),
Some(WindowPlacement::Floating { rect: normal })
@@ -846,7 +841,7 @@ fn malformed_resize_limits_disconnect_only_the_client_and_motion_survives() {
let (mut events, mut state, _) = fixture();
for (minimum, maximum) in [
((800, 0), (400, 0)),
((800i32, 0), (400, 0)),
((0, 800), (0, 400)),
((-1, 0), (0, 0)),
((0, 0), (-1, 0)),
@@ -883,10 +878,14 @@ fn malformed_resize_limits_disconnect_only_the_client_and_motion_survives() {
&MotionEvent {
location: (120.0, 130.0).into(),
serial: SERIAL_COUNTER.next_serial(),
time: 1,
time: smithay::backend::input::InputTime::from_millis(1),
},
);
pointer.unset_grab(&mut state, SERIAL_COUNTER.next_serial(), 2);
pointer.unset_grab(
&mut state,
SERIAL_COUNTER.next_serial(),
smithay::backend::input::InputTime::from_millis(2),
);
wire.set_nonblocking(true).unwrap();
let mut messages = Vec::new();
let _ = wire.read_to_end(&mut messages);
@@ -1310,6 +1309,7 @@ fn shell_can_move_a_window_to_the_current_workspace_on_another_output() {
let second = Output::new(
"second".into(),
smithay::output::PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: smithay::output::Subpixel::Unknown,
make: "test".into(),
+11 -16
View File
@@ -531,11 +531,8 @@ impl Ferese {
return;
};
let serial = with_states(surface, |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.and_then(|data| data.lock().ok().and_then(|data| data.current_serial))
let serial = window.toplevel().and_then(|toplevel| {
toplevel.with_cached_state(|state| state.last_acked.as_ref().map(|configure| configure.serial))
});
if !transaction.accepts(serial) {
return;
@@ -601,15 +598,10 @@ impl Ferese {
let Some(&id) = self.windows.ids().get(window) else {
return;
};
// current_serial is promoted by Smithay on commit, unlike
// configure_serial, which only records an acknowledgement.
// Read the acknowledgement attached to this commit, not a newer ack
// that arrived without its corresponding buffer.
let committed_serial = window.toplevel().and_then(|toplevel| {
with_states(toplevel.wl_surface(), |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.and_then(|data| data.lock().ok().and_then(|data| data.current_serial))
})
toplevel.with_cached_state(|state| state.last_acked.as_ref().map(|configure| configure.serial))
});
if self
.windows
@@ -687,14 +679,17 @@ impl Ferese {
// Never let an old buffer undo a newer resize or a fullscreen transition.
self.windows.update(id, |w| w.placement_anchor = None);
let settled_configure = window.toplevel().is_some_and(|toplevel| {
let committed = toplevel.with_cached_state(|state| state.last_acked.clone());
with_states(toplevel.wl_surface(), |states| {
states.data_map.get::<XdgToplevelSurfaceData>().is_some_and(|data| {
let Ok(data) = data.lock() else { return false };
floating_commit_is_current(
data.pending_configures().is_empty(),
data.current_serial,
data.configure_serial,
data.current.states.contains(xdg_toplevel::State::Resizing),
committed.as_ref().map(|configure| configure.serial),
data.last_acked.as_ref().map(|configure| configure.serial),
committed
.as_ref()
.is_some_and(|configure| configure.state.states.contains(xdg_toplevel::State::Resizing)),
)
})
})
+9 -2
View File
@@ -45,12 +45,15 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
refresh: backend
.window()
.current_monitor()
.and_then(|monitor| monitor.refresh_rate_millihertz())
.and_then(|monitor| monitor.current_video_mode())
.and_then(|mode| mode.refresh_rate_millihertz())
.map(|rate| rate.get())
.unwrap_or(60_000) as i32,
};
let output = Output::new(
"ferese-winit".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "Ferese".into(),
@@ -104,7 +107,9 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
let rate = backend
.window()
.current_monitor()
.and_then(|monitor| monitor.refresh_rate_millihertz())
.and_then(|monitor| monitor.current_video_mode())
.and_then(|mode| mode.refresh_rate_millihertz())
.map(|rate| rate.get())
.unwrap_or(60_000) as i32;
if !resize_nested_output(state, &output, &drawable, (size.w, size.h), scale_factor, rate) {
return;
@@ -355,6 +360,7 @@ mod tests {
let output = Output::new(
"nested".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),
@@ -471,6 +477,7 @@ mod tests {
let output = Output::new(
"nested".into(),
PhysicalProperties {
serial_number: String::new(),
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "test".into(),