2022-01-20 19:51:46 +01:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
|
|
2024-11-18 19:47:59 +01:00
|
|
|
use crate::{
|
2025-03-19 14:16:58 +01:00
|
|
|
config::{AdaptiveSync, OutputState, ScreenFilter},
|
2024-11-18 19:47:59 +01:00
|
|
|
shell::Shell,
|
|
|
|
|
state::BackendData,
|
2025-05-21 22:07:46 +02:00
|
|
|
utils::{env::dev_var, prelude::*},
|
2024-11-18 19:47:59 +01:00
|
|
|
};
|
2022-02-04 21:23:27 +01:00
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
use anyhow::{Context, Result};
|
2024-06-07 20:04:39 +02:00
|
|
|
use calloop::LoopSignal;
|
2025-05-21 22:07:46 +02:00
|
|
|
use indexmap::IndexMap;
|
2024-06-07 20:04:39 +02:00
|
|
|
use render::gles::GbmGlowBackend;
|
2022-01-25 15:45:15 +01:00
|
|
|
use smithay::{
|
|
|
|
|
backend::{
|
2023-02-13 17:44:24 +01:00
|
|
|
allocator::{
|
2024-02-07 12:33:32 +01:00
|
|
|
dmabuf::Dmabuf,
|
2024-06-07 20:04:39 +02:00
|
|
|
gbm::{GbmAllocator, GbmBufferFlags},
|
2022-12-27 18:27:29 +01:00
|
|
|
},
|
2025-01-02 17:38:59 +01:00
|
|
|
drm::{output::DrmOutputRenderElements, DrmDeviceFd, DrmNode, NodeType},
|
2024-08-05 11:32:42 -07:00
|
|
|
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
|
2022-04-25 23:00:30 +02:00
|
|
|
input::InputEvent,
|
2022-01-25 15:45:15 +01:00
|
|
|
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
2024-06-07 20:04:39 +02:00
|
|
|
renderer::{glow::GlowRenderer, multigpu::GpuManager},
|
2023-01-03 19:17:51 +01:00
|
|
|
session::{libseat::LibSeatSession, Event as SessionEvent, Session},
|
2025-05-21 22:07:46 +02:00
|
|
|
udev::{primary_gpu, UdevBackend, UdevEvent},
|
2022-01-25 15:45:15 +01:00
|
|
|
},
|
2024-06-07 20:04:39 +02:00
|
|
|
output::Output,
|
2022-01-25 15:45:15 +01:00
|
|
|
reexports::{
|
2024-06-07 20:04:39 +02:00
|
|
|
calloop::{Dispatcher, EventLoop, LoopHandle},
|
2025-01-02 17:38:59 +01:00
|
|
|
drm::{
|
2025-05-21 22:07:46 +02:00
|
|
|
control::{connector::Interface, crtc, Device as _},
|
2025-01-02 17:38:59 +01:00
|
|
|
Device as _,
|
|
|
|
|
},
|
2023-07-28 12:54:02 -07:00
|
|
|
input::{self, Libinput},
|
2024-06-07 20:04:39 +02:00
|
|
|
wayland_server::{Client, DisplayHandle},
|
2023-02-27 13:54:49 -08:00
|
|
|
},
|
2025-01-02 17:38:59 +01:00
|
|
|
utils::{Clock, DevPath, Monotonic, Size},
|
2024-06-26 14:39:03 -07:00
|
|
|
wayland::{
|
|
|
|
|
dmabuf::DmabufGlobal,
|
|
|
|
|
drm_syncobj::{supports_syncobj_eventfd, DrmSyncobjState},
|
|
|
|
|
relative_pointer::RelativePointerManagerState,
|
|
|
|
|
},
|
2022-01-25 15:45:15 +01:00
|
|
|
};
|
2025-01-02 17:38:59 +01:00
|
|
|
use surface::GbmDrmOutput;
|
2023-03-03 19:34:41 +01:00
|
|
|
use tracing::{error, info, trace, warn};
|
2022-02-04 21:23:27 +01:00
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
use std::{
|
2024-06-10 14:59:30 +02:00
|
|
|
borrow::BorrowMut,
|
2022-01-25 15:45:15 +01:00
|
|
|
collections::{HashMap, HashSet},
|
2024-06-07 20:04:39 +02:00
|
|
|
path::Path,
|
2025-01-06 19:23:50 +01:00
|
|
|
sync::{atomic::AtomicBool, Arc, RwLock},
|
2022-01-25 15:45:15 +01:00
|
|
|
};
|
2022-01-20 19:51:46 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
mod device;
|
2022-02-01 13:59:39 +01:00
|
|
|
mod drm_helpers;
|
2024-06-07 20:04:39 +02:00
|
|
|
pub mod render;
|
2022-03-22 12:41:54 +01:00
|
|
|
mod socket;
|
2024-06-07 20:04:39 +02:00
|
|
|
mod surface;
|
2024-08-16 20:49:59 -07:00
|
|
|
pub(crate) use surface::Surface;
|
2022-01-25 15:45:15 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
use device::*;
|
|
|
|
|
pub use surface::Timings;
|
2024-03-12 19:42:48 +01:00
|
|
|
|
2025-01-02 17:38:59 +01:00
|
|
|
use super::render::{init_shaders, output_elements, CursorMode, CLEAR_COLOR};
|
2024-06-10 14:59:30 +02:00
|
|
|
|
2023-07-12 18:54:53 +02:00
|
|
|
#[derive(Debug)]
|
2022-01-25 15:45:15 +01:00
|
|
|
pub struct KmsState {
|
2025-05-21 22:07:46 +02:00
|
|
|
pub drm_devices: IndexMap<DrmNode, Device>,
|
2023-07-28 12:54:02 -07:00
|
|
|
pub input_devices: HashMap<String, input::Device>,
|
2025-05-21 22:07:46 +02:00
|
|
|
pub primary_node: Arc<RwLock<Option<DrmNode>>>,
|
2024-08-05 11:32:42 -07:00
|
|
|
// Mesa llvmpipe renderer, if supported and there are no render nodes
|
|
|
|
|
pub software_renderer: Option<GlowRenderer>,
|
2024-06-07 20:04:39 +02:00
|
|
|
pub api: GpuManager<GbmGlowBackend<DrmDeviceFd>>,
|
|
|
|
|
|
2023-01-03 19:17:51 +01:00
|
|
|
session: LibSeatSession,
|
2024-06-07 20:04:39 +02:00
|
|
|
libinput: Libinput,
|
2024-06-26 14:39:03 -07:00
|
|
|
|
|
|
|
|
pub syncobj_state: Option<DrmSyncobjState>,
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
pub fn init_backend(
|
|
|
|
|
dh: &DisplayHandle,
|
|
|
|
|
event_loop: &mut EventLoop<'static, State>,
|
|
|
|
|
state: &mut State,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
// establish session
|
|
|
|
|
let (session, notifier) = LibSeatSession::new().context("Failed to acquire session")?;
|
2022-01-25 15:45:15 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
// setup input
|
|
|
|
|
let libinput_context = init_libinput(dh, &session, &event_loop.handle())
|
|
|
|
|
.context("Failed to initialize libinput backend")?;
|
2023-07-12 18:54:53 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
// watch for gpu events
|
|
|
|
|
let udev_dispatcher = init_udev(session.seat(), &event_loop.handle())
|
|
|
|
|
.context("Failed to initialize udev connection")?;
|
2024-04-24 17:25:33 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
// handle session events
|
|
|
|
|
let loop_signal = event_loop.get_signal();
|
|
|
|
|
let dispatcher = udev_dispatcher.clone();
|
2024-06-10 20:41:29 +02:00
|
|
|
event_loop
|
2024-06-07 20:04:39 +02:00
|
|
|
.handle()
|
|
|
|
|
.insert_source(notifier, move |event, &mut (), state| match event {
|
|
|
|
|
SessionEvent::ActivateSession => {
|
|
|
|
|
state.resume_session(
|
|
|
|
|
dispatcher.clone(),
|
|
|
|
|
state.common.event_loop_handle.clone(),
|
|
|
|
|
loop_signal.clone(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
SessionEvent::PauseSession => {
|
|
|
|
|
state.pause_session();
|
|
|
|
|
}
|
2024-04-24 17:25:33 +02:00
|
|
|
})
|
2024-06-07 20:04:39 +02:00
|
|
|
.map_err(|err| err.error)
|
|
|
|
|
.context("Failed to initialize session event source")?;
|
|
|
|
|
|
|
|
|
|
// finish backend initialization
|
|
|
|
|
state.backend = BackendData::Kms(KmsState {
|
2025-05-21 22:07:46 +02:00
|
|
|
drm_devices: IndexMap::new(),
|
2024-06-07 20:04:39 +02:00
|
|
|
input_devices: HashMap::new(),
|
2025-05-21 22:07:46 +02:00
|
|
|
primary_node: Arc::new(RwLock::new(None)),
|
|
|
|
|
software_renderer: None,
|
2024-06-07 20:04:39 +02:00
|
|
|
api: GpuManager::new(GbmGlowBackend::new()).context("Failed to initialize gpu backend")?,
|
|
|
|
|
|
|
|
|
|
session,
|
|
|
|
|
libinput: libinput_context,
|
2024-06-26 14:39:03 -07:00
|
|
|
|
|
|
|
|
syncobj_state: None,
|
2024-06-07 20:04:39 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// manually add already present gpus
|
|
|
|
|
for (dev, path) in udev_dispatcher.as_source_ref().device_list() {
|
|
|
|
|
if let Err(err) = state.device_added(dev, path.into(), dh) {
|
|
|
|
|
warn!("Failed to add device {}: {:?}", path.display(), err);
|
|
|
|
|
}
|
2024-04-24 17:25:33 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-21 22:07:46 +02:00
|
|
|
// start x11
|
|
|
|
|
let primary = state.backend.kms().primary_node.read().unwrap().clone();
|
|
|
|
|
state.launch_xwayland(primary);
|
2024-06-26 14:39:03 -07:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2023-03-07 16:37:11 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
fn init_libinput(
|
2022-07-04 16:00:29 +02:00
|
|
|
dh: &DisplayHandle,
|
2024-06-07 20:04:39 +02:00
|
|
|
session: &LibSeatSession,
|
|
|
|
|
evlh: &LoopHandle<'static, State>,
|
|
|
|
|
) -> Result<Libinput> {
|
2022-02-04 21:23:27 +01:00
|
|
|
let mut libinput_context =
|
2023-01-03 19:17:51 +01:00
|
|
|
Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(session.clone().into());
|
2022-02-04 21:23:27 +01:00
|
|
|
libinput_context
|
|
|
|
|
.udev_assign_seat(&session.seat())
|
|
|
|
|
.map_err(|_| anyhow::anyhow!("Failed to assign seat to libinput"))?;
|
2023-02-24 17:41:52 +01:00
|
|
|
let libinput_backend = LibinputInputBackend::new(libinput_context.clone());
|
2022-01-25 15:45:15 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
evlh.insert_source(libinput_backend, move |mut event, _, state| {
|
|
|
|
|
if let InputEvent::DeviceAdded { ref mut device } = &mut event {
|
|
|
|
|
state.common.config.read_device(device);
|
|
|
|
|
state
|
|
|
|
|
.backend
|
|
|
|
|
.kms()
|
|
|
|
|
.input_devices
|
|
|
|
|
.insert(device.name().into(), device.clone());
|
|
|
|
|
} else if let InputEvent::DeviceRemoved { device } = &event {
|
|
|
|
|
state.backend.kms().input_devices.remove(device.name());
|
|
|
|
|
}
|
2024-02-14 14:30:32 +01:00
|
|
|
|
2024-09-18 13:20:17 -07:00
|
|
|
state.process_input_event(event);
|
2022-03-16 20:06:31 +01:00
|
|
|
|
2025-05-20 17:41:27 +02:00
|
|
|
for output in state.common.shell.read().outputs() {
|
2024-06-07 20:04:39 +02:00
|
|
|
state.backend.kms().schedule_render(output);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.map_err(|err| err.error)
|
|
|
|
|
.context("Failed to initialize libinput event source")?;
|
|
|
|
|
|
|
|
|
|
// Create relative pointer global
|
|
|
|
|
RelativePointerManagerState::new::<State>(&dh);
|
|
|
|
|
|
|
|
|
|
Ok(libinput_context)
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 22:07:46 +02:00
|
|
|
fn determine_boot_gpu(seat: String) -> Option<DrmNode> {
|
|
|
|
|
let primary_node = primary_gpu(&seat)
|
2022-03-16 20:06:31 +01:00
|
|
|
.ok()
|
2025-05-21 22:07:46 +02:00
|
|
|
.flatten()
|
|
|
|
|
.and_then(|x| DrmNode::from_path(x).ok());
|
|
|
|
|
primary_node.and_then(|x| x.node_with_type(NodeType::Render).and_then(Result::ok))
|
|
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
|
2025-05-21 22:07:46 +02:00
|
|
|
fn determine_primary_gpu(
|
|
|
|
|
drm_devices: &IndexMap<DrmNode, Device>,
|
|
|
|
|
seat: String,
|
|
|
|
|
) -> Result<Option<DrmNode>> {
|
|
|
|
|
if let Some(device) = dev_var("COSMIC_RENDER_DEVICE") {
|
|
|
|
|
if let Some(node) = drm_devices
|
|
|
|
|
.values()
|
|
|
|
|
.find_map(|dev| device.matches(&dev.render_node).then_some(dev.render_node))
|
|
|
|
|
{
|
|
|
|
|
return Ok(Some(node));
|
|
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
}
|
2025-05-21 22:07:46 +02:00
|
|
|
|
|
|
|
|
// try to find builtin display
|
|
|
|
|
for dev in drm_devices.values() {
|
|
|
|
|
if dev.surfaces.values().any(|s| {
|
|
|
|
|
if let Some(conn_info) = dev.drm.device().get_connector(s.connector, false).ok() {
|
|
|
|
|
let i = conn_info.interface();
|
|
|
|
|
i == Interface::EmbeddedDisplayPort || i == Interface::LVDS || i == Interface::DSI
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}) {
|
|
|
|
|
return Ok(Some(dev.render_node));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// else try to find the boot gpu
|
|
|
|
|
let boot = determine_boot_gpu(seat);
|
|
|
|
|
if let Some(boot) = boot {
|
|
|
|
|
if drm_devices.values().any(|dev| dev.render_node == boot) {
|
|
|
|
|
return Ok(Some(boot));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// else just take the first
|
|
|
|
|
Ok(drm_devices.values().next().map(|dev| dev.render_node))
|
2024-06-07 20:04:39 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-05 11:32:42 -07:00
|
|
|
/// Create `GlowRenderer` for `EGL_MESA_device_software` device, if present
|
|
|
|
|
fn software_renderer() -> anyhow::Result<GlowRenderer> {
|
|
|
|
|
let mut devices = EGLDevice::enumerate()?;
|
|
|
|
|
let device = devices
|
|
|
|
|
.find(|device| {
|
|
|
|
|
device
|
|
|
|
|
.extensions()
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|ext| ext == "EGL_MESA_device_software")
|
|
|
|
|
})
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("no EGL device found with `EGL_MESA_device_software`"))?;
|
|
|
|
|
let display = unsafe { EGLDisplay::new(device)? };
|
|
|
|
|
let context = EGLContext::new(&display)?;
|
|
|
|
|
unsafe { Ok(GlowRenderer::new(context)?) }
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
fn init_udev(
|
|
|
|
|
seat: String,
|
|
|
|
|
evlh: &LoopHandle<'static, State>,
|
|
|
|
|
) -> Result<Dispatcher<'static, UdevBackend, State>> {
|
|
|
|
|
let udev_backend = UdevBackend::new(&seat)?;
|
2022-03-16 20:06:31 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
let dispatcher = Dispatcher::new(udev_backend, move |event, _, state: &mut State| {
|
2023-09-29 21:33:16 +02:00
|
|
|
let dh = state.common.display_handle.clone();
|
2022-04-26 12:28:50 +02:00
|
|
|
match match event {
|
2023-09-29 21:33:16 +02:00
|
|
|
UdevEvent::Added { device_id, path } => state
|
2024-01-23 15:24:52 +00:00
|
|
|
.device_added(device_id, path, &dh)
|
2022-04-26 12:28:50 +02:00
|
|
|
.with_context(|| format!("Failed to add drm device: {}", device_id)),
|
2023-09-29 21:33:16 +02:00
|
|
|
UdevEvent::Changed { device_id } => state
|
2022-04-26 12:28:50 +02:00
|
|
|
.device_changed(device_id)
|
|
|
|
|
.with_context(|| format!("Failed to update drm device: {}", device_id)),
|
2023-09-29 21:33:16 +02:00
|
|
|
UdevEvent::Removed { device_id } => state
|
|
|
|
|
.device_removed(device_id, &dh)
|
2022-04-26 12:28:50 +02:00
|
|
|
.with_context(|| format!("Failed to remove drm device: {}", device_id)),
|
|
|
|
|
} {
|
|
|
|
|
Ok(()) => {
|
2023-03-03 19:34:41 +01:00
|
|
|
trace!("Successfully handled udev event.")
|
2022-04-26 12:28:50 +02:00
|
|
|
}
|
|
|
|
|
Err(err) => {
|
2023-02-24 17:41:52 +01:00
|
|
|
error!(?err, "Error while handling udev event.")
|
2022-04-26 12:28:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-02-27 13:54:49 -08:00
|
|
|
|
2024-06-10 20:41:29 +02:00
|
|
|
evlh.register_dispatcher(dispatcher.clone())
|
|
|
|
|
.context("Failed to register udev event source")?;
|
2023-01-18 20:23:41 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
Ok(dispatcher)
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl State {
|
2024-06-07 20:04:39 +02:00
|
|
|
fn resume_session(
|
|
|
|
|
&mut self,
|
|
|
|
|
dispatcher: Dispatcher<'static, UdevBackend, Self>,
|
|
|
|
|
loop_handle: LoopHandle<'static, State>,
|
|
|
|
|
loop_signal: LoopSignal,
|
|
|
|
|
) {
|
|
|
|
|
let backend = self.backend.kms();
|
2023-10-05 22:01:21 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
// resume input
|
|
|
|
|
if let Err(err) = backend.libinput.resume() {
|
|
|
|
|
error!(?err, "Failed to resume libinput context.");
|
|
|
|
|
}
|
|
|
|
|
// active drm, resume leases
|
|
|
|
|
for device in backend.drm_devices.values_mut() {
|
|
|
|
|
if let Err(err) = device.drm.activate(true) {
|
|
|
|
|
error!(?err, "Failed to resume drm device");
|
2022-11-21 10:10:50 +01:00
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
if let Some(lease_state) = device.leasing_global.as_mut() {
|
|
|
|
|
lease_state.resume::<State>();
|
2024-01-17 11:34:19 +00:00
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
2022-05-03 13:37:51 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
// update state and schedule new render,
|
|
|
|
|
// after processing the rest of the pending event loop events
|
|
|
|
|
let dispatcher = dispatcher.clone();
|
|
|
|
|
loop_handle.insert_idle(move |state| {
|
|
|
|
|
// add new devices, update devices now
|
|
|
|
|
for (dev, path) in dispatcher.as_source_ref().device_list() {
|
|
|
|
|
let drm_node = match DrmNode::from_dev_id(dev) {
|
|
|
|
|
Ok(node) => node,
|
2024-01-17 11:34:19 +00:00
|
|
|
Err(err) => {
|
2024-06-07 20:04:39 +02:00
|
|
|
error!(?err, "Failed to read drm device {}.", path.display(),);
|
|
|
|
|
continue;
|
2024-01-17 11:34:19 +00:00
|
|
|
}
|
|
|
|
|
};
|
2024-06-07 20:04:39 +02:00
|
|
|
if state.backend.kms().drm_devices.contains_key(&drm_node) {
|
|
|
|
|
if let Err(err) = state.device_changed(dev) {
|
|
|
|
|
error!(?err, "Failed to update drm device {}.", path.display(),);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let dh = state.common.display_handle.clone();
|
|
|
|
|
if let Err(err) = state.device_added(dev, path.into(), &dh) {
|
|
|
|
|
error!(?err, "Failed to add drm device {}.", path.display(),);
|
2023-10-05 22:01:21 +02:00
|
|
|
}
|
2022-11-21 10:10:50 +01:00
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
2022-04-05 16:35:58 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
// update outputs
|
|
|
|
|
state.common.config.read_outputs(
|
|
|
|
|
&mut state.common.output_configuration_state,
|
|
|
|
|
&mut state.backend,
|
|
|
|
|
&state.common.shell,
|
|
|
|
|
&state.common.event_loop_handle,
|
|
|
|
|
&mut state.common.workspace_state.update(),
|
|
|
|
|
&state.common.xdg_activation_state,
|
|
|
|
|
state.common.startup_done.clone(),
|
2025-01-02 17:38:59 +01:00
|
|
|
&state.common.clock,
|
2024-04-10 15:49:08 +02:00
|
|
|
);
|
2024-06-07 20:04:39 +02:00
|
|
|
state.common.refresh();
|
|
|
|
|
});
|
|
|
|
|
loop_signal.wakeup();
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
fn pause_session(&mut self) {
|
2023-02-14 23:15:58 +01:00
|
|
|
let backend = self.backend.kms();
|
2024-06-07 20:04:39 +02:00
|
|
|
backend.libinput.suspend();
|
|
|
|
|
for device in backend.drm_devices.values_mut() {
|
|
|
|
|
device.drm.pause();
|
|
|
|
|
if let Some(lease_state) = device.leasing_global.as_mut() {
|
|
|
|
|
lease_state.suspend();
|
2023-10-05 22:01:21 +02:00
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
for surface in device.surfaces.values_mut() {
|
2024-06-07 20:04:39 +02:00
|
|
|
surface.suspend();
|
2022-08-11 17:13:56 +02:00
|
|
|
}
|
2022-04-26 12:28:50 +02:00
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
impl KmsState {
|
2025-05-21 22:07:46 +02:00
|
|
|
fn select_primary_gpu(&mut self, dh: &DisplayHandle) -> Result<()> {
|
|
|
|
|
// We don't have to check the allow/blocklist here,
|
|
|
|
|
// as any disallowed devices won't be in `self.drm_devices`.
|
|
|
|
|
|
|
|
|
|
let mut primary_node = self.primary_node.write().unwrap();
|
|
|
|
|
let _ = primary_node.take(); // if we error don't leave an old node in place
|
|
|
|
|
*primary_node = determine_primary_gpu(&self.drm_devices, self.session.seat())?;
|
|
|
|
|
|
|
|
|
|
if let Some(node) = *primary_node {
|
|
|
|
|
info!("Using {} as primary gpu for rendering.", node);
|
|
|
|
|
self.software_renderer.take();
|
|
|
|
|
} else if self.software_renderer.is_none() {
|
|
|
|
|
info!("Failed to find a suitable gpu, using software renderingr");
|
|
|
|
|
self.software_renderer = match software_renderer() {
|
|
|
|
|
Ok(renderer) => Some(renderer),
|
|
|
|
|
Err(err) => {
|
|
|
|
|
error!(?err, "Failed to initialize software EGL renderer.");
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !crate::utils::env::bool_var("COSMIC_DISABLE_SYNCOBJ").unwrap_or(false) {
|
|
|
|
|
if let Some(primary_node) = primary_node
|
|
|
|
|
.as_ref()
|
|
|
|
|
.and_then(|node| node.node_with_type(NodeType::Primary).and_then(|x| x.ok()))
|
|
|
|
|
{
|
|
|
|
|
if let Some(device) = self.drm_devices.get(&primary_node) {
|
|
|
|
|
let import_device = device.drm.device().device_fd().clone();
|
|
|
|
|
if supports_syncobj_eventfd(&import_device) {
|
|
|
|
|
if let Some(state) = self.syncobj_state.as_mut() {
|
|
|
|
|
state.update_device(import_device);
|
|
|
|
|
} else {
|
|
|
|
|
let syncobj_state = DrmSyncobjState::new::<State>(&dh, import_device);
|
|
|
|
|
self.syncobj_state = Some(syncobj_state);
|
|
|
|
|
}
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(old_state) = self.syncobj_state.take() {
|
|
|
|
|
dh.remove_global::<State>(old_state.into_global());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
pub fn switch_vt(&mut self, num: i32) -> Result<(), anyhow::Error> {
|
|
|
|
|
self.session.change_vt(num).map_err(Into::into)
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
pub fn dmabuf_imported(
|
2022-01-25 15:45:15 +01:00
|
|
|
&mut self,
|
2024-06-07 20:04:39 +02:00
|
|
|
_client: Option<Client>,
|
|
|
|
|
global: &DmabufGlobal,
|
|
|
|
|
dmabuf: Dmabuf,
|
|
|
|
|
) -> Result<DrmNode> {
|
|
|
|
|
let (expected_node, other_nodes) =
|
|
|
|
|
self.drm_devices
|
|
|
|
|
.values_mut()
|
|
|
|
|
.partition::<Vec<_>, _>(|device| {
|
|
|
|
|
device
|
|
|
|
|
.socket
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|s| &s.dmabuf_global == global)
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
});
|
2022-01-25 15:45:15 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
let mut last_err = anyhow::anyhow!("Dmabuf cannot be imported on any gpu");
|
|
|
|
|
for device in expected_node.into_iter().chain(other_nodes.into_iter()) {
|
|
|
|
|
let mut _egl = None;
|
|
|
|
|
let egl_display = if let Some(egl_display) =
|
|
|
|
|
device.egl.as_ref().map(|internals| &internals.display)
|
|
|
|
|
{
|
|
|
|
|
egl_display
|
|
|
|
|
} else {
|
|
|
|
|
_egl = Some(init_egl(&device.gbm).context("Failed to initialize egl context")?);
|
|
|
|
|
&_egl.as_ref().unwrap().display
|
|
|
|
|
};
|
2022-01-25 15:45:15 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
let result = egl_display
|
|
|
|
|
.create_image_from_dmabuf(&dmabuf)
|
2024-06-20 21:04:31 +02:00
|
|
|
.map(|image| {
|
|
|
|
|
unsafe {
|
|
|
|
|
smithay::backend::egl::ffi::egl::DestroyImageKHR(
|
|
|
|
|
**egl_display.get_display_handle(),
|
|
|
|
|
image,
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
device.render_node
|
|
|
|
|
})
|
2024-06-07 20:04:39 +02:00
|
|
|
.map_err(Into::into);
|
2024-01-17 11:34:19 +00:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
match result {
|
|
|
|
|
Ok(node) => {
|
|
|
|
|
dmabuf.set_node(node); // so the MultiRenderer knows what node to use
|
|
|
|
|
return Ok(node);
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
trace!(?err, "Failed to import dmabuf on {:?}", device.render_node);
|
|
|
|
|
last_err = err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-31 13:57:37 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
Err(last_err)
|
2024-01-17 11:34:19 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
pub fn schedule_render(&mut self, output: &Output) {
|
|
|
|
|
for surface in self
|
|
|
|
|
.drm_devices
|
|
|
|
|
.values()
|
|
|
|
|
.flat_map(|d| d.surfaces.values())
|
|
|
|
|
.filter(|s| s.output == *output || s.output.mirroring().is_some_and(|o| &o == output))
|
|
|
|
|
{
|
|
|
|
|
surface.schedule_render();
|
|
|
|
|
}
|
2022-07-04 16:00:29 +02:00
|
|
|
}
|
2022-05-16 18:11:24 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
pub fn target_node_for_output(&self, output: &Output) -> Option<DrmNode> {
|
|
|
|
|
self.drm_devices
|
|
|
|
|
.values()
|
|
|
|
|
.find(|dev| dev.surfaces.values().any(|s| s.output == *output))
|
|
|
|
|
.map(|dev| &dev.render_node)
|
|
|
|
|
.copied()
|
2023-03-31 13:57:37 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
pub fn refresh_used_devices(&mut self) -> Result<()> {
|
|
|
|
|
let mut used_devices = HashSet::new();
|
2022-03-22 12:41:54 +01:00
|
|
|
|
2024-06-18 18:19:39 +02:00
|
|
|
for device in self.drm_devices.values_mut() {
|
2025-05-21 22:07:46 +02:00
|
|
|
if device.in_use(self.primary_node.read().unwrap().as_ref()) {
|
2024-06-07 20:04:39 +02:00
|
|
|
if device.egl.is_none() {
|
|
|
|
|
let egl = init_egl(&device.gbm).context("Failed to create EGL context")?;
|
2024-06-10 14:59:30 +02:00
|
|
|
let mut renderer = unsafe {
|
|
|
|
|
GlowRenderer::new(
|
|
|
|
|
EGLContext::new_shared_with_priority(
|
|
|
|
|
&egl.display,
|
|
|
|
|
&egl.context,
|
|
|
|
|
ContextPriority::High,
|
|
|
|
|
)
|
|
|
|
|
.context("Failed to create shared EGL context")?,
|
|
|
|
|
)
|
|
|
|
|
.context("Failed to create GL renderer")?
|
|
|
|
|
};
|
|
|
|
|
init_shaders(renderer.borrow_mut()).context("Failed to compile shaders")?;
|
2024-06-07 20:04:39 +02:00
|
|
|
self.api.as_mut().add_node(
|
|
|
|
|
device.render_node,
|
|
|
|
|
GbmAllocator::new(
|
|
|
|
|
device.gbm.clone(),
|
|
|
|
|
// SCANOUT because stride bugs
|
|
|
|
|
GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT,
|
|
|
|
|
),
|
2024-06-10 14:59:30 +02:00
|
|
|
renderer,
|
2024-06-07 20:04:39 +02:00
|
|
|
);
|
|
|
|
|
device.egl = Some(egl);
|
|
|
|
|
}
|
2024-06-18 18:19:39 +02:00
|
|
|
used_devices.insert(device.render_node);
|
2024-06-07 20:04:39 +02:00
|
|
|
} else {
|
2024-06-18 18:19:39 +02:00
|
|
|
if device.egl.is_some() {
|
2024-06-07 20:04:39 +02:00
|
|
|
let _ = device.egl.take();
|
|
|
|
|
self.api.as_mut().remove_node(&device.render_node);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-07 22:20:44 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-18 18:19:39 +02:00
|
|
|
// trigger re-evaluation... urgh
|
2025-05-21 22:07:46 +02:00
|
|
|
if let Some(primary_node) = self.primary_node.read().unwrap().as_ref() {
|
2024-06-18 18:19:39 +02:00
|
|
|
let _ = self.api.single_renderer(primary_node);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
// I hate this. I want partial borrows of hashmap values
|
2024-06-18 18:19:39 +02:00
|
|
|
let all_devices = self
|
|
|
|
|
.drm_devices
|
|
|
|
|
.values()
|
|
|
|
|
.map(|d| d.render_node)
|
|
|
|
|
.collect::<Vec<_>>();
|
2024-06-07 20:04:39 +02:00
|
|
|
for node in all_devices {
|
|
|
|
|
let (mut device, mut others) = self
|
|
|
|
|
.drm_devices
|
2024-06-18 18:19:39 +02:00
|
|
|
.values_mut()
|
|
|
|
|
.partition::<Vec<_>, _>(|d| d.render_node == node);
|
|
|
|
|
let device = &mut device[0];
|
2024-03-12 19:42:48 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
for surface in device.surfaces.values_mut() {
|
|
|
|
|
let known_nodes = surface.known_nodes().clone();
|
|
|
|
|
for gone_device in known_nodes.difference(&used_devices) {
|
|
|
|
|
surface.remove_node(*gone_device);
|
2024-04-24 17:25:33 +02:00
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
for new_device in used_devices.difference(&known_nodes) {
|
|
|
|
|
let (render_node, egl, gbm) = if node == *new_device {
|
|
|
|
|
// we need to make sure to do partial borrows here, as device.surfaces is borrowed mutable
|
|
|
|
|
(
|
|
|
|
|
device.render_node,
|
|
|
|
|
device.egl.as_ref().unwrap(),
|
|
|
|
|
device.gbm.clone(),
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
let device = others
|
|
|
|
|
.iter_mut()
|
2024-06-18 18:19:39 +02:00
|
|
|
.find(|d| d.render_node == *new_device)
|
2024-06-07 20:04:39 +02:00
|
|
|
.unwrap();
|
|
|
|
|
(
|
|
|
|
|
device.render_node,
|
|
|
|
|
device.egl.as_ref().unwrap(),
|
|
|
|
|
device.gbm.clone(),
|
|
|
|
|
)
|
2024-04-24 17:25:33 +02:00
|
|
|
};
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
surface.add_node(
|
|
|
|
|
render_node,
|
|
|
|
|
GbmAllocator::new(gbm, GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT),
|
|
|
|
|
EGLContext::new_shared_with_priority(
|
|
|
|
|
&egl.display,
|
|
|
|
|
&egl.context,
|
|
|
|
|
ContextPriority::High,
|
2024-04-24 17:25:33 +02:00
|
|
|
)
|
2024-06-07 20:04:39 +02:00
|
|
|
.context("Failed to create shared EGL context")?,
|
|
|
|
|
);
|
2024-04-24 17:25:33 +02:00
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
2023-03-07 16:37:11 +01:00
|
|
|
}
|
2022-11-06 11:50:09 +01:00
|
|
|
|
2022-01-25 15:45:15 +01:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2022-01-28 18:54:25 +01:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
pub fn apply_config_for_outputs(
|
2022-04-05 16:35:58 +02:00
|
|
|
&mut self,
|
2022-04-13 22:59:14 +02:00
|
|
|
test_only: bool,
|
2024-06-07 20:04:39 +02:00
|
|
|
loop_handle: &LoopHandle<'static, State>,
|
2025-03-19 14:16:58 +01:00
|
|
|
screen_filter: &ScreenFilter,
|
2025-05-20 17:41:27 +02:00
|
|
|
shell: Arc<parking_lot::RwLock<Shell>>,
|
2024-06-11 17:24:46 +02:00
|
|
|
startup_done: Arc<AtomicBool>,
|
2025-01-02 17:38:59 +01:00
|
|
|
clock: &Clock<Monotonic>,
|
2024-06-07 20:04:39 +02:00
|
|
|
) -> Result<Vec<Output>, anyhow::Error> {
|
|
|
|
|
if !self.session.is_active() {
|
|
|
|
|
return Ok(Vec::new());
|
|
|
|
|
}
|
2024-04-24 17:25:33 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
for device in self.drm_devices.values_mut() {
|
|
|
|
|
// we only want outputs exposed to wayland - not leased ones
|
|
|
|
|
// but that is also not all surface, because that doesn't contain all detected, but unmapped outputs
|
|
|
|
|
let outputs = device
|
|
|
|
|
.outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(conn, _)| {
|
|
|
|
|
!device
|
|
|
|
|
.leased_connectors
|
2024-04-24 17:25:33 +02:00
|
|
|
.iter()
|
2024-06-07 20:04:39 +02:00
|
|
|
.any(|(leased_conn, _)| *conn == leased_conn)
|
|
|
|
|
})
|
|
|
|
|
.map(|(_, output)| output.clone())
|
|
|
|
|
.collect::<Vec<_>>();
|
2022-04-05 16:35:58 +02:00
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
let mut new_pairings = HashMap::new();
|
|
|
|
|
|
|
|
|
|
// figure out potential new crtcs
|
|
|
|
|
// TODO: Right now we always keep crtcs of already enabled outputs,
|
|
|
|
|
// even if another configuration could potentially enable more outputs
|
|
|
|
|
|
2025-01-02 17:38:59 +01:00
|
|
|
let res_handles = device.drm.device().resource_handles()?;
|
2024-06-07 20:04:39 +02:00
|
|
|
let free_crtcs = res_handles
|
|
|
|
|
.crtcs()
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|crtc| {
|
|
|
|
|
!device
|
|
|
|
|
.surfaces
|
|
|
|
|
.get(crtc)
|
|
|
|
|
.is_some_and(|surface| surface.output.is_enabled())
|
|
|
|
|
})
|
|
|
|
|
.copied()
|
|
|
|
|
.collect::<HashSet<crtc::Handle>>();
|
|
|
|
|
let open_conns = outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|output| {
|
|
|
|
|
output.is_enabled() && !device.surfaces.values().any(|s| &s.output == *output)
|
|
|
|
|
})
|
|
|
|
|
.flat_map(|output| {
|
|
|
|
|
device
|
|
|
|
|
.outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.find_map(|(conn, o)| (output == o).then_some(*conn))
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for conn in open_conns {
|
2025-01-02 17:38:59 +01:00
|
|
|
let conn_info = device
|
|
|
|
|
.drm
|
|
|
|
|
.device()
|
|
|
|
|
.get_connector(conn, false)
|
|
|
|
|
.with_context(|| {
|
|
|
|
|
format!(
|
|
|
|
|
"Failed to query drm device: {:?}",
|
|
|
|
|
device.drm.device().dev_path().as_deref().map(Path::display),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
2024-06-07 20:04:39 +02:00
|
|
|
'outer: for encoder_info in conn_info
|
|
|
|
|
.encoders()
|
|
|
|
|
.iter()
|
2025-01-02 17:38:59 +01:00
|
|
|
.flat_map(|encoder_handle| device.drm.device().get_encoder(*encoder_handle))
|
2024-06-07 20:04:39 +02:00
|
|
|
{
|
|
|
|
|
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
2024-11-18 18:36:44 +01:00
|
|
|
if free_crtcs.contains(&crtc) {
|
2024-06-07 20:04:39 +02:00
|
|
|
new_pairings.insert(conn, crtc);
|
|
|
|
|
break 'outer;
|
|
|
|
|
}
|
2022-04-13 22:59:14 +02:00
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
|
|
|
|
|
// test failed, we don't have a crtc for conn
|
|
|
|
|
anyhow::bail!("Missing crtc for {conn:?}, gpu doesn't have enough resources.");
|
2022-04-13 22:59:14 +02:00
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// first drop old surfaces
|
|
|
|
|
if !test_only {
|
|
|
|
|
for output in outputs.iter().filter(|o| !o.is_enabled()) {
|
|
|
|
|
device
|
|
|
|
|
.surfaces
|
|
|
|
|
.retain(|_, surface| surface.output != *output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-18 18:36:44 +01:00
|
|
|
// add new ones
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut w = shell.read().global_space().size.w as u32;
|
2024-11-18 18:36:44 +01:00
|
|
|
if !test_only {
|
|
|
|
|
for (conn, crtc) in new_pairings {
|
|
|
|
|
let (output, _) = device.connector_added(
|
2025-05-21 22:07:46 +02:00
|
|
|
self.primary_node.clone(),
|
2024-11-18 18:36:44 +01:00
|
|
|
conn,
|
|
|
|
|
Some(crtc),
|
|
|
|
|
(w, 0),
|
|
|
|
|
loop_handle,
|
2025-03-19 14:16:58 +01:00
|
|
|
screen_filter.clone(),
|
2024-11-18 18:36:44 +01:00
|
|
|
shell.clone(),
|
|
|
|
|
startup_done.clone(),
|
|
|
|
|
)?;
|
|
|
|
|
if output.mirroring().is_none() {
|
2025-01-06 19:47:12 +01:00
|
|
|
w += output.geometry().size.w as u32;
|
2024-11-18 18:36:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !test_only {
|
|
|
|
|
self.refresh_used_devices()
|
|
|
|
|
.context("Failed to enable devices")?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut all_outputs = Vec::new();
|
|
|
|
|
for device in self.drm_devices.values_mut() {
|
2025-01-02 17:38:59 +01:00
|
|
|
let now = clock.now();
|
2025-01-06 19:23:50 +01:00
|
|
|
let output_map = device
|
2025-01-02 17:38:59 +01:00
|
|
|
.surfaces
|
|
|
|
|
.iter()
|
2025-01-20 18:10:50 +01:00
|
|
|
.filter(|(_, s)| s.is_active())
|
2025-01-02 17:38:59 +01:00
|
|
|
.map(|(crtc, surface)| (*crtc, surface.output.clone()))
|
|
|
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
|
|
2025-01-02 21:32:47 +01:00
|
|
|
// configure primary scanout allowance
|
|
|
|
|
if !device.surfaces.is_empty() {
|
|
|
|
|
let mut renderer = self
|
|
|
|
|
.api
|
|
|
|
|
.single_renderer(&device.render_node)
|
|
|
|
|
.with_context(|| "Failed to create renderer")?;
|
|
|
|
|
|
|
|
|
|
device
|
|
|
|
|
.allow_primary_scanout_any(
|
|
|
|
|
device
|
|
|
|
|
.surfaces
|
|
|
|
|
.values()
|
2025-01-06 19:23:50 +01:00
|
|
|
.filter(|s| s.output.is_enabled() && s.output.mirroring().is_none())
|
2025-01-02 21:32:47 +01:00
|
|
|
.count()
|
|
|
|
|
<= 1,
|
|
|
|
|
&mut renderer,
|
|
|
|
|
clock,
|
|
|
|
|
&shell,
|
|
|
|
|
)
|
|
|
|
|
.context("Failed to switch primary-plane scanout flags")?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reconfigure existing
|
2024-06-07 20:04:39 +02:00
|
|
|
for (crtc, surface) in device.surfaces.iter_mut() {
|
|
|
|
|
let output_config = surface.output.config();
|
|
|
|
|
|
2023-02-27 23:53:27 +01:00
|
|
|
let drm = &mut device.drm;
|
2022-04-13 22:59:14 +02:00
|
|
|
let conn = surface.connector;
|
2025-01-02 17:38:59 +01:00
|
|
|
let conn_info = drm.device().get_connector(conn, false)?;
|
2022-04-13 22:59:14 +02:00
|
|
|
let mode = conn_info
|
|
|
|
|
.modes()
|
|
|
|
|
.iter()
|
2022-07-19 14:41:04 +02:00
|
|
|
// match the size
|
|
|
|
|
.filter(|mode| {
|
|
|
|
|
let (x, y) = mode.size();
|
|
|
|
|
Size::from((x as i32, y as i32)) == output_config.mode_size()
|
|
|
|
|
})
|
|
|
|
|
// and then select the closest refresh rate (e.g. to match 59.98 as 60)
|
2022-04-13 22:59:14 +02:00
|
|
|
.min_by_key(|mode| {
|
|
|
|
|
let refresh_rate = drm_helpers::calculate_refresh_rate(**mode);
|
|
|
|
|
(output_config.mode.1.unwrap() as i32 - refresh_rate as i32).abs()
|
|
|
|
|
})
|
2024-06-07 20:04:39 +02:00
|
|
|
.ok_or(anyhow::anyhow!("Unable to find matching mode"))?;
|
2022-04-13 22:59:14 +02:00
|
|
|
|
|
|
|
|
if !test_only {
|
2024-06-07 20:04:39 +02:00
|
|
|
if !surface.is_active() {
|
2025-01-02 17:38:59 +01:00
|
|
|
let compositor: GbmDrmOutput = {
|
|
|
|
|
let mut planes = drm
|
|
|
|
|
.device()
|
|
|
|
|
.planes(crtc)
|
|
|
|
|
.with_context(|| "Failed to enumerate planes")?;
|
|
|
|
|
let driver = drm.device().get_driver().ok();
|
|
|
|
|
|
|
|
|
|
// QUIRK: Using an overlay plane on a nvidia card breaks the display controller (wtf...)
|
|
|
|
|
if driver.is_some_and(|driver| {
|
|
|
|
|
driver
|
|
|
|
|
.name()
|
|
|
|
|
.to_string_lossy()
|
|
|
|
|
.to_lowercase()
|
|
|
|
|
.contains("nvidia")
|
|
|
|
|
}) {
|
|
|
|
|
planes.overlay = vec![];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut renderer = self
|
|
|
|
|
.api
|
|
|
|
|
.single_renderer(&device.render_node)
|
|
|
|
|
.with_context(|| "Failed to create renderer")?;
|
|
|
|
|
|
|
|
|
|
let mut elements = DrmOutputRenderElements::default();
|
|
|
|
|
for (crtc, output) in output_map.iter() {
|
|
|
|
|
let output_elements = output_elements(
|
|
|
|
|
Some(&device.render_node),
|
|
|
|
|
&mut renderer,
|
|
|
|
|
&shell,
|
|
|
|
|
now,
|
|
|
|
|
&output,
|
|
|
|
|
CursorMode::All,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.with_context(|| "Failed to render outputs")?;
|
|
|
|
|
|
|
|
|
|
elements.add_output(crtc, CLEAR_COLOR, output_elements);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let compositor = drm
|
|
|
|
|
.initialize_output(
|
|
|
|
|
*crtc,
|
|
|
|
|
*mode,
|
|
|
|
|
&[conn],
|
|
|
|
|
&surface.output,
|
|
|
|
|
Some(planes),
|
|
|
|
|
&mut renderer,
|
|
|
|
|
&elements,
|
|
|
|
|
)
|
|
|
|
|
.with_context(|| "Failed to create drm surface")?;
|
|
|
|
|
|
|
|
|
|
let _ = renderer;
|
|
|
|
|
|
|
|
|
|
compositor
|
|
|
|
|
};
|
2024-06-07 20:04:39 +02:00
|
|
|
|
2023-04-18 19:14:31 +02:00
|
|
|
if let Some(bpc) = output_config.max_bpc {
|
2025-01-02 17:38:59 +01:00
|
|
|
if let Err(err) = drm_helpers::set_max_bpc(drm.device(), conn, bpc) {
|
2023-04-18 19:14:31 +02:00
|
|
|
warn!(
|
|
|
|
|
?bpc,
|
|
|
|
|
?err,
|
|
|
|
|
"Failed to set max_bpc on connector: {}",
|
2024-06-07 20:04:39 +02:00
|
|
|
surface.output.name()
|
2023-04-18 19:14:31 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-24 17:25:33 +02:00
|
|
|
|
2024-11-18 19:47:59 +01:00
|
|
|
let vrr = output_config.vrr;
|
2024-06-07 20:04:39 +02:00
|
|
|
std::mem::drop(output_config);
|
2024-11-18 19:47:59 +01:00
|
|
|
|
2025-01-02 17:38:59 +01:00
|
|
|
match surface.resume(compositor) {
|
2024-11-18 19:47:59 +01:00
|
|
|
Ok(_) => {
|
2024-11-18 21:30:02 +01:00
|
|
|
surface.output.set_adaptive_sync_support(
|
|
|
|
|
surface.adaptive_sync_support().ok(),
|
|
|
|
|
);
|
2024-11-18 19:47:59 +01:00
|
|
|
if surface.use_adaptive_sync(vrr)? {
|
|
|
|
|
surface.output.set_adaptive_sync(vrr);
|
|
|
|
|
} else {
|
|
|
|
|
surface.output.config_mut().vrr = AdaptiveSync::Disabled;
|
|
|
|
|
surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
surface.output.config_mut().enabled = OutputState::Disabled;
|
|
|
|
|
return Err(err).context("Failed to create surface");
|
|
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
}
|
2024-11-18 19:47:59 +01:00
|
|
|
} else {
|
|
|
|
|
let vrr = output_config.vrr;
|
2024-06-07 20:04:39 +02:00
|
|
|
std::mem::drop(output_config);
|
2024-11-18 19:47:59 +01:00
|
|
|
if vrr != surface.output.adaptive_sync() {
|
|
|
|
|
if surface.use_adaptive_sync(vrr)? {
|
|
|
|
|
surface.output.set_adaptive_sync(vrr);
|
|
|
|
|
} else if vrr != AdaptiveSync::Disabled {
|
|
|
|
|
anyhow::bail!("Requested VRR mode unsupported");
|
|
|
|
|
} else {
|
|
|
|
|
surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-02 17:38:59 +01:00
|
|
|
|
|
|
|
|
let mut renderer = self
|
|
|
|
|
.api
|
|
|
|
|
.single_renderer(&device.render_node)
|
|
|
|
|
.with_context(|| "Failed to create renderer")?;
|
|
|
|
|
|
|
|
|
|
let mut elements = DrmOutputRenderElements::default();
|
|
|
|
|
for (crtc, output) in output_map.iter() {
|
|
|
|
|
let output_elements = output_elements(
|
|
|
|
|
Some(&device.render_node),
|
|
|
|
|
&mut renderer,
|
|
|
|
|
&shell,
|
|
|
|
|
now,
|
|
|
|
|
&output,
|
|
|
|
|
CursorMode::All,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.with_context(|| "Failed to render outputs")?;
|
|
|
|
|
|
|
|
|
|
elements.add_output(crtc, CLEAR_COLOR, output_elements);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drm.use_mode(&surface.crtc, *mode, &mut renderer, &elements)
|
2024-06-10 20:41:29 +02:00
|
|
|
.context("Failed to apply new mode")?;
|
2024-04-24 17:25:33 +02:00
|
|
|
}
|
2024-02-01 18:42:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-17 11:34:19 +00:00
|
|
|
|
2024-11-18 18:36:44 +01:00
|
|
|
all_outputs.extend(
|
|
|
|
|
device
|
|
|
|
|
.outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(conn, _)| {
|
|
|
|
|
!device
|
|
|
|
|
.leased_connectors
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|(leased_conn, _)| *conn == leased_conn)
|
|
|
|
|
})
|
|
|
|
|
.map(|(_, output)| output.clone())
|
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
|
);
|
2025-01-02 17:38:59 +01:00
|
|
|
|
2025-01-07 17:38:49 +01:00
|
|
|
if !device.surfaces.is_empty() {
|
2025-01-02 17:38:59 +01:00
|
|
|
let mut renderer = self
|
|
|
|
|
.api
|
|
|
|
|
.single_renderer(&device.render_node)
|
|
|
|
|
.with_context(|| "Failed to create renderer")?;
|
|
|
|
|
|
|
|
|
|
let mut elements = DrmOutputRenderElements::default();
|
|
|
|
|
for (crtc, output) in output_map.iter() {
|
|
|
|
|
let output_elements = output_elements(
|
|
|
|
|
Some(&device.render_node),
|
|
|
|
|
&mut renderer,
|
|
|
|
|
&shell,
|
|
|
|
|
now,
|
|
|
|
|
&output,
|
|
|
|
|
CursorMode::All,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.with_context(|| "Failed to render outputs")?;
|
|
|
|
|
|
|
|
|
|
elements.add_output(crtc, CLEAR_COLOR, output_elements);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Err(err) = device
|
|
|
|
|
.drm
|
|
|
|
|
.try_to_restore_modifiers(&mut renderer, &elements)
|
|
|
|
|
{
|
|
|
|
|
warn!(?err, "Failed to restore modifiers");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-25 15:45:15 +01:00
|
|
|
}
|
2024-06-07 20:04:39 +02:00
|
|
|
|
2024-06-07 15:44:25 +02:00
|
|
|
// we need to handle mirroring, after all outputs have been enabled
|
|
|
|
|
for device in self.drm_devices.values_mut() {
|
|
|
|
|
for surface in device.surfaces.values_mut() {
|
|
|
|
|
let mirrored_output =
|
|
|
|
|
if let OutputState::Mirroring(conn) = &surface.output.config().enabled {
|
|
|
|
|
Some(
|
|
|
|
|
all_outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|output| &output.name() == conn)
|
|
|
|
|
.cloned()
|
|
|
|
|
.ok_or(anyhow::anyhow!("Unable to find mirroring output"))?,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if !test_only {
|
|
|
|
|
if mirrored_output != surface.output.mirroring() {
|
|
|
|
|
surface.set_mirroring(mirrored_output.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 20:04:39 +02:00
|
|
|
Ok(all_outputs)
|
2022-01-28 18:54:25 +01:00
|
|
|
}
|
2025-03-19 14:16:58 +01:00
|
|
|
|
|
|
|
|
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> Result<()> {
|
|
|
|
|
for device in self.drm_devices.values_mut() {
|
|
|
|
|
for surface in device.surfaces.values_mut() {
|
|
|
|
|
surface.set_screen_filter(screen_filter.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't expect this to fail in a meaningful way.
|
|
|
|
|
// The shader is already compiled at this point and we don't rely on any features,
|
|
|
|
|
// that might not be available for any filters we currently expose.
|
|
|
|
|
//
|
|
|
|
|
// But we might conditionally fail here in the future.
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2022-02-04 21:23:27 +01:00
|
|
|
}
|