cosmic-comp/src/state.rs

1114 lines
41 KiB
Rust
Raw Normal View History

// SPDX-License-Identifier: GPL-3.0-only
2021-12-21 18:57:09 +01:00
use crate::{
backend::{
kms::KmsState,
render::{GlMultiError, RendererRef},
winit::WinitState,
x11::X11State,
},
config::{Config, OutputConfig, OutputState, ScreenFilter},
input::{gestures::GestureState, PointerFocusState},
2024-04-10 15:49:08 +02:00
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
2024-04-29 15:40:29 +02:00
utils::prelude::OutputExt,
2025-02-17 20:30:26 +01:00
wayland::{
handlers::screencopy::SessionHolder,
protocols::{
a11y::A11yState,
atspi::AtspiState,
drm::WlDrmState,
image_source::ImageSourceState,
output_configuration::OutputConfigurationState,
output_power::OutputPowerState,
overlap_notify::OverlapNotifyState,
screencopy::ScreencopyState,
toplevel_info::ToplevelInfoState,
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
workspace::{WorkspaceClientState, WorkspaceState, WorkspaceUpdateGuard},
},
},
2024-04-10 15:49:08 +02:00
xwayland::XWaylandState,
2021-12-21 18:57:09 +01:00
};
2023-06-27 17:40:14 +02:00
use anyhow::Context;
2025-02-14 19:14:43 +01:00
use calloop::RegistrationToken;
2023-06-27 17:40:14 +02:00
use i18n_embed::{
fluent::{fluent_language_loader, FluentLanguageLoader},
DesktopLanguageRequester,
};
use once_cell::sync::Lazy;
use rust_embed::RustEmbed;
2021-12-21 18:57:09 +01:00
use smithay::{
2022-11-03 18:51:27 +01:00
backend::{
allocator::{dmabuf::Dmabuf, Fourcc},
2022-11-03 18:51:27 +01:00
drm::DrmNode,
renderer::{
2023-03-31 13:57:37 +02:00
element::{
default_primary_scanout_output_compare, utils::select_dmabuf_feedback,
RenderElementState, RenderElementStates,
2023-03-31 13:57:37 +02:00
},
ImportDma,
},
2022-11-03 18:51:27 +01:00
},
desktop::{
layer_map_for_output,
utils::{
send_dmabuf_feedback_surface_tree, send_frames_surface_tree,
2024-06-07 19:44:59 +02:00
surface_primary_scanout_output, update_surface_primary_scanout_output,
with_surfaces_surface_tree,
},
2024-04-10 15:49:08 +02:00
PopupManager,
2022-11-17 20:32:54 +01:00
},
2024-04-05 13:53:35 +02:00
input::{pointer::CursorImageStatus, SeatState},
output::{Output, Scale},
reexports::{
2022-04-27 13:25:17 +02:00
calloop::{LoopHandle, LoopSignal},
2024-04-10 15:49:08 +02:00
wayland_protocols::xdg::shell::server::xdg_toplevel::WmCapabilities,
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode,
wayland_server::{
backend::{ClientData, ClientId, DisconnectReason},
protocol::{wl_shm, wl_surface::WlSurface},
Client, DisplayHandle, Resource,
},
},
utils::{Clock, IsAlive, Monotonic, Point},
2021-12-21 18:57:09 +01:00
wayland::{
2024-06-06 14:39:55 -07:00
alpha_modifier::AlphaModifierState,
compositor::{CompositorClientState, CompositorState, SurfaceData},
2024-09-09 16:21:27 +02:00
cursor_shape::CursorShapeManagerState,
dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState},
2023-06-14 14:44:36 +02:00
fractional_scale::{with_fractional_scale, FractionalScaleManagerState},
idle_inhibit::IdleInhibitManagerState,
idle_notify::IdleNotifierState,
input_method::InputMethodManagerState,
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState,
output::OutputManagerState,
pointer_constraints::PointerConstraintsState,
2023-09-05 13:41:21 -07:00
pointer_gestures::PointerGesturesState,
presentation::PresentationState,
2023-03-31 13:57:37 +02:00
seat::WaylandFocus,
security_context::{SecurityContext, SecurityContextState},
selection::{
data_device::DataDeviceState, primary_selection::PrimarySelectionState,
wlr_data_control::DataControlState,
},
session_lock::SessionLockManagerState,
2024-04-10 15:49:08 +02:00
shell::{
kde::decoration::KdeDecorationState,
wlr_layer::WlrLayerShellState,
xdg::{decoration::XdgDecorationState, XdgShellState},
},
shm::ShmState,
2024-08-20 11:55:08 -07:00
single_pixel_buffer::SinglePixelBufferState,
2023-12-28 13:36:59 -08:00
tablet_manager::TabletManagerState,
text_input::TextInputManagerState,
2022-11-17 20:32:54 +01:00
viewporter::ViewporterState,
virtual_keyboard::VirtualKeyboardManagerState,
2024-04-10 15:49:08 +02:00
xdg_activation::XdgActivationState,
2024-08-29 17:30:50 -07:00
xdg_foreign::XdgForeignState,
2023-08-07 16:15:19 -07:00
xwayland_keyboard_grab::XWaylandKeyboardGrabState,
xwayland_shell::XWaylandShellState,
2021-12-21 18:57:09 +01:00
},
xwayland::XWaylandClientData,
2021-12-21 18:57:09 +01:00
};
2023-12-07 19:53:41 +00:00
use time::UtcOffset;
2022-01-11 19:18:41 +01:00
use std::{
2024-04-24 09:34:46 -07:00
cell::RefCell,
cmp::min,
2024-06-10 20:41:29 +02:00
collections::HashSet,
2024-04-24 09:34:46 -07:00
ffi::OsString,
process::Child,
sync::{atomic::AtomicBool, Arc, Mutex, Once, RwLock},
2025-03-05 19:37:12 +01:00
time::{Duration, Instant},
};
2023-06-27 17:40:14 +02:00
#[derive(RustEmbed)]
#[folder = "resources/i18n"]
struct Localizations;
pub static LANG_LOADER: Lazy<FluentLanguageLoader> = Lazy::new(|| fluent_language_loader!());
#[macro_export]
macro_rules! fl {
($message_id:literal) => {{
i18n_embed_fl::fl!($crate::state::LANG_LOADER, $message_id)
}};
($message_id:literal, $($args:expr),*) => {{
i18n_embed_fl::fl!($crate::state::LANG_LOADER, $message_id, $($args), *)
}};
}
pub struct ClientState {
2023-05-12 20:01:37 +02:00
pub compositor_client_state: CompositorClientState,
pub workspace_client_state: WorkspaceClientState,
pub advertised_drm_node: Option<DrmNode>,
pub privileged: bool,
pub evls: LoopSignal,
pub security_context: Option<SecurityContext>,
}
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
self.evls.wakeup();
}
}
pub fn advertised_node_for_client(client: &Client) -> Option<DrmNode> {
// Lets check the global drm-node the client got either through default-feedback or wl_drm
if let Some(normal_client) = client.get_data::<ClientState>() {
return normal_client.advertised_drm_node.clone();
}
// last but not least all xwayland-surfaces should also share a single node
if let Some(xwayland_client) = client.get_data::<XWaylandClientData>() {
return xwayland_client.user_data().get::<DrmNode>().cloned();
}
None
}
pub fn advertised_node_for_surface(w: &WlSurface, dh: &DisplayHandle) -> Option<DrmNode> {
let client = dh.get_client(w.id()).ok()?;
advertised_node_for_client(&client)
}
2025-03-05 19:37:12 +01:00
#[derive(Debug)]
pub enum LastRefresh {
None,
At(Instant),
Scheduled(RegistrationToken),
}
2023-07-12 18:54:53 +02:00
#[derive(Debug)]
pub struct State {
2022-01-11 17:00:04 +01:00
pub backend: BackendData,
2022-03-16 19:47:39 +01:00
pub common: Common,
2024-04-24 09:34:46 -07:00
pub ready: Once,
2025-03-05 19:37:12 +01:00
pub last_refresh: LastRefresh,
2022-01-11 17:00:04 +01:00
}
2023-07-12 18:54:53 +02:00
#[derive(Debug)]
2022-01-11 17:00:04 +01:00
pub struct Common {
2022-03-28 23:45:30 +02:00
pub config: Config,
2022-02-08 17:15:24 +01:00
pub socket: OsString,
2022-08-31 13:01:23 +02:00
pub display_handle: DisplayHandle,
2023-09-29 21:33:16 +02:00
pub event_loop_handle: LoopHandle<'static, State>,
2022-04-27 13:25:17 +02:00
pub event_loop_signal: LoopSignal,
2021-12-21 18:57:09 +01:00
2024-04-10 15:49:08 +02:00
pub popups: PopupManager,
pub shell: Arc<RwLock<Shell>>,
2021-12-15 23:23:49 +01:00
2022-11-17 20:32:54 +01:00
pub clock: Clock<Monotonic>,
pub startup_done: Arc<AtomicBool>,
pub should_stop: bool,
2023-12-07 19:53:41 +00:00
pub local_offset: time::UtcOffset,
pub gesture_state: Option<GestureState>,
2022-01-11 17:22:23 +01:00
2024-04-24 09:34:46 -07:00
pub kiosk_child: Option<Child>,
pub theme: cosmic::Theme,
// wayland state
pub compositor_state: CompositorState,
pub data_device_state: DataDeviceState,
pub dmabuf_state: DmabufState,
2023-06-14 14:44:36 +02:00
pub fractional_scale_state: FractionalScaleManagerState,
pub keyboard_shortcuts_inhibit_state: KeyboardShortcutsInhibitState,
pub output_state: OutputManagerState,
pub output_configuration_state: OutputConfigurationState<State>,
pub output_power_state: OutputPowerState,
2022-11-17 20:32:54 +01:00
pub presentation_state: PresentationState,
2022-07-15 14:22:02 +02:00
pub primary_selection_state: PrimarySelectionState,
pub data_control_state: Option<DataControlState>,
2024-03-12 19:42:48 +01:00
pub image_source_state: ImageSourceState,
pub screencopy_state: ScreencopyState,
pub seat_state: SeatState<State>,
2023-10-16 12:28:19 -07:00
pub session_lock_manager_state: SessionLockManagerState,
pub idle_notifier_state: IdleNotifierState<State>,
pub idle_inhibit_manager_state: IdleInhibitManagerState,
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
pub shm_state: ShmState,
2024-09-09 16:21:27 +02:00
pub cursor_shape_manager_state: CursorShapeManagerState,
pub wl_drm_state: WlDrmState<Option<DrmNode>>,
pub viewporter_state: ViewporterState,
pub kde_decoration_state: KdeDecorationState,
pub xdg_decoration_state: XdgDecorationState,
2024-11-22 19:10:58 -05:00
pub overlap_notify_state: OverlapNotifyState,
2025-02-17 20:30:26 +01:00
pub a11y_state: A11yState,
2024-04-10 15:49:08 +02:00
// shell-related wayland state
pub xdg_shell_state: XdgShellState,
pub layer_shell_state: WlrLayerShellState,
pub toplevel_info_state: ToplevelInfoState<State, CosmicSurface>,
pub toplevel_management_state: ToplevelManagementState,
pub xdg_activation_state: XdgActivationState,
2024-08-29 17:30:50 -07:00
pub xdg_foreign_state: XdgForeignState,
2024-04-10 15:49:08 +02:00
pub workspace_state: WorkspaceState<State>,
2024-08-23 18:26:08 +02:00
pub xwayland_scale: Option<i32>,
2024-04-10 15:49:08 +02:00
pub xwayland_state: Option<XWaylandState>,
pub xwayland_shell_state: XWaylandShellState,
pub pointer_focus_state: Option<PointerFocusState>,
pub atspi_state: AtspiState,
pub atspi_ei: crate::wayland::handlers::atspi::AtspiEiState,
2022-01-11 17:22:23 +01:00
}
2023-07-12 18:54:53 +02:00
#[derive(Debug)]
2021-12-15 23:23:49 +01:00
pub enum BackendData {
X11(X11State),
Winit(WinitState),
2022-01-20 19:51:46 +01:00
Kms(KmsState),
2021-12-15 23:23:49 +01:00
// TODO
// Wayland(WaylandState),
Unset,
}
2023-03-31 13:57:37 +02:00
#[derive(Debug, Clone)]
pub struct SurfaceDmabufFeedback {
pub render_feedback: DmabufFeedback,
pub scanout_feedback: DmabufFeedback,
pub primary_scanout_feedback: DmabufFeedback,
2023-03-31 13:57:37 +02:00
}
#[derive(Debug)]
struct SurfaceFrameThrottlingState {
last_sent_at: RefCell<Option<(Output, usize)>>,
}
impl Default for SurfaceFrameThrottlingState {
fn default() -> Self {
SurfaceFrameThrottlingState {
last_sent_at: RefCell::new(None),
}
}
}
2021-12-15 23:23:49 +01:00
impl BackendData {
2022-01-20 19:51:46 +01:00
pub fn kms(&mut self) -> &mut KmsState {
match self {
BackendData::Kms(ref mut kms_state) => kms_state,
_ => unreachable!("Called kms in non kms backend"),
}
}
2021-12-15 23:23:49 +01:00
pub fn x11(&mut self) -> &mut X11State {
match self {
BackendData::X11(ref mut x11_state) => x11_state,
_ => unreachable!("Called x11 in non x11 backend"),
}
}
pub fn winit(&mut self) -> &mut WinitState {
match self {
BackendData::Winit(ref mut winit_state) => winit_state,
_ => unreachable!("Called winit in non winit backend"),
}
}
pub fn apply_config_for_outputs(
&mut self,
test_only: bool,
loop_handle: &LoopHandle<'static, State>,
2025-03-19 14:16:58 +01:00
screen_filter: &ScreenFilter,
shell: Arc<RwLock<Shell>>,
2024-04-10 15:49:08 +02:00
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
xdg_activation_state: &XdgActivationState,
startup_done: Arc<AtomicBool>,
2025-01-02 17:38:59 +01:00
clock: &Clock<Monotonic>,
) -> Result<(), anyhow::Error> {
let result = match self {
2025-01-02 17:38:59 +01:00
BackendData::Kms(ref mut state) => state.apply_config_for_outputs(
test_only,
loop_handle,
2025-03-19 14:16:58 +01:00
screen_filter,
2025-01-02 17:38:59 +01:00
shell.clone(),
startup_done,
clock,
),
BackendData::Winit(ref mut state) => state.apply_config_for_outputs(test_only),
BackendData::X11(ref mut state) => state.apply_config_for_outputs(test_only),
_ => unreachable!("No backend set when applying output config"),
}?;
let mut shell = shell.write().unwrap();
for output in result {
// apply to Output
let final_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
2024-04-29 15:40:29 +02:00
let mode = Some(final_config.output_mode()).filter(|m| match output.current_mode() {
None => true,
Some(c_m) => m.size != c_m.size || m.refresh != c_m.refresh,
});
let transform =
Some(final_config.transform.into()).filter(|x| *x != output.current_transform());
2022-05-03 13:37:51 +02:00
let scale = Some(final_config.scale)
.filter(|x| *x != output.current_scale().fractional_scale());
let location = Some(Point::from((
final_config.position.0 as i32,
final_config.position.1 as i32,
)))
.filter(|x| *x != output.current_location());
2022-04-20 16:06:37 +02:00
output.change_current_state(mode, transform, scale.map(Scale::Fractional), location);
2024-04-29 15:40:29 +02:00
output.set_adaptive_sync(final_config.vrr);
2024-04-29 19:00:39 +02:00
output.set_mirroring(match &final_config.enabled {
OutputState::Mirroring(conn) => shell
.outputs()
.find(|output| &output.name() == conn)
.cloned(),
_ => None,
});
2024-04-29 15:40:29 +02:00
match final_config.enabled {
OutputState::Enabled => shell.workspaces.add_output(&output, workspace_state),
_ => {
let shell = &mut *shell;
shell.workspaces.remove_output(
&output,
shell.seats.iter(),
workspace_state,
xdg_activation_state,
)
}
}
layer_map_for_output(&output).arrange();
2024-06-10 20:41:29 +02:00
self.schedule_render(&output);
2022-03-30 13:47:06 +02:00
}
// Update layout for changes in resolution, scale, orientation
shell.workspaces.recalculate();
2024-08-23 18:26:08 +02:00
loop_handle.insert_idle(|state| state.common.update_xwayland_scale());
Ok(())
2022-03-30 13:47:06 +02:00
}
2024-06-10 20:41:29 +02:00
pub fn schedule_render(&mut self, output: &Output) {
match self {
2024-03-12 19:42:48 +01:00
BackendData::Winit(_) => {} // We cannot do this on the winit backend.
// Winit has a very strict render-loop and skipping frames breaks atleast the wayland winit-backend.
// Swapping with damage (which should be empty on these frames) is likely good enough anyway.
2024-03-12 19:42:48 +01:00
BackendData::X11(ref mut state) => state.schedule_render(output),
BackendData::Kms(ref mut state) => state.schedule_render(output),
_ => unreachable!("No backend was initialized"),
}
}
pub fn dmabuf_imported(
&mut self,
client: Option<Client>,
global: &DmabufGlobal,
dmabuf: Dmabuf,
) -> Result<Option<DrmNode>, anyhow::Error> {
match self {
BackendData::Kms(ref mut state) => {
return state
.dmabuf_imported(client, global, dmabuf)
.map(|node| Some(node))
}
BackendData::Winit(ref mut state) => {
state.backend.renderer().import_dmabuf(&dmabuf, None)?;
}
BackendData::X11(ref mut state) => {
state.renderer.import_dmabuf(&dmabuf, None)?;
}
_ => unreachable!("No backend set when importing dmabuf"),
};
Ok(None)
}
/// Get an offscreen renderer for screen capture / screenshot rendering
///
/// `kms_node_cb` callback use used to determine nodes to render with when using kms backend.
/// If this returns `None`, it will attempt to use llvmpipe, then panic if no renderer is
/// found.
pub fn offscreen_renderer<N: Into<KmsNodes>, F: FnOnce(&mut KmsState) -> Option<N>>(
&mut self,
kms_node_cb: F,
) -> Result<RendererRef, GlMultiError> {
match self {
BackendData::Kms(kms) => {
if let Some(nodes) = kms_node_cb(kms) {
let nodes = nodes.into();
Ok(RendererRef::GlMulti(kms.api.renderer(
&nodes.render_node,
&nodes.target_node,
nodes.copy_format,
)?))
} else {
Ok(RendererRef::Glow(
kms.software_renderer
.as_mut()
.expect("No Software Rendering"),
))
}
}
BackendData::Winit(winit) => Ok(RendererRef::Glow(winit.backend.renderer())),
BackendData::X11(x11) => Ok(RendererRef::Glow(&mut x11.renderer)),
_ => unreachable!("No backend set when getting offscreen renderer"),
}
}
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> anyhow::Result<()> {
2025-03-19 14:16:58 +01:00
match self {
BackendData::Kms(ref mut state) => state.update_screen_filter(screen_filter),
BackendData::Winit(ref mut state) => state.update_screen_filter(screen_filter),
BackendData::X11(ref mut state) => state.update_screen_filter(screen_filter),
2025-03-19 14:16:58 +01:00
_ => unreachable!("No backend set when setting screen filters"),
}
}
}
pub struct KmsNodes {
pub render_node: DrmNode,
pub target_node: DrmNode,
pub copy_format: Fourcc,
}
impl From<DrmNode> for KmsNodes {
fn from(node: DrmNode) -> Self {
KmsNodes {
render_node: node,
target_node: node,
// Ignored if render == target
copy_format: Fourcc::Abgr8888,
}
}
}
pub fn client_has_no_security_context(client: &Client) -> bool {
client
.get_data::<ClientState>()
.map_or(true, |client_state| client_state.security_context.is_none())
}
pub fn client_is_privileged(client: &Client) -> bool {
client
.get_data::<ClientState>()
.map_or(false, |client_state| client_state.privileged)
}
fn enable_wayland_security() -> bool {
crate::utils::env::bool_var("COSMIC_ENABLE_WAYLAND_SECURITY").unwrap_or(false)
}
impl State {
2022-03-16 20:01:34 +01:00
pub fn new(
dh: &DisplayHandle,
2022-03-16 20:01:34 +01:00
socket: OsString,
2023-09-29 21:33:16 +02:00
handle: LoopHandle<'static, State>,
2022-04-27 13:25:17 +02:00
signal: LoopSignal,
2022-03-16 20:01:34 +01:00
) -> State {
2023-06-27 17:40:14 +02:00
let requested_languages = DesktopLanguageRequester::requested_languages();
i18n_embed::select(&*LANG_LOADER, &Localizations, &requested_languages)
.with_context(|| "Failed to load languages")
.unwrap();
2023-10-07 19:15:44 -07:00
#[cfg(feature = "profile-with-tracy")]
unsafe {
time::util::local_offset::set_soundness(time::util::local_offset::Soundness::Unsound);
}
2023-12-07 19:53:41 +00:00
let local_offset = UtcOffset::current_local_offset().expect("No yet multithreaded");
2023-10-07 19:15:44 -07:00
#[cfg(feature = "profile-with-tracy")]
unsafe {
time::util::local_offset::set_soundness(time::util::local_offset::Soundness::Sound);
}
2023-10-24 10:43:11 -07:00
let clock = Clock::new();
let config = Config::load(&handle);
let compositor_state = CompositorState::new::<Self>(dh);
let data_device_state = DataDeviceState::new::<Self>(dh);
let dmabuf_state = DmabufState::new();
2023-06-14 14:44:36 +02:00
let fractional_scale_state = FractionalScaleManagerState::new::<State>(dh);
let keyboard_shortcuts_inhibit_state = KeyboardShortcutsInhibitState::new::<Self>(dh);
let output_state = OutputManagerState::new_with_xdg_output::<Self>(dh);
let output_configuration_state =
OutputConfigurationState::new(dh, handle.clone(), client_is_privileged);
let output_power_state = OutputPowerState::new::<Self, _>(dh, client_is_privileged);
2024-11-22 19:10:58 -05:00
let overlap_notify_state =
OverlapNotifyState::new::<Self, _>(dh, client_has_no_security_context);
2022-11-17 20:32:54 +01:00
let presentation_state = PresentationState::new::<Self>(dh, clock.id() as u32);
let primary_selection_state = PrimarySelectionState::new::<Self>(dh);
let image_source_state = ImageSourceState::new::<Self, _>(dh, client_is_privileged);
let screencopy_state = ScreencopyState::new::<Self, _>(dh, client_is_privileged);
let shm_state =
ShmState::new::<Self>(dh, vec![wl_shm::Format::Xbgr8888, wl_shm::Format::Abgr8888]);
2024-09-09 16:21:27 +02:00
let cursor_shape_manager_state = CursorShapeManagerState::new::<State>(dh);
2022-09-28 12:01:29 +02:00
let seat_state = SeatState::<Self>::new();
let viewporter_state = ViewporterState::new::<Self>(dh);
let wl_drm_state = WlDrmState::<Option<DrmNode>>::default();
let kde_decoration_state = KdeDecorationState::new::<Self>(&dh, Mode::Client);
let xdg_decoration_state = XdgDecorationState::new::<Self>(&dh);
2023-10-16 12:28:19 -07:00
let session_lock_manager_state =
SessionLockManagerState::new::<Self, _>(&dh, client_is_privileged);
2023-08-07 16:15:19 -07:00
XWaylandKeyboardGrabState::new::<Self>(&dh);
let xwayland_shell_state = XWaylandShellState::new::<Self>(&dh);
PointerConstraintsState::new::<Self>(&dh);
2023-09-05 13:41:21 -07:00
PointerGesturesState::new::<Self>(&dh);
2023-12-28 13:36:59 -08:00
TabletManagerState::new::<Self>(&dh);
SecurityContextState::new::<Self, _>(&dh, client_has_no_security_context);
InputMethodManagerState::new::<Self, _>(&dh, client_is_privileged);
TextInputManagerState::new::<Self>(&dh);
VirtualKeyboardManagerState::new::<State, _>(&dh, client_is_privileged);
2024-06-06 14:39:55 -07:00
AlphaModifierState::new::<Self>(&dh);
2024-08-20 11:55:08 -07:00
SinglePixelBufferState::new::<Self>(&dh);
let idle_notifier_state = IdleNotifierState::<Self>::new(&dh, handle.clone());
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(&dh);
let idle_inhibiting_surfaces = HashSet::new();
let data_control_state = crate::utils::env::bool_var("COSMIC_DATA_CONTROL_ENABLED")
.unwrap_or(false)
2024-04-03 16:02:27 +02:00
.then(|| {
DataControlState::new::<Self, _>(dh, Some(&primary_selection_state), |_| true)
});
2024-04-10 15:49:08 +02:00
let shell = Arc::new(RwLock::new(Shell::new(&config)));
let layer_shell_state =
WlrLayerShellState::new_with_filter::<State, _>(dh, client_is_privileged);
2024-04-10 15:49:08 +02:00
let xdg_shell_state = XdgShellState::new_with_capabilities::<State>(
dh,
[
WmCapabilities::Fullscreen,
WmCapabilities::Maximize,
WmCapabilities::Minimize,
WmCapabilities::WindowMenu,
],
);
let xdg_activation_state = XdgActivationState::new::<State>(dh);
2024-08-29 17:30:50 -07:00
let xdg_foreign_state = XdgForeignState::new::<State>(dh);
let toplevel_info_state = ToplevelInfoState::new(dh, client_is_privileged);
2024-04-10 15:49:08 +02:00
let toplevel_management_state = ToplevelManagementState::new::<State, _>(
dh,
vec![
ManagementCapabilities::Close,
ManagementCapabilities::Activate,
ManagementCapabilities::Maximize,
ManagementCapabilities::Minimize,
ManagementCapabilities::MoveToWorkspace,
],
client_is_privileged,
2024-04-10 15:49:08 +02:00
);
let workspace_state = WorkspaceState::new(dh, client_is_privileged);
2021-12-21 18:57:09 +01:00
if let Err(err) = crate::dbus::init(&handle) {
tracing::warn!(?err, "Failed to initialize dbus handlers");
}
2025-02-17 20:30:26 +01:00
let a11y_state = A11yState::new::<State, _>(dh, client_is_privileged);
// TODO: Restrict to only specific client?
2025-02-17 20:30:26 +01:00
let atspi_state = AtspiState::new::<State, _>(dh, |_| true);
State {
2022-01-11 17:00:04 +01:00
common: Common {
config,
2022-02-08 17:15:24 +01:00
socket,
2022-08-31 13:01:23 +02:00
display_handle: dh.clone(),
event_loop_handle: handle,
2022-04-27 13:25:17 +02:00
event_loop_signal: signal,
2021-12-21 18:57:09 +01:00
2024-04-10 15:49:08 +02:00
popups: PopupManager::default(),
2022-03-24 20:32:31 +01:00
shell,
2021-12-21 18:57:09 +01:00
2023-12-07 19:53:41 +00:00
local_offset,
2021-12-15 23:23:49 +01:00
2022-11-17 20:32:54 +01:00
clock,
startup_done: Arc::new(AtomicBool::new(false)),
2022-01-11 17:00:04 +01:00
should_stop: false,
gesture_state: None,
2022-01-11 17:22:23 +01:00
2024-04-24 09:34:46 -07:00
kiosk_child: None,
theme: cosmic::theme::system_preference(),
compositor_state,
data_device_state,
dmabuf_state,
2023-06-14 14:44:36 +02:00
fractional_scale_state,
idle_notifier_state,
idle_inhibit_manager_state,
idle_inhibiting_surfaces,
2024-03-12 19:42:48 +01:00
image_source_state,
screencopy_state,
shm_state,
2024-09-09 16:21:27 +02:00
cursor_shape_manager_state,
seat_state,
2023-10-16 12:28:19 -07:00
session_lock_manager_state,
keyboard_shortcuts_inhibit_state,
output_state,
output_configuration_state,
output_power_state,
2024-11-22 19:10:58 -05:00
overlap_notify_state,
2022-11-17 20:32:54 +01:00
presentation_state,
2022-07-15 14:22:02 +02:00
primary_selection_state,
data_control_state,
viewporter_state,
wl_drm_state,
kde_decoration_state,
xdg_decoration_state,
2024-04-10 15:49:08 +02:00
xdg_shell_state,
layer_shell_state,
toplevel_info_state,
toplevel_management_state,
xdg_activation_state,
2024-08-29 17:30:50 -07:00
xdg_foreign_state,
2024-04-10 15:49:08 +02:00
workspace_state,
2025-02-17 20:30:26 +01:00
a11y_state,
2024-08-23 18:26:08 +02:00
xwayland_scale: None,
2024-04-10 15:49:08 +02:00
xwayland_state: None,
xwayland_shell_state,
pointer_focus_state: None,
atspi_state,
atspi_ei: Default::default(),
2022-01-11 17:00:04 +01:00
},
2021-12-15 23:23:49 +01:00
backend: BackendData::Unset,
2024-04-24 09:34:46 -07:00
ready: Once::new(),
2025-03-05 19:37:12 +01:00
last_refresh: LastRefresh::None,
}
}
2022-03-16 19:47:39 +01:00
pub fn new_client_state(&self) -> ClientState {
ClientState {
2023-05-12 20:01:37 +02:00
compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(),
advertised_drm_node: match &self.backend {
BackendData::Kms(kms_state) => kms_state.primary_node,
_ => None,
},
privileged: !enable_wayland_security(),
evls: self.common.event_loop_signal.clone(),
security_context: None,
}
}
}
2022-01-11 19:18:41 +01:00
fn primary_scanout_output_compare<'a>(
current_output: &'a Output,
current_state: &RenderElementState,
next_output: &'a Output,
next_state: &RenderElementState,
) -> &'a Output {
if !crate::wayland::protocols::output_configuration::head_is_enabled(current_output) {
return next_output;
}
default_primary_scanout_output_compare(current_output, current_state, next_output, next_state)
}
2022-09-28 12:01:29 +02:00
impl Common {
2024-06-07 19:44:59 +02:00
pub fn update_primary_output(
2023-03-31 13:57:37 +02:00
&self,
output: &Output,
render_element_states: &RenderElementStates,
) {
2024-04-10 15:49:08 +02:00
let shell = self.shell.read().unwrap();
let processor = |surface: &WlSurface, states: &SurfaceData| {
let primary_scanout_output = update_surface_primary_scanout_output(
surface,
output,
states,
render_element_states,
primary_scanout_output_compare,
);
if let Some(output) = primary_scanout_output {
with_fractional_scale(states, |fraction_scale| {
fraction_scale.set_preferred_scale(output.current_scale().fractional_scale());
});
}
};
// lock surface
if let Some(session_lock) = shell.session_lock.as_ref() {
if let Some(lock_surface) = session_lock.surfaces.get(output) {
with_surfaces_surface_tree(lock_surface.wl_surface(), processor)
}
}
2022-11-03 18:51:27 +01:00
2024-06-07 19:44:59 +02:00
for seat in shell
.seats
.iter()
.filter(|seat| &seat.active_output() == output)
{
let cursor_status = seat
.user_data()
.get::<Mutex<CursorImageStatus>>()
.map(|lock| {
let mut cursor_status = lock.lock().unwrap();
if let CursorImageStatus::Surface(ref surface) = *cursor_status {
if !surface.alive() {
*cursor_status = CursorImageStatus::default_named();
}
}
cursor_status.clone()
})
.unwrap_or(CursorImageStatus::default_named());
// cursor ...
if let CursorImageStatus::Surface(wl_surface) = cursor_status {
with_surfaces_surface_tree(&wl_surface, processor);
}
// grabs
2024-06-07 19:44:59 +02:00
if let Some(move_grab) = seat.user_data().get::<SeatMoveGrabState>() {
if let Some(grab_state) = move_grab.lock().unwrap().as_ref() {
for (window, _) in grab_state.element().windows() {
window.with_surfaces(processor);
2024-06-07 19:44:59 +02:00
}
}
}
}
// sticky window
2024-06-19 20:13:14 +02:00
for set in shell.workspaces.sets.values() {
set.sticky_layer.mapped().for_each(|mapped| {
2024-06-07 19:44:59 +02:00
for (window, _) in mapped.windows() {
window.with_surfaces(processor);
2024-06-07 19:44:59 +02:00
}
});
2024-06-19 20:13:14 +02:00
}
2024-06-07 19:44:59 +02:00
// normal windows
2024-06-19 20:13:14 +02:00
for space in shell.workspaces.spaces() {
space.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() {
window.with_surfaces(processor);
2024-06-19 20:13:14 +02:00
}
});
space.minimized_windows.iter().for_each(|m| {
for (window, _) in m.window.windows() {
window.with_surfaces(processor);
2024-06-19 20:13:14 +02:00
}
})
}
// OR windows
shell.override_redirect_windows.iter().for_each(|or| {
if let Some(wl_surface) = or.wl_surface() {
with_surfaces_surface_tree(&wl_surface, processor);
2024-06-07 19:44:59 +02:00
}
});
2024-06-19 20:13:14 +02:00
// layer surfaces
for o in shell.outputs() {
let map = smithay::desktop::layer_map_for_output(o);
for layer_surface in map.layers() {
layer_surface.with_surfaces(processor);
2024-06-07 19:44:59 +02:00
}
}
}
pub fn send_dmabuf_feedback(
&self,
output: &Output,
render_element_states: &RenderElementStates,
mut dmabuf_feedback: impl FnMut(DrmNode) -> Option<SurfaceDmabufFeedback>,
) {
let shell = self.shell.read().unwrap();
if let Some(session_lock) = shell.session_lock.as_ref() {
if let Some(lock_surface) = session_lock.surfaces.get(output) {
2023-10-16 12:28:19 -07:00
if let Some(feedback) =
advertised_node_for_surface(lock_surface.wl_surface(), &self.display_handle)
2023-10-16 12:28:19 -07:00
.and_then(|source| dmabuf_feedback(source))
{
send_dmabuf_feedback_surface_tree(
&lock_surface.wl_surface(),
output,
surface_primary_scanout_output,
|surface, _| {
select_dmabuf_feedback(
surface,
render_element_states,
&feedback.render_feedback,
&feedback.primary_scanout_feedback,
2023-10-16 12:28:19 -07:00
)
},
)
}
}
}
2024-04-10 15:49:08 +02:00
for seat in shell
.seats
.iter()
.filter(|seat| &seat.active_output() == output)
{
2024-06-07 19:44:59 +02:00
if let Some(move_grab) = seat.user_data().get::<SeatMoveGrabState>() {
if let Some(grab_state) = move_grab.lock().unwrap().as_ref() {
for (window, _) in grab_state.element().windows() {
if let Some(feedback) = window
.wl_surface()
.and_then(|wl_surface| {
advertised_node_for_surface(&wl_surface, &self.display_handle)
})
.and_then(|source| dmabuf_feedback(source))
{
window.send_dmabuf_feedback(
output,
&feedback,
render_element_states,
surface_primary_scanout_output,
);
}
}
2024-06-07 19:44:59 +02:00
}
}
2024-06-07 19:44:59 +02:00
}
2024-06-07 19:44:59 +02:00
shell
.workspaces
.sets
.get(output)
.unwrap()
.sticky_layer
.mapped()
.for_each(|mapped| {
for (window, _) in mapped.windows() {
if let Some(feedback) = window
.wl_surface()
.and_then(|wl_surface| {
advertised_node_for_surface(&wl_surface, &self.display_handle)
})
.and_then(|source| dmabuf_feedback(source))
{
window.send_dmabuf_feedback(
output,
&feedback,
render_element_states,
surface_primary_scanout_output,
);
}
}
2024-06-07 19:44:59 +02:00
});
if let Some(active) = shell.active_space(output) {
active.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() {
if let Some(feedback) = window
.wl_surface()
.and_then(|wl_surface| {
advertised_node_for_surface(&wl_surface, &self.display_handle)
})
.and_then(|source| dmabuf_feedback(source))
{
window.send_dmabuf_feedback(
output,
&feedback,
render_element_states,
surface_primary_scanout_output,
);
}
}
});
}
2022-11-03 18:51:27 +01:00
2024-04-10 15:49:08 +02:00
shell.override_redirect_windows.iter().for_each(|or| {
if let Some(wl_surface) = or.wl_surface() {
if let Some(feedback) =
advertised_node_for_surface(&wl_surface, &self.display_handle)
.and_then(|source| dmabuf_feedback(source))
2023-03-31 13:57:37 +02:00
{
send_dmabuf_feedback_surface_tree(
&wl_surface,
output,
surface_primary_scanout_output,
|surface, _| {
select_dmabuf_feedback(
surface,
render_element_states,
&feedback.render_feedback,
&feedback.scanout_feedback,
)
},
)
}
}
});
2022-09-28 12:01:29 +02:00
let map = smithay::desktop::layer_map_for_output(output);
for layer_surface in map.layers() {
2023-03-31 13:57:37 +02:00
if let Some(feedback) =
advertised_node_for_surface(layer_surface.wl_surface(), &self.display_handle)
2023-03-31 13:57:37 +02:00
.and_then(|source| dmabuf_feedback(source))
{
layer_surface.send_dmabuf_feedback(
output,
surface_primary_scanout_output,
|surface, _| {
select_dmabuf_feedback(
surface,
render_element_states,
&feedback.render_feedback,
&feedback.scanout_feedback,
)
},
);
}
2022-09-28 12:01:29 +02:00
}
}
2022-11-17 20:32:54 +01:00
pub fn send_frames(&self, output: &Output, sequence: Option<usize>) {
2024-06-07 19:44:59 +02:00
let time = self.clock.now();
let should_send = |surface: &WlSurface, states: &SurfaceData| {
// Do the standard primary scanout output check. For pointer surfaces it deduplicates
// the frame callbacks across potentially multiple outputs, and for regular windows and
// layer-shell surfaces it avoids sending frame callbacks to invisible surfaces.
let current_primary_output = surface_primary_scanout_output(surface, states);
if current_primary_output.as_ref() != Some(output) {
return None;
}
let Some(sequence) = sequence else {
return Some(output.clone());
};
// Next, check the throttling status.
let frame_throttling_state = states
.data_map
.get_or_insert(SurfaceFrameThrottlingState::default);
let mut last_sent_at = frame_throttling_state.last_sent_at.borrow_mut();
let mut send = true;
// If we already sent a frame callback to this surface this output refresh
// cycle, don't send one again to prevent empty-damage commit busy loops.
if let Some((last_output, last_sequence)) = &*last_sent_at {
if last_output == output && *last_sequence == sequence {
send = false;
}
}
if send {
*last_sent_at = Some((output.clone(), sequence));
Some(output.clone())
} else {
None
}
};
const THROTTLE: Option<Duration> = Some(Duration::from_millis(995));
const SCREENCOPY_THROTTLE: Option<Duration> = Some(Duration::from_nanos(16_666_666));
fn throttle(session_holder: &impl SessionHolder) -> Option<Duration> {
if session_holder.sessions().is_empty() && session_holder.cursor_sessions().is_empty() {
THROTTLE
} else {
SCREENCOPY_THROTTLE
}
}
2024-04-10 15:49:08 +02:00
let shell = self.shell.read().unwrap();
2022-11-17 20:32:54 +01:00
2024-06-07 19:44:59 +02:00
if let Some(session_lock) = shell.session_lock.as_ref() {
if let Some(lock_surface) = session_lock.surfaces.get(output) {
send_frames_surface_tree(
lock_surface.wl_surface(),
output,
time,
None,
should_send,
);
2024-06-07 19:44:59 +02:00
}
}
for seat in shell
.seats
.iter()
.filter(|seat| &seat.active_output() == output)
{
let cursor_status = seat
.user_data()
.get::<Mutex<CursorImageStatus>>()
.map(|lock| {
let mut cursor_status = lock.lock().unwrap();
if let CursorImageStatus::Surface(ref surface) = *cursor_status {
if !surface.alive() {
*cursor_status = CursorImageStatus::default_named();
}
}
cursor_status.clone()
})
.unwrap_or(CursorImageStatus::default_named());
if let CursorImageStatus::Surface(wl_surface) = cursor_status {
send_frames_surface_tree(
&wl_surface,
output,
time,
Some(Duration::ZERO),
should_send,
)
2024-06-07 19:44:59 +02:00
}
if let Some(move_grab) = seat.user_data().get::<SeatMoveGrabState>() {
if let Some(grab_state) = move_grab.lock().unwrap().as_ref() {
for (window, _) in grab_state.element().windows() {
window.send_frame(output, time, throttle(&window), should_send);
2024-06-07 19:44:59 +02:00
}
}
}
}
shell
.workspaces
.sets
.get(output)
.unwrap()
.sticky_layer
.mapped()
.for_each(|mapped| {
for (window, _) in mapped.windows() {
window.send_frame(output, time, throttle(&window), should_send);
2024-06-07 19:44:59 +02:00
}
});
if let Some(active) = shell.active_space(output) {
active.mapped().for_each(|mapped| {
2024-06-07 19:44:59 +02:00
for (window, _) in mapped.windows() {
window.send_frame(output, time, throttle(&window), should_send);
2024-06-07 19:44:59 +02:00
}
});
// other (throttled) windows
active.minimized_windows.iter().for_each(|m| {
2024-06-07 19:44:59 +02:00
for (window, _) in m.window.windows() {
window.send_frame(output, time, throttle(&window), |_, _| None);
2024-06-07 19:44:59 +02:00
}
});
for space in shell
.workspaces
.spaces_for_output(output)
.filter(|w| w.handle != active.handle)
{
space.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() {
let throttle = min(throttle(space), throttle(&window));
window.send_frame(output, time, throttle, |_, _| None);
}
});
space.minimized_windows.iter().for_each(|m| {
for (window, _) in m.window.windows() {
window.send_frame(output, time, throttle(&window), |_, _| None);
}
})
}
2024-06-07 19:44:59 +02:00
}
2024-04-10 15:49:08 +02:00
shell.override_redirect_windows.iter().for_each(|or| {
if let Some(wl_surface) = or.wl_surface() {
send_frames_surface_tree(&wl_surface, output, time, THROTTLE, should_send);
}
});
2022-11-17 20:32:54 +01:00
let map = smithay::desktop::layer_map_for_output(output);
for layer_surface in map.layers() {
layer_surface.send_frame(output, time, THROTTLE, should_send);
2022-11-17 20:32:54 +01:00
}
}
2022-09-28 12:01:29 +02:00
}