Merge upstream/master (COSMIC 1.6) into local/yoda-main

32 commits amont, aucun conflit de code : seul le lockfile divergeait,
il est regenere. Les 9 commits yoda sont preserves, dont le patch des
onglets de piles suivant WindowControlsPosition.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lionel DARNIS 2026-08-19 21:56:05 +02:00
commit f60c573f4d
39 changed files with 1945 additions and 657 deletions

View file

@ -838,10 +838,7 @@ impl CosmicSurface {
self.0
.send_dmabuf_feedback(output, primary_scan_out_output, |_, data| {
if is_fullscreen {
feedback
.primary_scanout_feedback
.as_ref()
.unwrap_or(&feedback.render_feedback)
&feedback.primary_scanout_feedback
} else if frame_time_filter_fn(data) == Kind::ScanoutCandidate {
feedback
.overlay_scanout_feedback

View file

@ -212,6 +212,29 @@ impl Shell {
state.common.shell.write().update_active();
}
// We suppress Element(X) to Fullscreen(X) transition to avoid
// loss of focus by the X window when having a transition to fullscreen
// but in the case of X11 unmap/map the leave/enter needs to happen for the X11
// internal state to be focused on the window
pub fn set_focus_on_x11_map(
state: &mut State,
target: &KeyboardFocusTarget,
seat: &Seat<State>,
update_cursor: bool,
) {
let need_reset = seat
.get_keyboard()
.and_then(|keyboard| keyboard.current_focus())
.and_then(|current| current.x11_surface())
.is_some_and(|current| Some(current) == target.x11_surface());
if need_reset {
update_focus_state(seat, None, state, None, false);
}
Shell::set_focus(state, Some(target), seat, None, update_cursor);
}
pub fn append_focus_stack(&mut self, target: impl Into<FocusTarget>, seat: &Seat<State>) {
let target = target.into();
if target.is_minimized() {

View file

@ -326,7 +326,7 @@ impl KeyboardFocusTarget {
}
}
fn x11_surface(&self) -> Option<X11Surface> {
pub fn x11_surface(&self) -> Option<X11Surface> {
match self {
KeyboardFocusTarget::Element(mapped) => mapped.active_window().x11_surface().cloned(),
KeyboardFocusTarget::Fullscreen(surface) => surface.x11_surface().cloned(),

View file

@ -13,6 +13,7 @@ use smithay::{
use crate::{
config::key_bindings::cosmic_modifiers_from_smithay,
input::InputBackendId,
shell::{Trigger, layout::tiling::NodeDesc},
state::State,
};
@ -73,6 +74,7 @@ impl KeyboardGrab<State> for SwapWindowGrab {
data.handle_shortcut_action(
shortcuts::Action::Focus(direction),
&InputBackendId::Normal,
&self.seat,
serial,
time,

View file

@ -5024,6 +5024,20 @@ impl Shell {
let mut output_presentation_feedback = OutputPresentationFeedback::new(output);
if let Some(active) = self.active_space(output) {
for fs in active.get_fullscreen_surfaces() {
fs.surface.take_presentation_feedback(
&mut output_presentation_feedback,
surface_primary_scanout_output,
|surface, _| {
surface_presentation_feedback_flags_from_states(
surface,
None,
render_element_states,
)
},
);
}
active.mapped().for_each(|mapped| {
mapped.active_window().take_presentation_feedback(
&mut output_presentation_feedback,

View file

@ -5,7 +5,7 @@ use std::{any::Any, cell::RefCell, collections::HashMap, sync::Mutex};
use crate::{
backend::render::cursor::CursorState,
config::{Config, xkb_config_to_wl},
input::{ModifiersShortcutQueue, SupressedButtons, SupressedKeys},
input::{InputBackendId, ModifiersShortcutQueue, SupressedButtons, SupressedKeys},
state::State,
};
use smithay::{
@ -82,12 +82,25 @@ impl Seats {
self.last_active = Some(seat.clone());
}
pub fn for_device<D: Device>(&self, device: &D) -> Option<&Seat<State>> {
self.iter().find(|seat| {
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
devices.has_device(device)
})
pub fn for_device<D: Device>(
&self,
device: &D,
backend_id: &InputBackendId,
) -> Option<&Seat<State>> {
self.iter()
.find(|seat| {
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
devices.has_device(device, backend_id)
})
.or_else(|| {
// EI devices can be transiently unregistered while the compositor recreates
// the absolute-pointer device (e.g. on a scale/geometry change), which would
// otherwise drop all pointer/touch input until the client re-binds it. EI is
// single-seat, so fall back to the active seat here, matching the EI keyboard
// path, which always targets the active seat.
matches!(backend_id, InputBackendId::Ei(_)).then(|| self.last_active())
})
}
}
@ -96,6 +109,7 @@ impl Devices {
&self,
device: &D,
led_state: LedState,
backend_id: &InputBackendId,
) -> Vec<DeviceCapability> {
let id = device.id();
let mut map = self.capabilities.borrow_mut();
@ -113,7 +127,7 @@ impl Devices {
.cloned()
.filter(|c| map.values().flatten().all(|has| *c != *has))
.collect::<Vec<_>>();
map.insert(id, caps);
map.insert((backend_id.clone(), id), caps);
if device.has_capability(DeviceCapability::Keyboard)
&& let Some(device) = <dyn Any>::downcast_ref::<InputDevice>(device)
@ -126,11 +140,18 @@ impl Devices {
new_caps
}
pub fn has_device<D: Device>(&self, device: &D) -> bool {
self.capabilities.borrow().contains_key(&device.id())
/// Whether the given backend's device with this id is registered on the seat.
pub fn has_device<D: Device>(&self, device: &D, backend_id: &InputBackendId) -> bool {
self.capabilities
.borrow()
.contains_key(&(backend_id.clone(), device.id()))
}
pub fn remove_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
pub fn remove_device<D: Device>(
&self,
device: &D,
backend_id: &InputBackendId,
) -> Vec<DeviceCapability> {
let id = device.id();
let mut keyboards = self.keyboards.borrow_mut();
@ -139,7 +160,7 @@ impl Devices {
}
let mut map = self.capabilities.borrow_mut();
map.remove(&id)
map.remove(&(backend_id.clone(), id))
.unwrap_or_default()
.into_iter()
.filter(|c| map.values().flatten().all(|has| *c != *has))
@ -155,7 +176,8 @@ impl Devices {
#[derive(Default)]
pub struct Devices {
capabilities: RefCell<HashMap<String, Vec<DeviceCapability>>>,
// Keyed by `(backend, device_id)`
capabilities: RefCell<HashMap<(InputBackendId, String), Vec<DeviceCapability>>>,
// Used for updating keyboard leds on kms backend
keyboards: RefCell<Vec<InputDevice>>,
}
@ -185,7 +207,7 @@ struct FocusedOutput(pub Mutex<Option<Output>>);
pub struct PointerConstraintHint(pub Mutex<Option<(WlSurface, Point<f64, Logical>)>>);
#[derive(Default)]
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
pub struct LastModifierChange(pub Mutex<(HashMap<InputBackendId, Serial>, Option<Serial>)>);
pub fn create_seat(
dh: &DisplayHandle,
@ -265,6 +287,9 @@ pub trait SeatExt {
fn supressed_buttons(&self) -> &SupressedButtons;
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
fn last_modifier_change(&self) -> Option<Serial>;
fn last_modifier_change_for(&self, backend_id: &InputBackendId) -> Option<Serial>;
fn set_last_modifier_change(&self, backend_id: &InputBackendId, serial: Serial);
fn clear_last_modifier_change(&self, backend_id: &InputBackendId);
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)>;
fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point<f64, Logical>)>);
@ -343,13 +368,48 @@ impl SeatExt for Seat<State> {
}
fn last_modifier_change(&self) -> Option<Serial> {
*self
.user_data()
self.user_data()
.get::<LastModifierChange>()
.unwrap()
.0
.lock()
.unwrap()
.1
}
fn last_modifier_change_for(&self, backend_id: &InputBackendId) -> Option<Serial> {
self.user_data()
.get::<LastModifierChange>()
.unwrap()
.0
.lock()
.unwrap()
.0
.get(backend_id)
.copied()
}
fn set_last_modifier_change(&self, backend_id: &InputBackendId, serial: Serial) {
let mut guard = self
.user_data()
.get::<LastModifierChange>()
.unwrap()
.0
.lock()
.unwrap();
guard.0.insert(backend_id.clone(), serial);
guard.1 = Some(serial);
}
fn clear_last_modifier_change(&self, backend_id: &InputBackendId) {
self.user_data()
.get::<LastModifierChange>()
.unwrap()
.0
.lock()
.unwrap()
.0
.remove(backend_id);
}
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)> {