feat: track focused output workspaces
This commit is contained in:
1 parent
36d69ab6f5
commit
42ad4e1f68
4 files changed
+114
-11
No files matched your search
@@ -477,6 +477,7 @@ fn select_output(drm: &DrmDevice) -> io::Result<(connector::Info, crtc::Handle,
|
|||||||
|
|
||||||
fn create_output(state: &mut Ferese, connector: &connector::Info, mode: DrmMode) -> Output {
|
fn create_output(state: &mut Ferese, connector: &connector::Info, mode: DrmMode) -> Output {
|
||||||
let name = connector.to_string();
|
let name = connector.to_string();
|
||||||
|
let identity = format!("drm:{name}");
|
||||||
let physical_size = connector.size().unwrap_or((0, 0));
|
let physical_size = connector.size().unwrap_or((0, 0));
|
||||||
let output = Output::new(
|
let output = Output::new(
|
||||||
name,
|
name,
|
||||||
@@ -497,7 +498,15 @@ fn create_output(state: &mut Ferese, connector: &connector::Info, mode: DrmMode)
|
|||||||
Some(Scale::Integer(1)),
|
Some(Scale::Integer(1)),
|
||||||
Some((0, 0).into()),
|
Some((0, 0).into()),
|
||||||
);
|
);
|
||||||
state.space.map_output(&output, (0, 0));
|
let x = state
|
||||||
|
.space
|
||||||
|
.outputs()
|
||||||
|
.filter_map(|output| state.space.output_geometry(output))
|
||||||
|
.map(|geometry| geometry.loc.x + geometry.size.w)
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
|
state.space.map_output(&output, (x, 0));
|
||||||
|
state.register_output(&output, identity);
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+90
-6
@@ -7,7 +7,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use ferese_animation::{ClientSize, SpringConfig, WindowGeometry};
|
use ferese_animation::{ClientSize, SpringConfig, WindowGeometry};
|
||||||
use ferese_core::{WindowPlacement, WorkspaceSet};
|
use ferese_core::{OutputGeometry, OutputId, OutputWorkspaceMap, WindowPlacement, WorkspaceSet};
|
||||||
use ferese_layout::{Axis, Direction, GapConfig, LayoutResult, Rect, SizeConstraints, WindowId};
|
use ferese_layout::{Axis, Direction, GapConfig, LayoutResult, Rect, SizeConstraints, WindowId};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
@@ -69,6 +69,9 @@ pub struct Ferese {
|
|||||||
pub loop_signal: LoopSignal,
|
pub loop_signal: LoopSignal,
|
||||||
pub space: Space<Window>,
|
pub space: Space<Window>,
|
||||||
pub workspaces: WorkspaceSet,
|
pub workspaces: WorkspaceSet,
|
||||||
|
pub output_workspaces: OutputWorkspaceMap,
|
||||||
|
output_ids: HashMap<Output, OutputId>,
|
||||||
|
output_identity_ids: HashMap<String, OutputId>,
|
||||||
pub window_ids: HashMap<Window, WindowId>,
|
pub window_ids: HashMap<Window, WindowId>,
|
||||||
pub window_geometry: HashMap<WindowId, WindowGeometry>,
|
pub window_geometry: HashMap<WindowId, WindowGeometry>,
|
||||||
pub focused_window: Option<WindowId>,
|
pub focused_window: Option<WindowId>,
|
||||||
@@ -82,6 +85,7 @@ pub struct Ferese {
|
|||||||
pending_dmabuf_imports: Vec<(Dmabuf, ImportNotifier)>,
|
pending_dmabuf_imports: Vec<(Dmabuf, ImportNotifier)>,
|
||||||
pub(crate) pending_screencopies: Vec<crate::handlers::screencopy::PendingScreencopy>,
|
pub(crate) pending_screencopies: Vec<crate::handlers::screencopy::PendingScreencopy>,
|
||||||
next_window_id: u64,
|
next_window_id: u64,
|
||||||
|
next_output_id: u64,
|
||||||
last_animation_tick: Instant,
|
last_animation_tick: Instant,
|
||||||
pub popups: PopupManager,
|
pub popups: PopupManager,
|
||||||
pub seat: Seat<Self>,
|
pub seat: Seat<Self>,
|
||||||
@@ -112,6 +116,67 @@ pub struct Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ferese {
|
impl Ferese {
|
||||||
|
pub fn register_output(&mut self, output: &Output, identity: String) {
|
||||||
|
let Some(geometry) = self.space.output_geometry(output) else {
|
||||||
|
tracing::error!(output = %output.name(), "cannot register an unmapped output");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let output_id = *self.output_identity_ids.entry(identity).or_insert_with(|| {
|
||||||
|
let id = OutputId(self.next_output_id);
|
||||||
|
self.next_output_id = self.next_output_id.saturating_add(1);
|
||||||
|
id
|
||||||
|
});
|
||||||
|
let fallback_workspace = (1..)
|
||||||
|
.find_map(|index| {
|
||||||
|
let workspace = self.workspaces.ensure_numeric(index).ok()?;
|
||||||
|
self.output_workspaces
|
||||||
|
.output_for_workspace(workspace)
|
||||||
|
.is_none()
|
||||||
|
.then_some(workspace)
|
||||||
|
})
|
||||||
|
.expect("numeric workspaces are inexhaustible");
|
||||||
|
let geometry = OutputGeometry::new(
|
||||||
|
geometry.loc.x,
|
||||||
|
geometry.loc.y,
|
||||||
|
geometry.size.w,
|
||||||
|
geometry.size.h,
|
||||||
|
);
|
||||||
|
|
||||||
|
match self
|
||||||
|
.output_workspaces
|
||||||
|
.connect(output_id, geometry, fallback_workspace)
|
||||||
|
{
|
||||||
|
Ok(workspace) => {
|
||||||
|
self.output_ids.insert(output.clone(), output_id);
|
||||||
|
if self.output_workspaces.focused_output() == Some(output_id) {
|
||||||
|
self.activate_output_workspace(output_id, workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(%error, output = %output.name(), "failed to register output")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate_output_workspace(&mut self, output: OutputId, workspace: ferese_core::WorkspaceId) {
|
||||||
|
if let Err(error) = self.output_workspaces.focus_output(output) {
|
||||||
|
tracing::error!(%error, "failed to focus output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.workspaces.activate(workspace) {
|
||||||
|
Ok(focus) => self.focused_window = focus,
|
||||||
|
Err(error) => tracing::error!(%error, "failed to activate output workspace"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focused_output(&self) -> Option<&Output> {
|
||||||
|
let focused = self.output_workspaces.focused_output()?;
|
||||||
|
self.output_ids
|
||||||
|
.iter()
|
||||||
|
.find_map(|(output, id)| (*id == focused).then_some(output))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn queue_dmabuf_import(&mut self, dmabuf: Dmabuf, notifier: ImportNotifier) {
|
pub(crate) fn queue_dmabuf_import(&mut self, dmabuf: Dmabuf, notifier: ImportNotifier) {
|
||||||
self.pending_dmabuf_imports.push((dmabuf, notifier));
|
self.pending_dmabuf_imports.push((dmabuf, notifier));
|
||||||
}
|
}
|
||||||
@@ -183,6 +248,9 @@ impl Ferese {
|
|||||||
loop_signal: event_loop.get_signal(),
|
loop_signal: event_loop.get_signal(),
|
||||||
space: Space::default(),
|
space: Space::default(),
|
||||||
workspaces: WorkspaceSet::default(),
|
workspaces: WorkspaceSet::default(),
|
||||||
|
output_workspaces: OutputWorkspaceMap::default(),
|
||||||
|
output_ids: HashMap::new(),
|
||||||
|
output_identity_ids: HashMap::new(),
|
||||||
window_ids: HashMap::new(),
|
window_ids: HashMap::new(),
|
||||||
window_geometry: HashMap::new(),
|
window_geometry: HashMap::new(),
|
||||||
focused_window: None,
|
focused_window: None,
|
||||||
@@ -196,6 +264,7 @@ impl Ferese {
|
|||||||
pending_dmabuf_imports: Vec::new(),
|
pending_dmabuf_imports: Vec::new(),
|
||||||
pending_screencopies: Vec::new(),
|
pending_screencopies: Vec::new(),
|
||||||
next_window_id: 1,
|
next_window_id: 1,
|
||||||
|
next_output_id: 1,
|
||||||
last_animation_tick: start_time,
|
last_animation_tick: start_time,
|
||||||
popups: PopupManager::default(),
|
popups: PopupManager::default(),
|
||||||
seat,
|
seat,
|
||||||
@@ -864,15 +933,26 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_workspace(&mut self, index: u32) {
|
pub fn switch_workspace(&mut self, index: u32) {
|
||||||
let focus = match self.workspaces.switch_to_numeric(index) {
|
let workspace = match self.workspaces.ensure_numeric(index) {
|
||||||
Ok(focus) => focus,
|
Ok(workspace) => workspace,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
tracing::error!(%error, index, "failed to switch workspace");
|
tracing::error!(%error, index, "failed to switch workspace");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let Some(output) = self.output_workspaces.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let owner = match self.output_workspaces.switch_workspace(output, workspace) {
|
||||||
|
Ok(ferese_core::WorkspaceSwitch::Activated(output))
|
||||||
|
| Ok(ferese_core::WorkspaceSwitch::FocusedExisting(output)) => output,
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(%error, index, "failed to assign workspace to output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.activate_output_workspace(owner, workspace);
|
||||||
|
|
||||||
self.focused_window = focus;
|
|
||||||
self.relayout();
|
self.relayout();
|
||||||
self.restore_keyboard_focus();
|
self.restore_keyboard_focus();
|
||||||
}
|
}
|
||||||
@@ -923,7 +1003,9 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn output_bounds(&self) -> Option<Rect> {
|
fn output_bounds(&self) -> Option<Rect> {
|
||||||
let output = self.space.outputs().next()?;
|
let output = self
|
||||||
|
.focused_output()
|
||||||
|
.or_else(|| self.space.outputs().next())?;
|
||||||
let geometry = self.space.output_geometry(output)?;
|
let geometry = self.space.output_geometry(output)?;
|
||||||
let zone = layer_map_for_output(output).non_exclusive_zone();
|
let zone = layer_map_for_output(output).non_exclusive_zone();
|
||||||
|
|
||||||
@@ -936,7 +1018,9 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn full_output_bounds(&self) -> Option<Rect> {
|
fn full_output_bounds(&self) -> Option<Rect> {
|
||||||
let output = self.space.outputs().next()?;
|
let output = self
|
||||||
|
.focused_output()
|
||||||
|
.or_else(|| self.space.outputs().next())?;
|
||||||
let geometry = self.space.output_geometry(output)?;
|
let geometry = self.space.output_geometry(output)?;
|
||||||
|
|
||||||
Some(Rect::new(
|
Some(Rect::new(
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
|||||||
);
|
);
|
||||||
output.set_preferred(mode);
|
output.set_preferred(mode);
|
||||||
state.space.map_output(&output, (0, 0));
|
state.space.map_output(&output, (0, 0));
|
||||||
|
state.register_output(&output, "nested-primary".to_owned());
|
||||||
|
|
||||||
let mut damage_tracker = OutputDamageTracker::from_output(&output);
|
let mut damage_tracker = OutputDamageTracker::from_output(&output);
|
||||||
let clock = Clock::<Monotonic>::new();
|
let clock = Clock::<Monotonic>::new();
|
||||||
|
|||||||
@@ -115,6 +115,17 @@ impl WorkspaceSet {
|
|||||||
.expect("active workspace always exists")
|
.expect("active workspace always exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn activate(&mut self, workspace: WorkspaceId) -> Result<Option<WindowId>, WorkspaceError> {
|
||||||
|
if !self.workspaces.contains_key(&workspace) {
|
||||||
|
return Err(WorkspaceError::UnknownWorkspace(workspace));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.active = workspace;
|
||||||
|
|
||||||
|
debug_assert!(self.validate().is_ok());
|
||||||
|
Ok(self.active().fullscreen.or(self.active().last_focused))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn workspace(&self, id: WorkspaceId) -> Option<&Workspace> {
|
pub fn workspace(&self, id: WorkspaceId) -> Option<&Workspace> {
|
||||||
self.workspaces.get(&id)
|
self.workspaces.get(&id)
|
||||||
}
|
}
|
||||||
@@ -236,10 +247,8 @@ impl WorkspaceSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_to_numeric(&mut self, index: u32) -> Result<Option<WindowId>, WorkspaceError> {
|
pub fn switch_to_numeric(&mut self, index: u32) -> Result<Option<WindowId>, WorkspaceError> {
|
||||||
self.active = self.ensure_numeric(index)?;
|
let workspace = self.ensure_numeric(index)?;
|
||||||
|
self.activate(workspace)
|
||||||
debug_assert!(self.validate().is_ok());
|
|
||||||
Ok(self.active().fullscreen.or(self.active().last_focused))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_window(
|
pub fn insert_window(
|
||||||
|
|||||||
Reference in new issue
Block a user