Compare commits
No commits in common. "f60c573f4d9c7b52a4118fa2b5ad5601c6cf8fa2" and "b45d12edef7d4b37c12f1c0cdee2845482d448d1" have entirely different histories.
f60c573f4d
...
b45d12edef
39 changed files with 656 additions and 1944 deletions
425
Cargo.lock
generated
425
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -102,7 +102,6 @@ features = [
|
|||
"backend_drm",
|
||||
"backend_gbm",
|
||||
"backend_egl",
|
||||
"backend_libei",
|
||||
"backend_libinput",
|
||||
"backend_session_libseat",
|
||||
"backend_udev",
|
||||
|
|
@ -178,4 +177,4 @@ dnd = { path = "../window_clipboard/dnd" }
|
|||
mime = { path = "../window_clipboard/mime" }
|
||||
|
||||
[patch.crates-io]
|
||||
smithay = { git = "https://github.com/smithay/smithay.git", rev = "5fb12b8" }
|
||||
smithay = { git = "https://github.com/smithay/smithay.git", rev = "1ed69cb" }
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
a11y-zoom-move-continuously = Visningen bevæger sig kontinuerligt med markøren
|
||||
a11y-zoom-move-onedge = Vis bevægelser når markør rammer kant
|
||||
a11y-zoom-move-centered = Vis bevægelser, for at holde markør centreret
|
||||
a11y-zoom-settings = Forstørrelsesglas-indstillinger...
|
||||
grow-window = Gro
|
||||
shrink-window = Skrump
|
||||
swap-windows = Byt Vinduer
|
||||
stack-windows = Stabel Vinduer
|
||||
window-menu-tiled = Gør vindue flydende
|
||||
window-menu-screenshot = Tag skærmbillede
|
||||
window-menu-move = Flyt
|
||||
window-menu-resize = Skift størrelse
|
||||
window-menu-move-prev-workspace = Flyt til forrige arbejdsområde
|
||||
window-menu-move-next-workspace = Flyt til næste arbejdsområde
|
||||
window-menu-stack = Opret vinduestak
|
||||
window-menu-unstack-all = Udtag vinduer af stak
|
||||
window-menu-unstack = Udtag vindue af stak
|
||||
window-menu-sticky = Fastgjort vindue
|
||||
window-menu-close = Luk
|
||||
window-menu-close-all = Luk alle vinduer
|
||||
window-menu-resize-edge-top = Top
|
||||
window-menu-resize-edge-left = Venstre
|
||||
window-menu-resize-edge-right = Højre
|
||||
window-menu-resize-edge-bottom = Bund
|
||||
window-menu-minimize = Minimér
|
||||
window-menu-maximize = Maksimér
|
||||
window-menu-fullscreen = Fuld skærm
|
||||
unknown-keybinding = <uangiven>
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
a11y-zoom-move-continuously = View moves continuously with pointer
|
||||
a11y-zoom-move-onedge = View moves when pointer reaches edge
|
||||
a11y-zoom-move-centered = View moves to keep pointer centred
|
||||
a11y-zoom-settings = Magnifier settings...
|
||||
grow-window = Grow
|
||||
shrink-window = Shrink
|
||||
swap-windows = Swap Windows
|
||||
stack-windows = Stack Windows
|
||||
unknown-keybinding = <unset>
|
||||
window-menu-minimize = Minimise
|
||||
window-menu-maximize = Maximise
|
||||
window-menu-fullscreen = Fullscreen
|
||||
window-menu-tiled = Float window
|
||||
window-menu-screenshot = Take screenshot
|
||||
window-menu-move = Move
|
||||
window-menu-resize = Resize
|
||||
window-menu-move-prev-workspace = Move to previous workspace
|
||||
window-menu-move-next-workspace = Move to next workspace
|
||||
window-menu-stack = Create window stack
|
||||
window-menu-unstack-all = Unstack windows
|
||||
window-menu-unstack = Unstack window
|
||||
window-menu-sticky = Sticky window
|
||||
window-menu-close = Close
|
||||
window-menu-close-all = Close all windows
|
||||
window-menu-resize-edge-top = Top
|
||||
window-menu-resize-edge-left = Left
|
||||
window-menu-resize-edge-right = Right
|
||||
window-menu-resize-edge-bottom = Bottom
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
a11y-zoom-move-continuously = A vista móvese continuamente co punteiro
|
||||
a11y-zoom-move-onedge = A vista móvese cando o punteiro chega ao bordo
|
||||
a11y-zoom-move-centered = Mover a vista para manter o cursor centrado
|
||||
a11y-zoom-settings = Configuración da lupa...
|
||||
grow-window = Aumentar
|
||||
shrink-window = Encoller
|
||||
swap-windows = Cambiar xanelas
|
||||
stack-windows = Apilar xanelas
|
||||
unknown-keybinding = <unset>
|
||||
window-menu-minimize = Minimizar
|
||||
window-menu-maximize = Maximizar
|
||||
window-menu-fullscreen = Pantalla Completa
|
||||
window-menu-tiled = Xanela flotante
|
||||
window-menu-screenshot = Capturar pantalla
|
||||
window-menu-move = Mover
|
||||
window-menu-resize = Redimensionar
|
||||
window-menu-move-prev-workspace = Mover ao espazo de traballo anterior
|
||||
window-menu-move-next-workspace = Mover ao seguinte espazo de traballo
|
||||
window-menu-stack = Crear pila de xanelas
|
||||
window-menu-unstack-all = Desapilar xanelas
|
||||
window-menu-unstack = Desapilar xanela
|
||||
window-menu-sticky = Xanela persistente
|
||||
window-menu-close = Pechar
|
||||
window-menu-close-all = Pechar todas as xanelas
|
||||
window-menu-resize-edge-top = Arriba
|
||||
window-menu-resize-edge-left = Esquerda
|
||||
window-menu-resize-edge-right = Dereita
|
||||
window-menu-resize-edge-bottom = Fondo
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
grow-window = Crescere
|
||||
grow-window = Ingrandisci
|
||||
shrink-window = Riduci
|
||||
swap-windows = Scambia le finestre
|
||||
stack-windows = Sovrapponi le finestre
|
||||
|
|
@ -6,7 +6,7 @@ unknown-keybinding = <unset>
|
|||
window-menu-minimize = Minimizza
|
||||
window-menu-maximize = Massimizza
|
||||
window-menu-tiled = Finestra flottante
|
||||
window-menu-screenshot = Scatta uno screenshot
|
||||
window-menu-screenshot = Cattura una schermata
|
||||
window-menu-move = Sposta
|
||||
window-menu-resize = Ridimensiona
|
||||
window-menu-move-prev-workspace = Sposta all'area di lavoro precedente
|
||||
|
|
@ -22,7 +22,7 @@ window-menu-resize-edge-left = Sinistra
|
|||
window-menu-resize-edge-right = Destra
|
||||
window-menu-resize-edge-bottom = In basso
|
||||
window-menu-fullscreen = Schermo intero
|
||||
a11y-zoom-settings = Impostazioni lente d'ingrandimento...
|
||||
a11y-zoom-settings = Opzioni lente d'ingrandimento...
|
||||
a11y-zoom-move-continuously = Segue sempre il cursorse
|
||||
a11y-zoom-move-centered = Mantiene il cursore centrato
|
||||
a11y-zoom-move-onedge = Segue il cursore ai bordi
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use super::{drm_helpers, surface::Surface};
|
||||
use super::{drm_helpers, socket::Socket, surface::Surface};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EGLInternals {
|
||||
|
|
@ -98,12 +98,14 @@ pub struct Device {
|
|||
|
||||
pub texture_formats: FormatSet,
|
||||
event_token: Option<RegistrationToken>,
|
||||
pub socket: Option<Socket>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReusableDevice {
|
||||
leasing_global: Option<DrmLeaseState>,
|
||||
active_clients: HashSet<ClientId>,
|
||||
socket: Option<Socket>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -591,7 +593,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn device_removed(&mut self, dev: dev_t, _dh: &DisplayHandle) -> Result<()> {
|
||||
pub fn device_removed(&mut self, dev: dev_t, dh: &DisplayHandle) -> Result<()> {
|
||||
let backend = self.backend.kms();
|
||||
// we can't use DrmNode::from_node_id, because that assumes the node is still on sysfs
|
||||
let drm_node = backend
|
||||
|
|
@ -614,6 +616,13 @@ impl State {
|
|||
if let Some(token) = device.event_token.take() {
|
||||
self.common.event_loop_handle.remove(token);
|
||||
}
|
||||
if let Some(socket) = device.socket.take() {
|
||||
self.common.event_loop_handle.remove(socket.token);
|
||||
self.common
|
||||
.dmabuf_state
|
||||
.destroy_global::<State>(dh, socket.dmabuf_global);
|
||||
dh.remove_global::<State>(socket.drm_global);
|
||||
}
|
||||
backend.api.as_mut().remove_node(&device.inner.render_node);
|
||||
backend
|
||||
.primary_node
|
||||
|
|
@ -746,7 +755,23 @@ impl Device {
|
|||
let ReusableDevice {
|
||||
leasing_global,
|
||||
active_clients,
|
||||
socket,
|
||||
} = reuse.unwrap_or_else(|| {
|
||||
let socket = match (!is_software)
|
||||
.then(|| common.create_socket(dh, render_node, texture_formats.clone()))
|
||||
.transpose()
|
||||
{
|
||||
Ok(socket) => socket,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
?err,
|
||||
"Failed to initialize hardware-acceleration for clients on {}.",
|
||||
render_node,
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let leasing_global = match (!is_software)
|
||||
.then(|| DrmLeaseState::new::<State>(dh, &dev_node))
|
||||
.transpose()
|
||||
|
|
@ -765,6 +790,7 @@ impl Device {
|
|||
ReusableDevice {
|
||||
leasing_global,
|
||||
active_clients: HashSet::new(),
|
||||
socket,
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -805,6 +831,7 @@ impl Device {
|
|||
|
||||
texture_formats,
|
||||
event_token: Some(token),
|
||||
socket,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -865,6 +892,7 @@ impl Device {
|
|||
let device = ReusableDevice {
|
||||
leasing_global: self.inner.leasing_global,
|
||||
active_clients: self.inner.active_clients,
|
||||
socket: self.socket,
|
||||
};
|
||||
|
||||
let state = OldDeviceState {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ pub fn display_configuration(
|
|||
// And then cleanup
|
||||
if device.is_atomic() {
|
||||
let mut req = AtomicModeReq::new();
|
||||
let mut has_changes = false;
|
||||
let plane_handles = device.plane_handles()?;
|
||||
|
||||
// We cannot just shortcut and use the legacy api for all cleanups because of this.
|
||||
|
|
@ -96,15 +95,10 @@ pub fn display_configuration(
|
|||
let fb_id = get_prop(device, plane, "FB_ID")?;
|
||||
req.add_property(plane, crtc_id, property::Value::CRTC(None));
|
||||
req.add_property(plane, fb_id, property::Value::Framebuffer(None));
|
||||
has_changes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Skip an empty commit: a no-op modeset that also fails with EPERM
|
||||
// without DRM master (e.g. a render-only secondary GPU).
|
||||
if has_changes {
|
||||
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
|
||||
}
|
||||
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
|
||||
} else {
|
||||
for crtc in res_handles.crtcs() {
|
||||
#[allow(deprecated)]
|
||||
|
|
|
|||
|
|
@ -4,15 +4,12 @@ use crate::{
|
|||
config::{CompOutputConfig, ScreenFilter},
|
||||
shell::Shell,
|
||||
state::BackendData,
|
||||
utils::{env::dev_var, global::remove_global_with_timer, prelude::*},
|
||||
wayland::protocols::{drm::WlDrmState, output_power::OutputPowerState},
|
||||
utils::{env::dev_var, prelude::*},
|
||||
wayland::protocols::output_power::OutputPowerState,
|
||||
};
|
||||
|
||||
use anyhow::{self, Context, Result};
|
||||
use calloop::{
|
||||
LoopSignal,
|
||||
timer::{TimeoutAction, Timer},
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use calloop::LoopSignal;
|
||||
use cosmic_comp_config::output::comp::{AdaptiveSync, OutputState};
|
||||
use indexmap::IndexMap;
|
||||
use render::gles::GbmGlowBackend;
|
||||
|
|
@ -35,12 +32,11 @@ use smithay::{
|
|||
control::{Device as _, connector::Interface, crtc},
|
||||
},
|
||||
input::{self, Libinput},
|
||||
wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags,
|
||||
wayland_server::{Client, DisplayHandle},
|
||||
},
|
||||
utils::{Clock, DevPath, Monotonic, Size},
|
||||
wayland::{
|
||||
dmabuf::{DmabufFeedbackBuilder, DmabufGlobal},
|
||||
dmabuf::DmabufGlobal,
|
||||
drm_syncobj::{DrmSyncobjState, supports_syncobj_eventfd},
|
||||
relative_pointer::RelativePointerManagerState,
|
||||
},
|
||||
|
|
@ -52,12 +48,12 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
path::Path,
|
||||
sync::{Arc, RwLock, atomic::AtomicBool},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
mod device;
|
||||
mod drm_helpers;
|
||||
pub mod render;
|
||||
mod socket;
|
||||
mod surface;
|
||||
use device::*;
|
||||
pub(crate) use surface::Surface;
|
||||
|
|
@ -78,7 +74,6 @@ pub struct KmsState {
|
|||
libinput: Libinput,
|
||||
|
||||
pub syncobj_state: Option<DrmSyncobjState>,
|
||||
pub dmabuf_global: Option<DmabufGlobal>,
|
||||
}
|
||||
|
||||
pub struct KmsGuard<'a> {
|
||||
|
|
@ -136,7 +131,6 @@ pub fn init_backend(
|
|||
libinput: libinput_context,
|
||||
|
||||
syncobj_state: None,
|
||||
dmabuf_global: None,
|
||||
});
|
||||
|
||||
// manually add already present gpus
|
||||
|
|
@ -148,10 +142,9 @@ pub fn init_backend(
|
|||
}
|
||||
}
|
||||
|
||||
if let Err(err) = state.select_primary_gpu(dh) {
|
||||
if let Err(err) = state.backend.kms().select_primary_gpu(dh) {
|
||||
warn!("Failed to determine primary gpu: {}", err);
|
||||
}
|
||||
state.update_default_feedback();
|
||||
|
||||
if let Err(err) = state.refresh_output_config() {
|
||||
info!(
|
||||
|
|
@ -211,7 +204,7 @@ fn init_libinput(
|
|||
state.backend.kms().input_devices.remove(&*device.name());
|
||||
}
|
||||
|
||||
state.process_input_event(event, crate::input::InputBackendId::Normal);
|
||||
state.process_input_event(event);
|
||||
|
||||
for output in state.common.shell.read().outputs() {
|
||||
state.backend.kms().schedule_render(output);
|
||||
|
|
@ -324,13 +317,11 @@ fn init_udev(
|
|||
|
||||
{
|
||||
let backend = state.backend.kms();
|
||||
if matches!(event, UdevEvent::Added { .. } | UdevEvent::Removed { .. }) {
|
||||
if backend.primary_node.read().unwrap().is_none()
|
||||
&& let Err(err) = state.select_primary_gpu(&dh)
|
||||
{
|
||||
warn!("Failed to determine a new primary gpu: {}", err);
|
||||
}
|
||||
state.update_default_feedback();
|
||||
if matches!(event, UdevEvent::Added { .. } | UdevEvent::Removed { .. })
|
||||
&& backend.primary_node.read().unwrap().is_none()
|
||||
&& let Err(err) = state.backend.kms().select_primary_gpu(&dh)
|
||||
{
|
||||
warn!("Failed to determine a new primary gpu: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -474,118 +465,50 @@ impl State {
|
|||
device.drm.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KmsState {
|
||||
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 kms = self.backend.kms();
|
||||
let mut primary_node = kms.primary_node.write().unwrap();
|
||||
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(&kms.drm_devices, kms.session.seat())?;
|
||||
*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);
|
||||
kms.software_renderer.take();
|
||||
|
||||
// setup minimal feedback. We will update it in `update_default_feedback`
|
||||
let primary_formats = kms
|
||||
.drm_devices
|
||||
.values()
|
||||
.find(|dev| dev.inner.render_node == node)
|
||||
.unwrap()
|
||||
.texture_formats
|
||||
.clone();
|
||||
let feedback = DmabufFeedbackBuilder::new(node.dev_id(), primary_formats.clone())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
if let Some(global) = kms.dmabuf_global.as_ref() {
|
||||
self.common
|
||||
.dmabuf_state
|
||||
.set_default_feedback(global, &feedback);
|
||||
} else {
|
||||
let dmabuf_global = self
|
||||
.common
|
||||
.dmabuf_state
|
||||
.create_global_with_default_feedback::<State>(dh, &feedback);
|
||||
kms.dmabuf_global = Some(dmabuf_global);
|
||||
};
|
||||
|
||||
let device_path = node
|
||||
.dev_path_with_type(NodeType::Render)
|
||||
.or_else(|| node.dev_path())
|
||||
.ok_or(anyhow::anyhow!(
|
||||
"Could not determine path for gpu node: {}",
|
||||
node
|
||||
))?;
|
||||
|
||||
if let Some(drm) = self.common.wl_drm_state.as_mut() {
|
||||
drm.update_device(device_path, primary_formats);
|
||||
} else {
|
||||
self.common.wl_drm_state = Some(WlDrmState::new::<State>(
|
||||
dh,
|
||||
device_path,
|
||||
primary_formats,
|
||||
kms.dmabuf_global.as_ref().unwrap(),
|
||||
));
|
||||
}
|
||||
} else if kms.software_renderer.is_none() {
|
||||
self.software_renderer.take();
|
||||
} else if self.software_renderer.is_none() {
|
||||
info!("Failed to find a suitable gpu, using software renderingr");
|
||||
kms.software_renderer = match software_renderer() {
|
||||
self.software_renderer = match software_renderer() {
|
||||
Ok(renderer) => Some(renderer),
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to initialize software EGL renderer.");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(drm) = self.common.wl_drm_state.take() {
|
||||
remove_global_with_timer(dh, &self.common.event_loop_handle, drm.global().clone());
|
||||
}
|
||||
if let Some(global) = kms.dmabuf_global.take() {
|
||||
self.common
|
||||
.dmabuf_state
|
||||
.disable_global::<State>(dh, &global);
|
||||
let source = Timer::from_duration(Duration::from_secs(5));
|
||||
let res =
|
||||
self.common
|
||||
.event_loop_handle
|
||||
.insert_source(source, move |_, _, state| {
|
||||
state
|
||||
.common
|
||||
.dmabuf_state
|
||||
.destroy_global::<State>(&state.common.display_handle, global);
|
||||
TimeoutAction::Drop
|
||||
});
|
||||
if let Err(err) = res {
|
||||
tracing::error!(
|
||||
"failed to insert timer source to destroy output global: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()))
|
||||
&& let Some(device) = kms.drm_devices.get(&primary_node)
|
||||
&& 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) = kms.syncobj_state.as_mut() {
|
||||
if let Some(state) = self.syncobj_state.as_mut() {
|
||||
state.update_device(import_device);
|
||||
} else {
|
||||
let syncobj_state = DrmSyncobjState::new::<State>(dh, import_device);
|
||||
kms.syncobj_state = Some(syncobj_state);
|
||||
self.syncobj_state = Some(syncobj_state);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(old_state) = kms.syncobj_state.take() {
|
||||
if let Some(old_state) = self.syncobj_state.take() {
|
||||
dh.remove_global::<State>(old_state.into_global());
|
||||
}
|
||||
}
|
||||
|
|
@ -593,45 +516,6 @@ impl State {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn update_default_feedback(&mut self) {
|
||||
let kms = self.backend.kms();
|
||||
let primary_node = kms.primary_node.read().unwrap();
|
||||
if let Some(primary_node) = *primary_node {
|
||||
let primary_formats = kms
|
||||
.drm_devices
|
||||
.values()
|
||||
.find(|dev| dev.inner.render_node == primary_node)
|
||||
.unwrap()
|
||||
.texture_formats
|
||||
.clone();
|
||||
|
||||
let mut feedback =
|
||||
DmabufFeedbackBuilder::new(primary_node.dev_id(), primary_formats.clone());
|
||||
for dev in kms
|
||||
.drm_devices
|
||||
.values()
|
||||
.filter(|dev| dev.inner.render_node != primary_node)
|
||||
{
|
||||
feedback = feedback.add_preference_tranche(
|
||||
dev.inner.render_node.dev_id(),
|
||||
TrancheFlags::Sampling,
|
||||
dev.texture_formats.iter().cloned(),
|
||||
6..=6,
|
||||
);
|
||||
}
|
||||
|
||||
let default_feedback = feedback.build().unwrap();
|
||||
self.common.dmabuf_state.set_default_feedback(
|
||||
kms.dmabuf_global
|
||||
.as_ref()
|
||||
.expect("Primary node but no dmabuf global?"),
|
||||
&default_feedback,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KmsState {
|
||||
pub fn switch_vt(&mut self, num: i32) -> Result<(), anyhow::Error> {
|
||||
self.session.change_vt(num).map_err(Into::into)
|
||||
}
|
||||
|
|
@ -639,25 +523,29 @@ impl KmsState {
|
|||
pub fn dmabuf_imported(
|
||||
&mut self,
|
||||
client: Option<Client>,
|
||||
_global: &DmabufGlobal,
|
||||
global: &DmabufGlobal,
|
||||
dmabuf: Dmabuf,
|
||||
) -> Result<DrmNode> {
|
||||
let device_node = dmabuf
|
||||
.node()
|
||||
.unwrap_or_else(|| self.primary_node.read().unwrap().unwrap());
|
||||
let mut device = self
|
||||
.drm_devices
|
||||
.values_mut()
|
||||
.find(|dev| dev.inner.render_node == device_node)
|
||||
.ok_or(anyhow::anyhow!(
|
||||
"Unable to find device for node: {}",
|
||||
device_node
|
||||
))?;
|
||||
.find(|device| {
|
||||
device
|
||||
.socket
|
||||
.as_ref()
|
||||
.map(|s| &s.dmabuf_global == global)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.context("Couldn't find gpu for dmabuf global")?;
|
||||
|
||||
// If device advertised to client doesn't support format/modifier, select
|
||||
// first device that does. This is needed for image-copy from
|
||||
// output/toplevel on a different node.
|
||||
if dmabuf.node().is_none() && !device.texture_formats.contains(&dmabuf.format()) {
|
||||
//
|
||||
// TODO: After
|
||||
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/268,
|
||||
// only try the device specified explicitly by the client, if set.
|
||||
if !device.texture_formats.contains(&dmabuf.format()) {
|
||||
device = self
|
||||
.drm_devices
|
||||
.values_mut()
|
||||
|
|
|
|||
|
|
@ -1006,16 +1006,12 @@ impl SurfaceThreadState {
|
|||
&self.shell.read(),
|
||||
);
|
||||
|
||||
// Acquiring a renderer can fail transiently when the underlying DRM
|
||||
// device is lost (e.g. after a GPU reset).
|
||||
let mut renderer = if render_node != self.target_node {
|
||||
self.api
|
||||
.renderer(&render_node, &self.target_node, compositor.format())
|
||||
.map_err(|err| anyhow::format_err!("Failed to create renderer: {:?}", err))?
|
||||
.unwrap()
|
||||
} else {
|
||||
self.api
|
||||
.single_renderer(&self.target_node)
|
||||
.map_err(|err| anyhow::format_err!("Failed to create renderer: {:?}", err))?
|
||||
self.api.single_renderer(&self.target_node).unwrap()
|
||||
};
|
||||
|
||||
self.timings.start_render(&self.clock);
|
||||
|
|
@ -1271,10 +1267,7 @@ impl SurfaceThreadState {
|
|||
})
|
||||
.context("Failed to draw to offscreen render target")?;
|
||||
|
||||
renderer = self
|
||||
.api
|
||||
.single_renderer(&self.target_node)
|
||||
.map_err(|err| anyhow::format_err!("Failed to create renderer: {:?}", err))?;
|
||||
renderer = self.api.single_renderer(&self.target_node).unwrap();
|
||||
|
||||
elements = postprocess_elements(
|
||||
&mut renderer,
|
||||
|
|
@ -1529,7 +1522,7 @@ fn get_surface_dmabuf_feedback(
|
|||
render_node: DrmNode,
|
||||
target_node: DrmNode,
|
||||
render_formats: FormatSet,
|
||||
target_formats: FormatSet,
|
||||
_target_formats: FormatSet,
|
||||
primary_plane_formats: FormatSet,
|
||||
overlay_plane_formats: Option<FormatSet>,
|
||||
) -> SurfaceDmabufFeedback {
|
||||
|
|
@ -1547,40 +1540,60 @@ fn get_surface_dmabuf_feedback(
|
|||
.cloned()
|
||||
.collect::<FormatSet>()
|
||||
});
|
||||
let builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats);
|
||||
|
||||
let mut builder = DmabufFeedbackBuilder::new(render_node.dev_id(), render_formats.clone());
|
||||
/*
|
||||
// Sadly no implementation would pick this up as a preferred render tranche,
|
||||
// where the combined formats would increase our chances of doing a dmabuf copy.
|
||||
// .. So we should probably not advertise this on the off-chance it actually triggers bugs.
|
||||
//
|
||||
|
||||
if target_node != render_node {
|
||||
let combined_formats = render_formats.intersection(&target_formats).cloned().collect::<FormatSet>();
|
||||
if target_node != render_node.dev_id() && !combined_formats.is_empty() {
|
||||
builder = builder.add_preference_tranche(
|
||||
target_node.dev_id(),
|
||||
zwp_linux_dmabuf_feedback_v1::TrancheFlags::Sampling,
|
||||
target_formats,
|
||||
6..=6,
|
||||
render_node.dev_id(),
|
||||
None,
|
||||
combined_formats,
|
||||
);
|
||||
};
|
||||
let render_feedback = builder.clone().build().unwrap();
|
||||
|
||||
let primary_scanout_feedback = builder
|
||||
.clone()
|
||||
.add_preference_tranche(
|
||||
// We also can't advertise scan out tranches for the actual display device,
|
||||
// as e.g. the nvidia driver might then send us dmabufs, that makes e.g. the iris hangs on import...
|
||||
if target_node != render_node.dev_id() && !combined_formats.is_empty() {
|
||||
builder = builder.add_preference_tranche(
|
||||
target_node.dev_id(),
|
||||
zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout,
|
||||
primary_plane_formats,
|
||||
4..=6,
|
||||
)
|
||||
.build()
|
||||
.unwrap();
|
||||
let overlay_scanout_feedback = overlay_plane_formats.map(|formats| {
|
||||
Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout),
|
||||
combined_formats,
|
||||
);
|
||||
};
|
||||
|
||||
// So no fun combinations, we gotta wait for dmabuf-v6
|
||||
*/
|
||||
|
||||
let render_feedback = builder.clone().build().unwrap();
|
||||
let primary_scanout_feedback = (target_node == render_node).then(|| {
|
||||
builder
|
||||
.clone()
|
||||
.add_preference_tranche(
|
||||
target_node.dev_id(),
|
||||
zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout,
|
||||
formats,
|
||||
4..=6,
|
||||
render_node.dev_id(),
|
||||
Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout),
|
||||
primary_plane_formats,
|
||||
)
|
||||
.build()
|
||||
.unwrap()
|
||||
});
|
||||
let overlay_scanout_feedback = overlay_plane_formats
|
||||
.filter(|_| target_node == render_node)
|
||||
.map(|formats| {
|
||||
builder
|
||||
.add_preference_tranche(
|
||||
render_node.dev_id(),
|
||||
Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout),
|
||||
formats,
|
||||
)
|
||||
.build()
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
SurfaceDmabufFeedback {
|
||||
render_feedback,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::{
|
|||
shell::{Devices, SeatExt},
|
||||
state::{BackendData, Common},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::drm::WlDrmState,
|
||||
};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use cosmic_comp_config::output::comp::{OutputConfig, TransformDef};
|
||||
|
|
@ -274,7 +273,7 @@ fn init_egl_client_side(
|
|||
.create_global_with_default_feedback::<State>(dh, &feedback);
|
||||
|
||||
let render_node = render_node.unwrap().unwrap();
|
||||
state.common.wl_drm_state = Some(WlDrmState::new::<State>(
|
||||
let _drm_global_id = state.common.wl_drm_state.create_global::<State>(
|
||||
dh,
|
||||
render_node
|
||||
.dev_path_with_type(NodeType::Render)
|
||||
|
|
@ -285,7 +284,7 @@ fn init_egl_client_side(
|
|||
))?,
|
||||
dmabuf_formats,
|
||||
&dmabuf_global,
|
||||
));
|
||||
);
|
||||
|
||||
info!("EGL hardware-acceleration enabled.");
|
||||
}
|
||||
|
|
@ -310,9 +309,7 @@ impl State {
|
|||
WinitEvent::Focus(true) => {
|
||||
for seat in self.common.shell.read().seats.iter() {
|
||||
let devices = seat.user_data().get::<Devices>().unwrap();
|
||||
if devices
|
||||
.has_device(&WinitVirtualDevice, &crate::input::InputBackendId::Normal)
|
||||
{
|
||||
if devices.has_device(&WinitVirtualDevice) {
|
||||
seat.set_active_output(&self.backend.winit().output);
|
||||
break;
|
||||
}
|
||||
|
|
@ -342,9 +339,7 @@ impl State {
|
|||
render_ping.ping();
|
||||
}
|
||||
WinitEvent::Redraw => render_ping.ping(),
|
||||
WinitEvent::Input(event) => {
|
||||
self.process_input_event(event, crate::input::InputBackendId::Normal)
|
||||
}
|
||||
WinitEvent::Input(event) => self.process_input_event(event),
|
||||
WinitEvent::CloseRequested => {
|
||||
self.common.should_stop = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::{
|
|||
shell::{Devices, SeatExt},
|
||||
state::{BackendData, Common},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::drm::WlDrmState,
|
||||
};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use cosmic_comp_config::output::comp::OutputConfig;
|
||||
|
|
@ -498,7 +497,7 @@ where
|
|||
.common
|
||||
.dmabuf_state
|
||||
.create_global_with_default_feedback::<State>(dh, &default_feedback);
|
||||
state.common.wl_drm_state = Some(WlDrmState::new::<State>(
|
||||
let _drm_global_id = state.common.wl_drm_state.create_global::<State>(
|
||||
dh,
|
||||
render_node
|
||||
.dev_path_with_type(NodeType::Render)
|
||||
|
|
@ -509,7 +508,7 @@ where
|
|||
))?,
|
||||
renderer.dmabuf_formats(),
|
||||
&dmabuf_global,
|
||||
));
|
||||
);
|
||||
|
||||
info!("EGL hardware-acceleration enabled.");
|
||||
|
||||
|
|
@ -534,14 +533,14 @@ impl State {
|
|||
let device = event.device();
|
||||
for seat in self.common.shell.read().seats.iter() {
|
||||
let devices = seat.user_data().get::<Devices>().unwrap();
|
||||
if devices.has_device(&device, &crate::input::InputBackendId::Normal) {
|
||||
if devices.has_device(&device) {
|
||||
seat.set_active_output(&output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.process_input_event(event, crate::input::InputBackendId::Normal);
|
||||
self.process_input_event(event);
|
||||
// TODO actually figure out the output
|
||||
for output in self.common.shell.read().outputs() {
|
||||
self.backend.x11().schedule_render(output);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
input::InputBackendId,
|
||||
shell::Shell,
|
||||
state::{BackendData, State},
|
||||
utils::prelude::OutputExt,
|
||||
|
|
@ -821,27 +820,6 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
|
|||
}
|
||||
}
|
||||
}
|
||||
let ei_connections = state
|
||||
.common
|
||||
.ei_keyboard_source
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for conn in &ei_connections {
|
||||
state.release_ei_keyboard(conn);
|
||||
state.clear_input_source_state(&InputBackendId::Ei(conn.clone()));
|
||||
}
|
||||
for ei_seat in state.common.ei_seats.values() {
|
||||
if let Err(err) =
|
||||
ei_seat.add_keyboard("virtual keyboard", xkb_config_to_wl(&value))
|
||||
{
|
||||
warn!(?err, "Failed to update libei keyboard keymap");
|
||||
}
|
||||
}
|
||||
if !state.common.ei_seats.is_empty() {
|
||||
let seat = state.common.shell.read().seats.last_active().clone();
|
||||
state.broadcast_ei_keyboard_modifiers(&seat);
|
||||
}
|
||||
state.common.config.cosmic_conf.xkb_config = value;
|
||||
}
|
||||
"keyboard_config" => {
|
||||
|
|
|
|||
|
|
@ -150,10 +150,9 @@ impl A11yKeyboardMonitorState {
|
|||
}
|
||||
|
||||
pub fn key_event(&self, modifiers: &ModifiersState, keysym: &KeysymHandle, state: KeyState) {
|
||||
let has_key_grab = self.has_key_grab(modifiers, keysym.modified_sym());
|
||||
let clients = self.clients.lock().unwrap();
|
||||
for (unique_name, client) in clients.0.iter() {
|
||||
if !client.watched && !has_key_grab {
|
||||
if !client.watched && !self.has_key_grab(modifiers, keysym.modified_sym()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
use std::{
|
||||
os::unix::net::UnixStream,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use smithay::reexports::calloop;
|
||||
use zbus::names::{UniqueName, WellKnownName};
|
||||
|
||||
use super::name_owners::NameOwners;
|
||||
|
||||
static ALLOWED_NAMES: &[WellKnownName] = &[
|
||||
WellKnownName::from_static_str_unchecked("org.freedesktop.impl.portal.desktop.cosmic"),
|
||||
WellKnownName::from_static_str_unchecked("com.system76.CosmicOSK"),
|
||||
];
|
||||
|
||||
/// Channel for handing the EI socketpair (and requested device types)
|
||||
/// It's `None` until the EI sender side has been set up
|
||||
type EiSender = Arc<Mutex<Option<calloop::channel::Sender<crate::libei::EiRequest>>>>;
|
||||
|
||||
struct Ei {
|
||||
ei_sender: EiSender,
|
||||
name_owners: NameOwners,
|
||||
}
|
||||
|
||||
impl Ei {
|
||||
async fn check_sender_allowed(&self, sender: &UniqueName<'_>) -> zbus::fdo::Result<()> {
|
||||
if self.name_owners.check_owner(sender, ALLOWED_NAMES).await {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(zbus::fdo::Error::AccessDenied("Access denied".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[zbus::interface(name = "com.system76.CosmicComp.Ei")]
|
||||
impl Ei {
|
||||
/// Create a new EI sender context
|
||||
async fn get_sender_socket(
|
||||
&self,
|
||||
device_types: u32,
|
||||
#[zbus(header)] header: zbus::message::Header<'_>,
|
||||
) -> zbus::fdo::Result<zbus::zvariant::OwnedFd> {
|
||||
if let Some(sender) = header.sender() {
|
||||
self.check_sender_allowed(sender).await?;
|
||||
}
|
||||
|
||||
let (comp_stream, client_stream) = UnixStream::pair().map_err(|err| {
|
||||
zbus::fdo::Error::Failed(format!("Failed to create socket pair: {err}"))
|
||||
})?;
|
||||
|
||||
{
|
||||
let guard = self.ei_sender.lock().unwrap();
|
||||
let sender = guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| zbus::fdo::Error::Failed("EI sender not available".to_string()))?;
|
||||
sender.send((comp_stream, device_types)).map_err(|err| {
|
||||
zbus::fdo::Error::Failed(format!("Failed to hand off EI socket: {err}"))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(std::os::fd::OwnedFd::from(client_stream).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the `com.system76.CosmicComp.Ei` interface on the shared session connection.
|
||||
pub async fn init(
|
||||
conn: &zbus::Connection,
|
||||
name_owners: &NameOwners,
|
||||
ei_sender: EiSender,
|
||||
) -> zbus::Result<()> {
|
||||
let ei = Ei {
|
||||
ei_sender,
|
||||
name_owners: name_owners.clone(),
|
||||
};
|
||||
conn.object_server()
|
||||
.at("/com/system76/CosmicComp/Ei", ei)
|
||||
.await?;
|
||||
conn.request_name("com.system76.CosmicComp").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -9,13 +9,11 @@ use std::{
|
|||
cell::{RefCell, RefMut},
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tracing::{error, warn};
|
||||
|
||||
pub mod a11y_keyboard_monitor;
|
||||
use a11y_keyboard_monitor::A11yKeyboardMonitorState;
|
||||
pub mod ei;
|
||||
#[cfg(feature = "logind")]
|
||||
pub mod logind;
|
||||
mod name_owners;
|
||||
|
|
@ -31,7 +29,6 @@ struct DBusStateInner {
|
|||
session_conn: zbus::Result<zbus::Connection>,
|
||||
system_conn: zbus::Result<zbus::Connection>,
|
||||
a11y_keyboard_monitor: RefCell<Option<a11y_keyboard_monitor::A11yKeyboardMonitorState>>,
|
||||
ei_sender: Arc<Mutex<Option<calloop::channel::Sender<crate::libei::EiRequest>>>>,
|
||||
}
|
||||
|
||||
impl DBusState {
|
||||
|
|
@ -45,7 +42,6 @@ impl DBusState {
|
|||
session_conn,
|
||||
system_conn,
|
||||
a11y_keyboard_monitor: RefCell::new(None),
|
||||
ei_sender: Arc::new(Mutex::new(None)),
|
||||
}));
|
||||
evlh.insert_source(source, |_, _, _| {}).unwrap();
|
||||
let state_clone = state.clone();
|
||||
|
|
@ -69,10 +65,6 @@ impl DBusState {
|
|||
RefMut::filter_map(self.0.a11y_keyboard_monitor.borrow_mut(), |x| x.as_mut()).ok()
|
||||
}
|
||||
|
||||
pub fn set_ei_sender(&self, sender: calloop::channel::Sender<crate::libei::EiRequest>) {
|
||||
*self.0.ei_sender.lock().unwrap() = Some(sender);
|
||||
}
|
||||
|
||||
// TODO Lazy async init when we don't have anything blocking main thread
|
||||
async fn session_conn(&self) -> zbus::Result<&zbus::Connection> {
|
||||
self.0.session_conn.as_ref().map_err(|err| err.clone())
|
||||
|
|
@ -93,7 +85,6 @@ async fn init_session(state: &DBusState) -> zbus::Result<()> {
|
|||
let a11y_keyboard_monitor_state =
|
||||
A11yKeyboardMonitorState::new(conn, &name_owners, &state.0.executor).await?;
|
||||
*state.0.a11y_keyboard_monitor.borrow_mut() = Some(a11y_keyboard_monitor_state);
|
||||
ei::init(conn, &name_owners, state.0.ei_sender.clone()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use crate::{
|
||||
config::{Action, PrivateAction},
|
||||
input::InputBackendId,
|
||||
shell::{
|
||||
FocusResult, InvalidWorkspaceIndex, MoveResult, SeatExt, Trigger, WorkspaceDelta,
|
||||
focus::{FocusTarget, target::KeyboardFocusTarget},
|
||||
|
|
@ -40,7 +39,6 @@ impl State {
|
|||
pub fn handle_action(
|
||||
&mut self,
|
||||
action: Action,
|
||||
backend_id: &InputBackendId,
|
||||
seat: &Seat<State>,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
|
|
@ -71,7 +69,7 @@ impl State {
|
|||
Action::Shortcut(action) => {
|
||||
let propagate = propagate_by_default(&action);
|
||||
self.handle_shortcut_action(
|
||||
action, backend_id, seat, serial, time, pattern, direction, propagate,
|
||||
action, seat, serial, time, pattern, direction, propagate,
|
||||
)
|
||||
}
|
||||
Action::Private(PrivateAction::Escape) => {
|
||||
|
|
@ -145,7 +143,6 @@ impl State {
|
|||
pub fn handle_shortcut_action(
|
||||
&mut self,
|
||||
action: shortcuts::Action,
|
||||
backend_id: &InputBackendId,
|
||||
seat: &Seat<State>,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
|
|
@ -235,7 +232,6 @@ impl State {
|
|||
{
|
||||
self.handle_shortcut_action(
|
||||
Action::SwitchOutput(inferred),
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -275,7 +271,6 @@ impl State {
|
|||
{
|
||||
self.handle_shortcut_action(
|
||||
Action::SwitchOutput(inferred),
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -397,7 +392,6 @@ impl State {
|
|||
} else {
|
||||
Action::SendToOutput(inferred)
|
||||
},
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -423,7 +417,6 @@ impl State {
|
|||
} else {
|
||||
Action::SendToWorkspace(1)
|
||||
},
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -490,7 +483,6 @@ impl State {
|
|||
} else {
|
||||
Action::SendToOutput(inferred)
|
||||
},
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -516,7 +508,6 @@ impl State {
|
|||
} else {
|
||||
Action::SendToLastWorkspace
|
||||
},
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -541,9 +532,7 @@ impl State {
|
|||
if propagate
|
||||
&& let Some((serial, prev_output, prev_idx)) =
|
||||
shell.previous_workspace_idx.take()
|
||||
&& seat
|
||||
.last_modifier_change_for(backend_id)
|
||||
.is_some_and(|s| s == serial)
|
||||
&& seat.last_modifier_change().is_some_and(|s| s == serial)
|
||||
&& prev_output == current_output
|
||||
{
|
||||
let _ = shell.activate(
|
||||
|
|
@ -716,7 +705,6 @@ impl State {
|
|||
if res.is_ok() {
|
||||
self.handle_shortcut_action(
|
||||
Action::SwitchOutput(direction),
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -753,8 +741,7 @@ impl State {
|
|||
};
|
||||
|
||||
if let Some(direction) = dir {
|
||||
if let Some(last_mod_serial) = seat.last_modifier_change_for(backend_id)
|
||||
{
|
||||
if let Some(last_mod_serial) = seat.last_modifier_change() {
|
||||
let mut shell = self.common.shell.write();
|
||||
if !shell
|
||||
.previous_workspace_idx
|
||||
|
|
@ -789,7 +776,6 @@ impl State {
|
|||
|
||||
self.handle_shortcut_action(
|
||||
action,
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
@ -814,7 +800,7 @@ impl State {
|
|||
.move_current_element(direction, seat);
|
||||
match res {
|
||||
MoveResult::MoveFurther(_move_further) => {
|
||||
if let Some(last_mod_serial) = seat.last_modifier_change_for(backend_id) {
|
||||
if let Some(last_mod_serial) = seat.last_modifier_change() {
|
||||
let mut shell = self.common.shell.write();
|
||||
if !shell
|
||||
.previous_workspace_idx
|
||||
|
|
@ -848,7 +834,6 @@ impl State {
|
|||
|
||||
self.handle_shortcut_action(
|
||||
action,
|
||||
backend_id,
|
||||
seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
|
|||
953
src/input/mod.rs
953
src/input/mod.rs
File diff suppressed because it is too large
Load diff
41
src/lib.rs
41
src/lib.rs
|
|
@ -16,7 +16,7 @@ use smithay::{
|
|||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use state::{BackendData, LastRefresh, State};
|
||||
use state::{LastRefresh, State};
|
||||
use std::{
|
||||
env,
|
||||
ffi::OsString,
|
||||
|
|
@ -43,7 +43,6 @@ pub mod dbus;
|
|||
pub mod debug;
|
||||
pub mod hooks;
|
||||
pub mod input;
|
||||
pub mod libei;
|
||||
mod logger;
|
||||
pub mod session;
|
||||
pub mod shell;
|
||||
|
|
@ -177,10 +176,6 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box<dyn Error>> {
|
|||
with_xwayland,
|
||||
kiosk_command,
|
||||
);
|
||||
// Set up the libei sender side before the backend spawns Xwayland.
|
||||
let ei_sender = libei::setup_ei(&event_loop.handle());
|
||||
state.common.dbus_state.set_ei_sender(ei_sender);
|
||||
|
||||
// init backend
|
||||
backend::init_backend_auto(&display, &mut event_loop, &mut state)?;
|
||||
|
||||
|
|
@ -227,17 +222,19 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box<dyn Error>> {
|
|||
// Kiosk child exited with status
|
||||
Ok(Some(exit_status)) => {
|
||||
info!("Command exited with status {:?}", exit_status);
|
||||
// Stop cleanly so surface threads are joined before exit() (signal -> 1).
|
||||
state.common.kiosk_exit_code = Some(exit_status.code().unwrap_or(1));
|
||||
state.common.should_stop = true;
|
||||
match exit_status.code() {
|
||||
// Exiting with the same status as the kiosk child
|
||||
Some(code) => process::exit(code),
|
||||
// The kiosk child exited with signal, exiting with error
|
||||
None => process::exit(1),
|
||||
}
|
||||
}
|
||||
// Command still running
|
||||
Ok(None) => {}
|
||||
// Kiosk child disappeared, exiting with error
|
||||
Err(err) => {
|
||||
warn!(?err, "Failed to wait for command");
|
||||
state.common.kiosk_exit_code = Some(1);
|
||||
state.common.should_stop = true;
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -248,32 +245,10 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box<dyn Error>> {
|
|||
let _ = child.kill();
|
||||
}
|
||||
|
||||
let kiosk_exit_code = state.common.kiosk_exit_code;
|
||||
|
||||
// Join surface threads before exit() so no thread is mid-eglCreateSync when
|
||||
// Mesa's atexit handlers run and corrupt the heap (issue #2375). Safe here
|
||||
// because the event loop has stopped; an unconditional join in Surface::Drop
|
||||
// would instead deadlock against apply_config_for_outputs.
|
||||
if let BackendData::Kms(kms) = &mut state.backend {
|
||||
// Release master first so the surface drop path skips its blocking commit.
|
||||
for device in kms.drm_devices.values_mut() {
|
||||
device.drm.pause();
|
||||
}
|
||||
for device in kms.drm_devices.values_mut() {
|
||||
for (_, surface) in device.inner.surfaces.drain() {
|
||||
surface.drop_and_join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// drop eventloop & state before logger
|
||||
std::mem::drop(event_loop);
|
||||
std::mem::drop(state);
|
||||
|
||||
if let Some(code) = kiosk_exit_code {
|
||||
process::exit(code);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
228
src/libei.rs
228
src/libei.rs
|
|
@ -1,228 +0,0 @@
|
|||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use reis::eis;
|
||||
use smithay::reexports::reis;
|
||||
|
||||
use smithay::backend::libei::{EiInput, EiInputEvent, EiRegion};
|
||||
use smithay::input::keyboard::Keysym;
|
||||
use smithay::reexports::calloop;
|
||||
use smithay::wayland::input_method::InputMethodSeat;
|
||||
use smithay::wayland::text_input::TextInputSeat;
|
||||
|
||||
use crate::config::xkb_config_to_wl;
|
||||
use crate::input::InputBackendId;
|
||||
use crate::state::{BackendData, State};
|
||||
use crate::utils::{geometry::RectGlobalExt, prelude::OutputExt};
|
||||
|
||||
// Requested device types for an EI connection, mirroring the XDG RemoteDesktop portal `DeviceType` bitmask
|
||||
const DEVICE_TYPE_KEYBOARD: u32 = 1;
|
||||
const DEVICE_TYPE_POINTER: u32 = 2;
|
||||
const DEVICE_TYPE_TOUCHSCREEN: u32 = 4;
|
||||
|
||||
// Name of the EI absolute-pointer device. Shared so the connect path and the
|
||||
// re-advertise-on-output-change path recreate the same device.
|
||||
const ABSOLUTE_POINTER_NAME: &str = "virtual absolute pointer";
|
||||
|
||||
pub type EiRequest = (UnixStream, u32);
|
||||
|
||||
/// Build the regions advertised on the EI absolute devices (absolute pointer and touch) for the
|
||||
/// output layout: one region per output, each at its **global logical** offset with
|
||||
/// its logical size and scale.
|
||||
pub fn absolute_regions(state: &State) -> Vec<EiRegion> {
|
||||
let shell = state.common.shell.read();
|
||||
shell
|
||||
.outputs()
|
||||
.map(|output| {
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
EiRegion {
|
||||
// Keep the signed logical rect
|
||||
// the u32 clamp happens at the `ei_device.region` in smithay.
|
||||
rect: output.geometry().as_logical(),
|
||||
scale: scale as f32,
|
||||
// Tie the region to its output so a client can correlate it with the
|
||||
// matching screencast stream (the portal advertises the same name).
|
||||
mapping_id: Some(output.name()),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Re-advertise the coordinate regions on every active EI seat's absolute devices.
|
||||
pub fn refresh_absolute_pointer_regions(state: &State) {
|
||||
if state.common.ei_seats.is_empty() {
|
||||
return;
|
||||
}
|
||||
let regions = absolute_regions(state);
|
||||
for seat in state.common.ei_seats.values() {
|
||||
seat.update_regions(®ions);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_ei(
|
||||
handle: &calloop::LoopHandle<'static, State>,
|
||||
) -> calloop::channel::Sender<EiRequest> {
|
||||
let (sender, channel) = calloop::channel::channel::<EiRequest>();
|
||||
let handle_clone = handle.clone();
|
||||
handle
|
||||
.insert_source(channel, move |event, _, _| {
|
||||
let calloop::channel::Event::Msg((stream, device_types)) = event else {
|
||||
return;
|
||||
};
|
||||
let context = match eis::Context::new(stream) {
|
||||
Ok(context) => context,
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to create EI context: {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let source = EiInput::new(context);
|
||||
if let Err(err) =
|
||||
handle_clone.insert_source(source, move |event, connection, data| match event {
|
||||
EiInputEvent::Connected => {
|
||||
let conn = connection.eis_connection().clone();
|
||||
let seat = connection.add_seat("default");
|
||||
let wants_keyboard = device_types & DEVICE_TYPE_KEYBOARD != 0;
|
||||
if wants_keyboard {
|
||||
let conf = data.common.config.xkb_config();
|
||||
// The ei_keyboard device is given the compositor keymap; its key
|
||||
// events feed the shared seat like any other keyboard.
|
||||
let _ = seat.add_keyboard("virtual keyboard", xkb_config_to_wl(&conf));
|
||||
// The text device lets clients inject keysyms/utf8 directly, delivered
|
||||
seat.add_text("virtual text");
|
||||
}
|
||||
if device_types & DEVICE_TYPE_POINTER != 0 {
|
||||
seat.add_pointer("virtual pointer");
|
||||
let regions = absolute_regions(data);
|
||||
seat.add_pointer_absolute(ABSOLUTE_POINTER_NAME, ®ions);
|
||||
}
|
||||
if device_types & DEVICE_TYPE_TOUCHSCREEN != 0 {
|
||||
let regions = absolute_regions(data);
|
||||
seat.add_touch("virtual touch", ®ions);
|
||||
}
|
||||
// Kb-capable connections get a shared-seat source so their `ei_keyboard`
|
||||
// key events feed the seat keyboard tracking.
|
||||
if wants_keyboard {
|
||||
data.common.ei_keyboard_source.insert(
|
||||
conn.clone(),
|
||||
smithay::input::keyboard::KeyboardSource::new_auxiliary(),
|
||||
);
|
||||
}
|
||||
// Track the seat for every connection
|
||||
data.common.ei_seats.insert(conn, seat);
|
||||
data.update_ei_input_method();
|
||||
}
|
||||
EiInputEvent::Disconnected => {
|
||||
let conn = connection.eis_connection().clone();
|
||||
let backend_id = InputBackendId::Ei(conn.clone());
|
||||
// Release any keys/modifiers and pointer buttons this remote still holds
|
||||
data.release_ei_keyboard(&conn);
|
||||
data.release_ei_pointer(&conn);
|
||||
data.clear_input_source_state(&backend_id);
|
||||
data.common.ei_seats.remove(&conn);
|
||||
data.common.ei_keyboard_source.remove(&conn);
|
||||
data.common.ei_pointer_buttons.remove(&conn);
|
||||
data.update_ei_input_method();
|
||||
// Notify the remaining libei clients of the now-cleared modifier state
|
||||
let seat = data.common.shell.read().seats.last_active().clone();
|
||||
data.broadcast_ei_keyboard_modifiers(&seat);
|
||||
}
|
||||
EiInputEvent::Event(event) => {
|
||||
use smithay::backend::input::{InputEvent, KeyboardKeyEvent};
|
||||
match event {
|
||||
InputEvent::Keyboard { event } => {
|
||||
data.inject_ei_key(
|
||||
connection.eis_connection(),
|
||||
event.key_code(),
|
||||
event.state(),
|
||||
);
|
||||
}
|
||||
other => {
|
||||
let backend_id =
|
||||
InputBackendId::Ei(connection.eis_connection().clone());
|
||||
data.process_input_event(other, backend_id);
|
||||
if matches!(data.backend, BackendData::Kms(_)) {
|
||||
for output in data.common.shell.read().outputs() {
|
||||
data.backend.kms().schedule_render(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EiInputEvent::TextKeysym { keysym, state } => {
|
||||
data.inject_ei_text_keysym(connection.eis_connection(), keysym, state);
|
||||
}
|
||||
EiInputEvent::TextUtf8 { text } => {
|
||||
data.inject_ei_text(&text);
|
||||
}
|
||||
})
|
||||
{
|
||||
tracing::error!("Failed to insert EI input source: {}", err);
|
||||
}
|
||||
})
|
||||
.expect("Failed to insert EI channel source into the event loop");
|
||||
|
||||
sender
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Act as the input method for text injection while any text-capable EI connection is
|
||||
/// active, so `ei_text` UTF-8 can be committed into the focused app even without a real
|
||||
/// IME, but only when none is bound (a real IME always wins)
|
||||
pub(crate) fn update_ei_input_method(&mut self) {
|
||||
let active = !self.common.ei_keyboard_source.is_empty();
|
||||
let seats = self
|
||||
.common
|
||||
.shell
|
||||
.read()
|
||||
.seats
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for seat in seats {
|
||||
let has_ime = seat.input_method().has_instance();
|
||||
let text_input = seat.text_input();
|
||||
if active {
|
||||
if !has_ime {
|
||||
text_input.set_compositor_input_method(true);
|
||||
}
|
||||
} else {
|
||||
text_input.set_compositor_input_method(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inject UTF-8 text (from an EI `ei_text` device) into the focused client.
|
||||
pub fn inject_ei_text(&mut self, text: &str) {
|
||||
let seat = self.common.shell.read().seats.last_active().clone();
|
||||
// Only commit through text-input when we're the active input method (no real IME)
|
||||
if !seat.input_method().has_instance() {
|
||||
let text_input = seat.text_input();
|
||||
let mut injected = false;
|
||||
text_input.with_active_text_input(|ti, _surface| {
|
||||
ti.commit_string(Some(text.to_owned()));
|
||||
injected = true;
|
||||
});
|
||||
if injected {
|
||||
text_input.done(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the whole chunk to spare keycodes in one temporary keymap per batch (delivered
|
||||
// to just the focused client), so we change the keymap ~once per chunk instead of once
|
||||
// per character. Leaves the seat's own keyboard state untouched.
|
||||
let keysyms: Vec<Keysym> = text
|
||||
.chars()
|
||||
.map(Keysym::from_char)
|
||||
.filter(|keysym| keysym.raw() != 0)
|
||||
.collect();
|
||||
let Some(keyboard) = seat.get_keyboard() else {
|
||||
return;
|
||||
};
|
||||
// At most ~247 keysyms fit one spare keymap (keycodes 9..=255); leave margin.
|
||||
const BATCH: usize = 240;
|
||||
for batch in keysyms.chunks(BATCH) {
|
||||
keyboard.inject_text_keysyms(self, batch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -838,7 +838,10 @@ impl CosmicSurface {
|
|||
self.0
|
||||
.send_dmabuf_feedback(output, primary_scan_out_output, |_, data| {
|
||||
if is_fullscreen {
|
||||
&feedback.primary_scanout_feedback
|
||||
feedback
|
||||
.primary_scanout_feedback
|
||||
.as_ref()
|
||||
.unwrap_or(&feedback.render_feedback)
|
||||
} else if frame_time_filter_fn(data) == Kind::ScanoutCandidate {
|
||||
feedback
|
||||
.overlay_scanout_feedback
|
||||
|
|
|
|||
|
|
@ -212,29 +212,6 @@ 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() {
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ impl KeyboardFocusTarget {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn x11_surface(&self) -> Option<X11Surface> {
|
||||
fn x11_surface(&self) -> Option<X11Surface> {
|
||||
match self {
|
||||
KeyboardFocusTarget::Element(mapped) => mapped.active_window().x11_surface().cloned(),
|
||||
KeyboardFocusTarget::Fullscreen(surface) => surface.x11_surface().cloned(),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use smithay::{
|
|||
|
||||
use crate::{
|
||||
config::key_bindings::cosmic_modifiers_from_smithay,
|
||||
input::InputBackendId,
|
||||
shell::{Trigger, layout::tiling::NodeDesc},
|
||||
state::State,
|
||||
};
|
||||
|
|
@ -74,7 +73,6 @@ impl KeyboardGrab<State> for SwapWindowGrab {
|
|||
|
||||
data.handle_shortcut_action(
|
||||
shortcuts::Action::Focus(direction),
|
||||
&InputBackendId::Normal,
|
||||
&self.seat,
|
||||
serial,
|
||||
time,
|
||||
|
|
|
|||
|
|
@ -5024,20 +5024,6 @@ 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,
|
||||
|
|
|
|||
|
|
@ -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::{InputBackendId, ModifiersShortcutQueue, SupressedButtons, SupressedKeys},
|
||||
input::{ModifiersShortcutQueue, SupressedButtons, SupressedKeys},
|
||||
state::State,
|
||||
};
|
||||
use smithay::{
|
||||
|
|
@ -82,25 +82,12 @@ impl Seats {
|
|||
self.last_active = Some(seat.clone());
|
||||
}
|
||||
|
||||
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())
|
||||
})
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +96,6 @@ impl Devices {
|
|||
&self,
|
||||
device: &D,
|
||||
led_state: LedState,
|
||||
backend_id: &InputBackendId,
|
||||
) -> Vec<DeviceCapability> {
|
||||
let id = device.id();
|
||||
let mut map = self.capabilities.borrow_mut();
|
||||
|
|
@ -127,7 +113,7 @@ impl Devices {
|
|||
.cloned()
|
||||
.filter(|c| map.values().flatten().all(|has| *c != *has))
|
||||
.collect::<Vec<_>>();
|
||||
map.insert((backend_id.clone(), id), caps);
|
||||
map.insert(id, caps);
|
||||
|
||||
if device.has_capability(DeviceCapability::Keyboard)
|
||||
&& let Some(device) = <dyn Any>::downcast_ref::<InputDevice>(device)
|
||||
|
|
@ -140,18 +126,11 @@ impl Devices {
|
|||
new_caps
|
||||
}
|
||||
|
||||
/// 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 has_device<D: Device>(&self, device: &D) -> bool {
|
||||
self.capabilities.borrow().contains_key(&device.id())
|
||||
}
|
||||
|
||||
pub fn remove_device<D: Device>(
|
||||
&self,
|
||||
device: &D,
|
||||
backend_id: &InputBackendId,
|
||||
) -> Vec<DeviceCapability> {
|
||||
pub fn remove_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
|
||||
let id = device.id();
|
||||
|
||||
let mut keyboards = self.keyboards.borrow_mut();
|
||||
|
|
@ -160,7 +139,7 @@ impl Devices {
|
|||
}
|
||||
|
||||
let mut map = self.capabilities.borrow_mut();
|
||||
map.remove(&(backend_id.clone(), id))
|
||||
map.remove(&id)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|c| map.values().flatten().all(|has| *c != *has))
|
||||
|
|
@ -176,8 +155,7 @@ impl Devices {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct Devices {
|
||||
// Keyed by `(backend, device_id)`
|
||||
capabilities: RefCell<HashMap<(InputBackendId, String), Vec<DeviceCapability>>>,
|
||||
capabilities: RefCell<HashMap<String, Vec<DeviceCapability>>>,
|
||||
// Used for updating keyboard leds on kms backend
|
||||
keyboards: RefCell<Vec<InputDevice>>,
|
||||
}
|
||||
|
|
@ -207,7 +185,7 @@ struct FocusedOutput(pub Mutex<Option<Output>>);
|
|||
pub struct PointerConstraintHint(pub Mutex<Option<(WlSurface, Point<f64, Logical>)>>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LastModifierChange(pub Mutex<(HashMap<InputBackendId, Serial>, Option<Serial>)>);
|
||||
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
|
||||
|
||||
pub fn create_seat(
|
||||
dh: &DisplayHandle,
|
||||
|
|
@ -287,9 +265,6 @@ 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>)>);
|
||||
|
||||
|
|
@ -368,48 +343,13 @@ impl SeatExt for Seat<State> {
|
|||
}
|
||||
|
||||
fn last_modifier_change(&self) -> Option<Serial> {
|
||||
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
|
||||
*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>)> {
|
||||
|
|
|
|||
45
src/state.rs
45
src/state.rs
|
|
@ -244,32 +244,9 @@ pub struct Common {
|
|||
pub clock: Clock<Monotonic>,
|
||||
pub startup_done: Arc<AtomicBool>,
|
||||
pub should_stop: bool,
|
||||
pub kiosk_exit_code: Option<i32>,
|
||||
|
||||
pub gesture_state: Option<GestureState>,
|
||||
|
||||
/// Active libei sender seats, keyed by their `eis` connection. Tracked so their virtual
|
||||
/// keyboards can be re-created when the keyboard configuration changes at runtime.
|
||||
pub ei_seats: std::collections::HashMap<
|
||||
smithay::reexports::reis::eis::Connection,
|
||||
smithay::backend::libei::EiInputSeat,
|
||||
>,
|
||||
|
||||
/// The shared-seat [`KeyboardSource`] assigned to each libei connection, so its
|
||||
/// `ei_keyboard` key events feed the seat keyboard with independent per-source hold
|
||||
/// tracking (and can be released together on disconnect). Keyed by connection.
|
||||
pub ei_keyboard_source: std::collections::HashMap<
|
||||
smithay::reexports::reis::eis::Connection,
|
||||
smithay::input::keyboard::KeyboardSource,
|
||||
>,
|
||||
|
||||
/// Pointer buttons currently held by each libei connection, so they can be released when the
|
||||
/// connection drops
|
||||
pub ei_pointer_buttons: std::collections::HashMap<
|
||||
smithay::reexports::reis::eis::Connection,
|
||||
std::collections::HashSet<u32>,
|
||||
>,
|
||||
|
||||
pub kiosk_child: Option<Child>,
|
||||
pub theme: cosmic::Theme,
|
||||
|
||||
|
|
@ -298,7 +275,7 @@ pub struct Common {
|
|||
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
|
||||
pub shm_state: ShmState,
|
||||
pub cursor_shape_manager_state: CursorShapeManagerState,
|
||||
pub wl_drm_state: Option<WlDrmState<Option<DrmNode>>>,
|
||||
pub wl_drm_state: WlDrmState<Option<DrmNode>>,
|
||||
pub viewporter_state: ViewporterState,
|
||||
pub kde_decoration_state: KdeDecorationState,
|
||||
pub xdg_decoration_state: XdgDecorationState,
|
||||
|
|
@ -348,7 +325,7 @@ pub enum LockedBackend<'a> {
|
|||
pub struct SurfaceDmabufFeedback {
|
||||
pub render_feedback: DmabufFeedback,
|
||||
pub overlay_scanout_feedback: Option<DmabufFeedback>,
|
||||
pub primary_scanout_feedback: DmabufFeedback,
|
||||
pub primary_scanout_feedback: Option<DmabufFeedback>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -693,7 +670,7 @@ impl State {
|
|||
let cursor_shape_manager_state = CursorShapeManagerState::new::<State>(dh);
|
||||
let seat_state = SeatState::<Self>::new();
|
||||
let viewporter_state = ViewporterState::new::<Self>(dh);
|
||||
let wl_drm_state = None;
|
||||
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);
|
||||
let session_lock_manager_state =
|
||||
|
|
@ -777,11 +754,7 @@ impl State {
|
|||
clock,
|
||||
startup_done: Arc::new(AtomicBool::new(false)),
|
||||
should_stop: false,
|
||||
kiosk_exit_code: None,
|
||||
gesture_state: None,
|
||||
ei_seats: std::collections::HashMap::new(),
|
||||
ei_keyboard_source: std::collections::HashMap::new(),
|
||||
ei_pointer_buttons: std::collections::HashMap::new(),
|
||||
|
||||
kiosk_child: None,
|
||||
theme: cosmic::theme::system_preference(),
|
||||
|
|
@ -981,11 +954,8 @@ impl Common {
|
|||
);
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states, |fraction_scale| {
|
||||
// The 1.0 clamp is a workaround for Chromium
|
||||
// TODO: remove if Chromium ever gets fixed
|
||||
fraction_scale.set_preferred_scale(
|
||||
output.current_scale().fractional_scale().max(1.0),
|
||||
);
|
||||
fraction_scale
|
||||
.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1091,7 +1061,10 @@ impl Common {
|
|||
surface,
|
||||
render_element_states,
|
||||
&feedback.render_feedback,
|
||||
&feedback.primary_scanout_feedback,
|
||||
feedback
|
||||
.primary_scanout_feedback
|
||||
.as_ref()
|
||||
.unwrap_or(&feedback.render_feedback),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use calloop::{
|
||||
LoopHandle,
|
||||
timer::{TimeoutAction, Timer},
|
||||
};
|
||||
use smithay::reexports::wayland_server::DisplayHandle;
|
||||
use wayland_backend::server::GlobalId;
|
||||
|
||||
pub fn remove_global_with_timer<D: 'static>(
|
||||
dh: &DisplayHandle,
|
||||
event_loop_handle: &LoopHandle<D>,
|
||||
id: GlobalId,
|
||||
) {
|
||||
dh.disable_global::<D>(id.clone());
|
||||
let source = Timer::from_duration(Duration::from_secs(5));
|
||||
let dh = dh.clone();
|
||||
let res = event_loop_handle.insert_source(source, move |_, _, _state| {
|
||||
dh.remove_global::<D>(id.clone());
|
||||
TimeoutAction::Drop
|
||||
});
|
||||
if let Err(err) = res {
|
||||
tracing::error!(
|
||||
"failed to insert timer source to destroy output global: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ pub mod env;
|
|||
mod ids;
|
||||
pub(crate) use self::ids::id_gen;
|
||||
pub mod geometry;
|
||||
pub mod global;
|
||||
pub mod iced;
|
||||
pub mod prelude;
|
||||
pub mod quirks;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ impl DmabufHandler for State {
|
|||
fn new_surface_feedback(
|
||||
&mut self,
|
||||
surface: &WlSurface,
|
||||
_global: &DmabufGlobal,
|
||||
global: &DmabufGlobal,
|
||||
) -> Option<DmabufFeedback> {
|
||||
let BackendData::Kms(kms) = &self.backend else {
|
||||
return None;
|
||||
|
|
@ -57,7 +57,18 @@ impl DmabufHandler for State {
|
|||
f.ended_at.is_none() && f.surface.has_surface(surface, WindowSurfaceType::all())
|
||||
});
|
||||
|
||||
let node = kms.primary_node.read().unwrap().unwrap();
|
||||
let node = kms
|
||||
.drm_devices
|
||||
.values()
|
||||
.find(|device| {
|
||||
device
|
||||
.socket
|
||||
.as_ref()
|
||||
.map(|s| &s.dmabuf_global == global)
|
||||
.unwrap_or(false)
|
||||
})?
|
||||
.inner
|
||||
.render_node;
|
||||
let kms_surface = kms
|
||||
.drm_devices
|
||||
.values()
|
||||
|
|
@ -66,7 +77,9 @@ impl DmabufHandler for State {
|
|||
|
||||
Some(with_states(surface, |data| {
|
||||
if is_fullscreen {
|
||||
feedback.primary_scanout_feedback
|
||||
feedback
|
||||
.primary_scanout_feedback
|
||||
.unwrap_or(feedback.render_feedback)
|
||||
} else if frame_time_filter_fn(data) == Kind::ScanoutCandidate {
|
||||
feedback
|
||||
.overlay_scanout_feedback
|
||||
|
|
|
|||
|
|
@ -49,10 +49,7 @@ impl FractionalScaleHandler for State {
|
|||
|
||||
with_states(&surface, |states| {
|
||||
with_fractional_scale(states, |fractional_scale| {
|
||||
// The 1.0 clamp is a workaround for Chromium
|
||||
// TODO: remove if Chromium ever gets fixed
|
||||
fractional_scale
|
||||
.set_preferred_scale(output.current_scale().fractional_scale().max(1.0));
|
||||
fractional_scale.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,16 +230,6 @@ impl State {
|
|||
state.common.output_configuration_state.update();
|
||||
});
|
||||
|
||||
// Output scale or geometry may have changed. EI absolute-pointer regions
|
||||
// are immutable per device, so any connected EI client (e.g. an RDP server)
|
||||
// keeps mapping with the old scale until it reconnects. Recreate the
|
||||
// device with the updated region so the mapping tracks the change live.
|
||||
// (drop the backend lock first: refresh borrows `self` immutably.)
|
||||
drop(backend);
|
||||
if !test_only {
|
||||
crate::libei::refresh_absolute_pointer_regions(self);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,10 @@
|
|||
use crate::state::State;
|
||||
use smithay::{
|
||||
backend::input::TabletToolDescriptor, input::pointer::CursorImageStatus,
|
||||
input::tablet::TabletSeatHandler, reexports::wayland_server::protocol::wl_surface,
|
||||
wayland::tablet_manager::TabletSeatHandler,
|
||||
};
|
||||
|
||||
impl TabletSeatHandler for State {
|
||||
type ToolFocus = wl_surface::WlSurface;
|
||||
|
||||
fn tablet_tool_image(&mut self, _tool: &TabletToolDescriptor, _image: CursorImageStatus) {
|
||||
// TODO display cursor for each tablet tool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,15 +333,17 @@ impl XdgShellHandler for State {
|
|||
if let Some(ref grab) = grab {
|
||||
if grab.has_ended() {
|
||||
should_ungrab = true;
|
||||
} else if let Some(target) = grab.current_grab()
|
||||
&& let Some(wl_surface) = target.wl_surface()
|
||||
&& (wl_surface.as_ref() == surface.wl_surface()
|
||||
|| smithay::desktop::PopupManager::popups_for_surface(
|
||||
surface.wl_surface(),
|
||||
)
|
||||
.any(|(p, _)| p.wl_surface() == wl_surface.as_ref()))
|
||||
{
|
||||
should_ungrab = true;
|
||||
} else if let Some(target) = grab.current_grab() {
|
||||
if let Some(wl_surface) = target.wl_surface() {
|
||||
if wl_surface.as_ref() == surface.wl_surface()
|
||||
|| smithay::desktop::PopupManager::popups_for_surface(
|
||||
surface.wl_surface(),
|
||||
)
|
||||
.any(|(p, _)| p.wl_surface() == wl_surface.as_ref())
|
||||
{
|
||||
should_ungrab = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if should_ungrab {
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ where
|
|||
let radius_exists = with_states(surface.wl_surface(), |surface_data| {
|
||||
let hook_id = surface_data
|
||||
.data_map
|
||||
.get_or_insert_threadsafe(|| LayerHookId::new(None));
|
||||
.get_or_insert_threadsafe(|| ToplevelHookId::new(None));
|
||||
let guard = hook_id.lock().unwrap();
|
||||
guard.as_ref().map(|(_, t)| t.upgrade().is_ok())
|
||||
});
|
||||
|
|
|
|||
|
|
@ -38,11 +38,7 @@ use smithay::{
|
|||
};
|
||||
use tracing::trace;
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{convert::TryFrom, path::PathBuf, sync::Arc};
|
||||
|
||||
pub enum ImportError {
|
||||
Failed,
|
||||
|
|
@ -56,28 +52,19 @@ pub trait DrmHandler<R: 'static> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WlDrmState<R> {
|
||||
device: Arc<Mutex<DrmGlobalDeviceData>>,
|
||||
global: GlobalId,
|
||||
_marker: std::marker::PhantomData<R>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrmGlobalDeviceData {
|
||||
formats: Vec<Fourcc>,
|
||||
path: PathBuf,
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
pub struct WlDrmState<R>(std::marker::PhantomData<R>);
|
||||
|
||||
/// Data associated with a drm global.
|
||||
pub struct DrmGlobalData {
|
||||
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
||||
formats: Arc<Vec<Fourcc>>,
|
||||
device_path: PathBuf,
|
||||
dmabuf_global: DmabufGlobal,
|
||||
device: Arc<Mutex<DrmGlobalDeviceData>>,
|
||||
}
|
||||
|
||||
pub struct DrmInstanceData {
|
||||
device: Arc<Mutex<DrmGlobalDeviceData>>,
|
||||
formats: Arc<Vec<Fourcc>>,
|
||||
dmabuf_global: DmabufGlobal,
|
||||
}
|
||||
|
||||
|
|
@ -99,24 +86,16 @@ where
|
|||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
let data = DrmInstanceData {
|
||||
device: global_data.device.clone(),
|
||||
formats: global_data.formats.clone(),
|
||||
dmabuf_global: global_data.dmabuf_global,
|
||||
};
|
||||
let drm_instance = data_init.init(resource, data);
|
||||
|
||||
drm_instance.device(
|
||||
global_data
|
||||
.device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.path
|
||||
.to_string_lossy()
|
||||
.into_owned(),
|
||||
);
|
||||
drm_instance.device(global_data.device_path.to_string_lossy().into_owned());
|
||||
if drm_instance.version() >= 2 {
|
||||
drm_instance.capabilities(wl_drm::Capability::Prime as u32);
|
||||
}
|
||||
for format in global_data.device.lock().unwrap().formats.iter() {
|
||||
for format in global_data.formats.iter() {
|
||||
if let Ok(converted) = wl_drm::Format::try_from(*format as u32) {
|
||||
drm_instance.format(converted as u32);
|
||||
}
|
||||
|
|
@ -169,7 +148,7 @@ where
|
|||
} => {
|
||||
let format = match Fourcc::try_from(format) {
|
||||
Ok(format) => {
|
||||
if !data.device.lock().unwrap().formats.contains(&format) {
|
||||
if !data.formats.contains(&format) {
|
||||
drm.post_error(
|
||||
wl_drm::Error::InvalidFormat,
|
||||
String::from("Format not advertised by wl_drm"),
|
||||
|
|
@ -241,12 +220,13 @@ where
|
|||
}
|
||||
|
||||
impl<R: 'static> WlDrmState<R> {
|
||||
pub fn new<D>(
|
||||
pub fn create_global<D>(
|
||||
&mut self,
|
||||
display: &DisplayHandle,
|
||||
device_path: PathBuf,
|
||||
formats: FormatSet,
|
||||
dmabuf_global: &DmabufGlobal,
|
||||
) -> Self
|
||||
) -> GlobalId
|
||||
where
|
||||
D: GlobalDispatch<wl_drm::WlDrm, DrmGlobalData>
|
||||
+ Dispatch<wl_drm::WlDrm, DrmInstanceData>
|
||||
|
|
@ -254,16 +234,19 @@ impl<R: 'static> WlDrmState<R> {
|
|||
+ DmabufHandler
|
||||
+ 'static,
|
||||
{
|
||||
Self::new_with_filter::<D, _>(display, device_path, formats, dmabuf_global, |_| true)
|
||||
self.create_global_with_filter::<D, _>(display, device_path, formats, dmabuf_global, |_| {
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_with_filter<D, F>(
|
||||
pub fn create_global_with_filter<D, F>(
|
||||
&mut self,
|
||||
display: &DisplayHandle,
|
||||
device_path: PathBuf,
|
||||
formats: FormatSet,
|
||||
dmabuf_global: &DmabufGlobal,
|
||||
client_filter: F,
|
||||
) -> Self
|
||||
) -> GlobalId
|
||||
where
|
||||
D: GlobalDispatch<wl_drm::WlDrm, DrmGlobalData>
|
||||
+ Dispatch<wl_drm::WlDrm, DrmInstanceData>
|
||||
|
|
@ -272,45 +255,21 @@ impl<R: 'static> WlDrmState<R> {
|
|||
+ 'static,
|
||||
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
let device = Arc::new(Mutex::new(DrmGlobalDeviceData {
|
||||
formats: formats
|
||||
let formats = Arc::new(
|
||||
formats
|
||||
.into_iter()
|
||||
.filter(|f| f.modifier == Modifier::Invalid)
|
||||
.map(|f| f.code)
|
||||
.collect(),
|
||||
path: device_path,
|
||||
}));
|
||||
|
||||
);
|
||||
let data = DrmGlobalData {
|
||||
filter: Box::new(client_filter),
|
||||
device: device.clone(),
|
||||
formats,
|
||||
device_path,
|
||||
dmabuf_global: *dmabuf_global,
|
||||
};
|
||||
|
||||
let global = display.create_global::<D, wl_drm::WlDrm, _>(2, data);
|
||||
|
||||
WlDrmState {
|
||||
device,
|
||||
global,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_device(&mut self, device_path: PathBuf, formats: FormatSet) {
|
||||
let new_device = DrmGlobalDeviceData {
|
||||
formats: formats
|
||||
.into_iter()
|
||||
.filter(|f| f.modifier == Modifier::Invalid)
|
||||
.map(|f| f.code)
|
||||
.collect(),
|
||||
path: device_path,
|
||||
};
|
||||
|
||||
*self.device.lock().unwrap() = new_device;
|
||||
}
|
||||
|
||||
pub fn global(&self) -> &GlobalId {
|
||||
&self.global
|
||||
display.create_global::<D, wl_drm::WlDrm, _>(2, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use calloop::LoopHandle;
|
||||
use calloop::{
|
||||
LoopHandle,
|
||||
timer::{TimeoutAction, Timer},
|
||||
};
|
||||
use cosmic_comp_config::output::comp::AdaptiveSync;
|
||||
use cosmic_protocols::output_management::v1::server::{
|
||||
zcosmic_output_configuration_head_v1::ZcosmicOutputConfigurationHeadV1,
|
||||
|
|
@ -27,7 +30,7 @@ use smithay::{
|
|||
utils::{Logical, Physical, Point, Size, Transform},
|
||||
wayland::output::WlOutputData,
|
||||
};
|
||||
use std::{convert::TryFrom, sync::Mutex};
|
||||
use std::{convert::TryFrom, sync::Mutex, time::Duration};
|
||||
|
||||
mod handlers;
|
||||
|
||||
|
|
@ -516,6 +519,26 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn remove_global_with_timer<D: 'static>(
|
||||
dh: &DisplayHandle,
|
||||
event_loop_handle: &LoopHandle<D>,
|
||||
id: GlobalId,
|
||||
) {
|
||||
dh.disable_global::<D>(id.clone());
|
||||
let source = Timer::from_duration(Duration::from_secs(5));
|
||||
let dh = dh.clone();
|
||||
let res = event_loop_handle.insert_source(source, move |_, _, _state| {
|
||||
dh.remove_global::<D>(id.clone());
|
||||
TimeoutAction::Drop
|
||||
});
|
||||
if let Err(err) = res {
|
||||
tracing::error!(
|
||||
"failed to insert timer source to destroy output global: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! delegate_output_configuration {
|
||||
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
|
||||
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
|
|
@ -555,4 +578,4 @@ macro_rules! delegate_output_configuration {
|
|||
}
|
||||
pub(crate) use delegate_output_configuration;
|
||||
|
||||
use crate::utils::{global::remove_global_with_timer, prelude::OutputExt};
|
||||
use crate::utils::prelude::OutputExt;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ impl State {
|
|||
&self.common.display_handle,
|
||||
None,
|
||||
std::iter::empty::<(OsString, OsString)>(),
|
||||
["-enable-ei-portal"],
|
||||
std::iter::empty::<OsString>(),
|
||||
true,
|
||||
Stdio::null(),
|
||||
Stdio::null(),
|
||||
|
|
@ -394,7 +394,6 @@ impl Common {
|
|||
sym: Keysym,
|
||||
code: Keycode,
|
||||
state: KeyState,
|
||||
modifiers: ModifiersState,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
|
|
@ -419,6 +418,7 @@ impl Common {
|
|||
.last_active()
|
||||
.get_keyboard()
|
||||
.unwrap();
|
||||
let modifiers = keyboard.modifier_state();
|
||||
let is_modifier = sym.is_modifier_key();
|
||||
|
||||
let xstate = self.xwayland_state.as_mut().unwrap();
|
||||
|
|
@ -858,7 +858,7 @@ impl XwmHandler for State {
|
|||
if let Some(target) = res {
|
||||
let seat = shell.seats.last_active().clone();
|
||||
std::mem::drop(shell);
|
||||
Shell::set_focus_on_x11_map(self, &target, &seat, false);
|
||||
Shell::set_focus(self, Some(&target), &seat, None, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue