shell: handle fullscreen windows on a dedicated layer
I hoped to split this up into multiple commits, but the api
changes to `shell/workspace.rs` were to invasive to feasibly do this.
Here is a rough list of changes:
- Fullscreen windows aren't mapped to other layers anymore
- This they need their own logic for:
- Sending frames
- Dmabuf Feedback
- Primary outputs
- On commit handlers
- cursor tests
- They get their own unmap/remap logic
- They get a new restore state similar to minimized windows
- Refactored the minimized window state to reuse as much as possible
here
- They need to be part of focus stacks, which means adjusting them
to a new type `FocusTarget` as they previously only handled
`CosmicMapped`.
- Various shell handlers (minimize, move, menu) now have dedicated
logic for fullscreen surfaces
- This was partially necessary due to relying on CosmicSurface now,
partially because they should've had their own logic from the
start. E.g. the context menu is now reflecting the fullscreen
state
- Fullscreen windows may be rendered behind other windows now, when they
loose focus.
- This needed changes to input handling / rendering
This commit is contained in:
parent
8ef6c161a0
commit
adedb705e7
23 changed files with 2554 additions and 1796 deletions
|
|
@ -1,5 +1,8 @@
|
|||
use cosmic_settings_config::shortcuts::Action;
|
||||
use smithay::{input::pointer::MotionEvent, utils::SERIAL_COUNTER, wayland::seat::WaylandFocus};
|
||||
use smithay::{
|
||||
input::pointer::MotionEvent, reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
utils::SERIAL_COUNTER, wayland::seat::WaylandFocus,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
|
|
@ -11,6 +14,7 @@ use crate::{
|
|||
},
|
||||
state::State,
|
||||
utils::{prelude::SeatExt, screenshot::screenshot_window},
|
||||
wayland::protocols::workspace::WorkspaceHandle,
|
||||
};
|
||||
|
||||
use super::{Item, ResizeEdge};
|
||||
|
|
@ -24,69 +28,136 @@ fn toggle_stacking(state: &mut State, mapped: &CosmicMapped) {
|
|||
}
|
||||
}
|
||||
|
||||
fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||
let mut shell = state.common.shell.write();
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let (current_handle, output) = {
|
||||
let Some(ws) = shell.space_for(mapped) else {
|
||||
return;
|
||||
};
|
||||
(ws.handle, ws.output.clone())
|
||||
};
|
||||
let maybe_handle = shell
|
||||
fn prev_workspace(
|
||||
shell: &Shell,
|
||||
surface: &WlSurface,
|
||||
) -> Option<(WorkspaceHandle, WorkspaceHandle)> {
|
||||
let (current_handle, output) = shell.workspace_for_surface(surface)?;
|
||||
shell
|
||||
.workspaces
|
||||
.spaces_for_output(&output)
|
||||
.enumerate()
|
||||
.find_map(|(i, space)| (space.handle == current_handle).then_some(i))
|
||||
.and_then(|i| i.checked_sub(1))
|
||||
.and_then(|i| shell.workspaces.get(i, &output).map(|s| s.handle));
|
||||
if let Some(prev_handle) = maybe_handle {
|
||||
let res = shell.move_window(
|
||||
Some(&seat),
|
||||
mapped,
|
||||
¤t_handle,
|
||||
&prev_handle,
|
||||
true,
|
||||
None,
|
||||
&mut state.common.workspace_state.update(),
|
||||
);
|
||||
if let Some((target, _)) = res {
|
||||
std::mem::drop(shell);
|
||||
Shell::set_focus(state, Some(&target), &seat, None, true);
|
||||
}
|
||||
}
|
||||
.and_then(|i| shell.workspaces.get(i, &output))
|
||||
.map(|space| (current_handle, space.handle))
|
||||
}
|
||||
|
||||
fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||
let mut shell = state.common.shell.write();
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let (current_handle, output) = {
|
||||
let Some(ws) = shell.space_for(mapped) else {
|
||||
return;
|
||||
};
|
||||
(ws.handle, ws.output.clone())
|
||||
};
|
||||
let maybe_handle = shell
|
||||
fn next_workspace(
|
||||
shell: &Shell,
|
||||
surface: &WlSurface,
|
||||
) -> Option<(WorkspaceHandle, WorkspaceHandle)> {
|
||||
let (current_handle, output) = shell.workspace_for_surface(surface)?;
|
||||
shell
|
||||
.workspaces
|
||||
.spaces_for_output(&output)
|
||||
.skip_while(|space| space.handle != current_handle)
|
||||
.skip(1)
|
||||
.next()
|
||||
.map(|space| space.handle);
|
||||
if let Some(next_handle) = maybe_handle {
|
||||
let res = shell.move_window(
|
||||
Some(&seat),
|
||||
mapped,
|
||||
¤t_handle,
|
||||
&next_handle,
|
||||
true,
|
||||
None,
|
||||
&mut state.common.workspace_state.update(),
|
||||
);
|
||||
if let Some((target, _point)) = res {
|
||||
std::mem::drop(shell);
|
||||
Shell::set_focus(state, Some(&target), &seat, None, true)
|
||||
}
|
||||
.map(|space| (current_handle, space.handle))
|
||||
}
|
||||
|
||||
fn move_fullscreen_prev_workspace(state: &mut State, surface: &CosmicSurface) {
|
||||
let mut shell = state.common.shell.write();
|
||||
let Some(wl_surface) = surface.wl_surface() else {
|
||||
return;
|
||||
};
|
||||
let Some((from, to)) = prev_workspace(&shell, &*wl_surface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let res = shell.move_window(
|
||||
Some(&seat),
|
||||
surface,
|
||||
&from,
|
||||
&to,
|
||||
true,
|
||||
None,
|
||||
&mut state.common.workspace_state.update(),
|
||||
&state.common.event_loop_handle,
|
||||
);
|
||||
if let Some((target, _)) = res {
|
||||
std::mem::drop(shell);
|
||||
Shell::set_focus(state, Some(&target), &seat, None, true);
|
||||
}
|
||||
}
|
||||
|
||||
fn move_fullscreen_next_workspace(state: &mut State, surface: &CosmicSurface) {
|
||||
let mut shell = state.common.shell.write();
|
||||
let Some(wl_surface) = surface.wl_surface() else {
|
||||
return;
|
||||
};
|
||||
let Some((from, to)) = next_workspace(&shell, &*wl_surface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let res = shell.move_window(
|
||||
Some(&seat),
|
||||
surface,
|
||||
&from,
|
||||
&to,
|
||||
true,
|
||||
None,
|
||||
&mut state.common.workspace_state.update(),
|
||||
&state.common.event_loop_handle,
|
||||
);
|
||||
if let Some((target, _)) = res {
|
||||
std::mem::drop(shell);
|
||||
Shell::set_focus(state, Some(&target), &seat, None, true);
|
||||
}
|
||||
}
|
||||
|
||||
fn move_element_prev_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||
let mut shell = state.common.shell.write();
|
||||
let window = mapped.active_window();
|
||||
let Some(wl_surface) = window.wl_surface() else {
|
||||
return;
|
||||
};
|
||||
let Some((from, to)) = prev_workspace(&shell, &*wl_surface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let res = shell.move_element(
|
||||
Some(&seat),
|
||||
mapped,
|
||||
&from,
|
||||
&to,
|
||||
true,
|
||||
None,
|
||||
&mut state.common.workspace_state.update(),
|
||||
);
|
||||
if let Some((target, _)) = res {
|
||||
std::mem::drop(shell);
|
||||
Shell::set_focus(state, Some(&target), &seat, None, true);
|
||||
}
|
||||
}
|
||||
|
||||
fn move_element_next_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||
let mut shell = state.common.shell.write();
|
||||
let window = mapped.active_window();
|
||||
let Some(wl_surface) = window.wl_surface() else {
|
||||
return;
|
||||
};
|
||||
let Some((from, to)) = next_workspace(&shell, &*wl_surface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let res = shell.move_element(
|
||||
Some(&seat),
|
||||
mapped,
|
||||
&from,
|
||||
&to,
|
||||
true,
|
||||
None,
|
||||
&mut state.common.workspace_state.update(),
|
||||
);
|
||||
if let Some((target, _point)) = res {
|
||||
std::mem::drop(shell);
|
||||
Shell::set_focus(state, Some(&target), &seat, None, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +269,11 @@ pub fn window_items(
|
|||
Item::new(fl!("window-menu-minimize"), move |handle| {
|
||||
let mapped = minimize_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
state.common.shell.write().minimize_request(&mapped);
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.write()
|
||||
.minimize_request(&mapped.active_window());
|
||||
});
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::Minimize)),
|
||||
|
|
@ -209,7 +284,7 @@ pub fn window_items(
|
|||
let _ = handle.insert_idle(move |state| {
|
||||
let mut shell = state.common.shell.write();
|
||||
let seat = shell.seats.last_active().clone();
|
||||
shell.maximize_toggle(&mapped, &seat);
|
||||
shell.maximize_toggle(&mapped, &seat, &state.common.event_loop_handle);
|
||||
});
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::Maximize))
|
||||
|
|
@ -422,7 +497,8 @@ pub fn window_items(
|
|||
Some(
|
||||
Item::new(fl!("window-menu-move-prev-workspace"), move |handle| {
|
||||
let mapped = move_prev_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| move_prev_workspace(state, &mapped));
|
||||
let _ =
|
||||
handle.insert_idle(move |state| move_element_prev_workspace(state, &mapped));
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::MoveToPreviousWorkspace))
|
||||
.disabled(is_sticky),
|
||||
|
|
@ -430,7 +506,8 @@ pub fn window_items(
|
|||
Some(
|
||||
Item::new(fl!("window-menu-move-next-workspace"), move |handle| {
|
||||
let mapped = move_next_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| move_next_workspace(state, &mapped));
|
||||
let _ =
|
||||
handle.insert_idle(move |state| move_element_next_workspace(state, &mapped));
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::MoveToNextWorkspace))
|
||||
.disabled(is_sticky),
|
||||
|
|
@ -466,3 +543,105 @@ pub fn window_items(
|
|||
.into_iter()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn fullscreen_items(window: &CosmicSurface, config: &Config) -> impl Iterator<Item = Item> {
|
||||
let minimize_clone = window.clone();
|
||||
let fullscreen_clone = window.clone();
|
||||
let move_prev_clone = window.clone();
|
||||
let move_next_clone = window.clone();
|
||||
let move_clone = window.clone();
|
||||
let screenshot_clone = window.clone();
|
||||
let close_clone = window.clone();
|
||||
|
||||
vec![
|
||||
Some(
|
||||
Item::new(fl!("window-menu-minimize"), move |handle| {
|
||||
let window = minimize_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
state.common.shell.write().minimize_request(&window);
|
||||
});
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::Minimize)),
|
||||
),
|
||||
Some(
|
||||
Item::new(fl!("window-menu-fullscreen"), move |handle| {
|
||||
let window = fullscreen_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let mut shell = state.common.shell.write();
|
||||
shell.unfullscreen_request(&window, &state.common.event_loop_handle);
|
||||
});
|
||||
})
|
||||
//.shortcut(config.shortcut_for_action(&Action::Fullscreen))
|
||||
.toggled(true),
|
||||
),
|
||||
Some(Item::Separator),
|
||||
// TODO: Where to save?
|
||||
Some(Item::new(fl!("window-menu-screenshot"), move |handle| {
|
||||
let window = screenshot_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| screenshot_window(state, &window));
|
||||
})),
|
||||
Some(Item::Separator),
|
||||
Some(Item::new(fl!("window-menu-move"), move |handle| {
|
||||
let move_clone = move_clone.clone();
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
if let Some(surface) = move_clone.wl_surface() {
|
||||
let mut shell = state.common.shell.write();
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let res = shell.move_request(
|
||||
&surface,
|
||||
&seat,
|
||||
None,
|
||||
ReleaseMode::Click,
|
||||
false,
|
||||
&state.common.config,
|
||||
&state.common.event_loop_handle,
|
||||
false,
|
||||
);
|
||||
|
||||
std::mem::drop(shell);
|
||||
if let Some((grab, focus)) = res {
|
||||
if grab.is_touch_grab() {
|
||||
seat.get_touch().unwrap().set_grab(
|
||||
state,
|
||||
grab,
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
)
|
||||
} else {
|
||||
seat.get_pointer().unwrap().set_grab(
|
||||
state,
|
||||
grab,
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
focus,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})),
|
||||
Some(
|
||||
Item::new(fl!("window-menu-move-prev-workspace"), move |handle| {
|
||||
let window = move_prev_clone.clone();
|
||||
let _ =
|
||||
handle.insert_idle(move |state| move_fullscreen_prev_workspace(state, &window));
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::MoveToPreviousWorkspace)),
|
||||
),
|
||||
Some(
|
||||
Item::new(fl!("window-menu-move-next-workspace"), move |handle| {
|
||||
let window = move_next_clone.clone();
|
||||
let _ =
|
||||
handle.insert_idle(move |state| move_fullscreen_next_workspace(state, &window));
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::MoveToNextWorkspace)),
|
||||
),
|
||||
Some(Item::Separator),
|
||||
Some(
|
||||
Item::new(fl!("window-menu-close"), move |_handle| {
|
||||
close_clone.close();
|
||||
})
|
||||
.shortcut(config.shortcut_for_action(&Action::Close)),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -432,7 +432,8 @@ impl MoveGrab {
|
|||
}
|
||||
}
|
||||
|
||||
let indicator_location = shell.stacking_indicator(¤t_output, self.previous);
|
||||
let indicator_location =
|
||||
shell.stacking_indicator(¤t_output, self.previous.clone());
|
||||
if indicator_location.is_some() != grab_state.stacking_indicator.is_some() {
|
||||
grab_state.stacking_indicator = indicator_location.map(|geo| {
|
||||
let element = stack_hover(
|
||||
|
|
@ -731,7 +732,7 @@ impl MoveGrab {
|
|||
start: Instant::now(),
|
||||
stacking_indicator: None,
|
||||
snapping_zone: None,
|
||||
previous: previous_layer,
|
||||
previous: previous_layer.clone(),
|
||||
location: start_data.location(),
|
||||
cursor_output: cursor_output.clone(),
|
||||
};
|
||||
|
|
@ -779,7 +780,7 @@ impl Drop for MoveGrab {
|
|||
let output = self.cursor_output.clone();
|
||||
let seat = self.seat.clone();
|
||||
let window_outputs = self.window_outputs.drain().collect::<HashSet<_>>();
|
||||
let previous = self.previous;
|
||||
let previous = self.previous.clone();
|
||||
let window = self.window.clone();
|
||||
let is_touch_grab = matches!(self.start_data, GrabStartData::Touch(_));
|
||||
|
||||
|
|
@ -841,10 +842,14 @@ impl Drop for MoveGrab {
|
|||
window_location.to_local(&workspace.output),
|
||||
);
|
||||
|
||||
if previous == ManagedLayer::Floating {
|
||||
if matches!(previous, ManagedLayer::Floating) {
|
||||
if let Some(sz) = grab_state.snapping_zone {
|
||||
if sz == SnappingZone::Maximize {
|
||||
shell.maximize_toggle(&window, &seat);
|
||||
shell.maximize_toggle(
|
||||
&window,
|
||||
&seat,
|
||||
&state.common.event_loop_handle,
|
||||
);
|
||||
} else {
|
||||
let directions = match sz {
|
||||
SnappingZone::Maximize => vec![],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue