Skip to content

Commit

Permalink
floating: Request size only once
Browse files Browse the repository at this point in the history
Let floating windows resize themselves and keep that size.
  • Loading branch information
YaLTeR committed Dec 21, 2024
1 parent 7f2e7d5 commit 8459e53
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 22 deletions.
35 changes: 18 additions & 17 deletions src/layout/floating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ impl<W: LayoutElement> FloatingSpace<W> {
tile.update_config(self.scale, self.options.clone());

let win = tile.window_mut();
if win.is_pending_fullscreen() {
let size = if win.is_pending_fullscreen() {
let mut size = Size::from((0, 0));

// Make sure fixed-size through window rules keeps working.
Expand All @@ -380,8 +380,11 @@ impl<W: LayoutElement> FloatingSpace<W> {
size.h = min_size.h;
}

win.request_size(size, true, None);
}
size
} else {
win.size()
};
win.request_size_once(size, true);

if activate || self.tiles.is_empty() {
self.active_window_id = Some(win.id().clone());
Expand Down Expand Up @@ -591,16 +594,15 @@ impl<W: LayoutElement> FloatingSpace<W> {
win_width = ensure_min_max_size(win_width, min_size.w, max_size.w);
win_width = max(1, win_width);

let win_height = win
.requested_size()
.map(|size| size.h)
// If we requested height = 0, then switch to the current height.
.filter(|h| *h != 0)
.unwrap_or_else(|| win.size().h);
let mut win_height = win.size_to_request().h;
// If we requested height = 0, then switch to the current height.
if win_height == 0 {
win_height = win.size().h;
}
let win_height = ensure_min_max_size(win_height, min_size.h, max_size.h);

let win_size = Size::from((win_width, win_height));
win.request_size(win_size, animate, None);
win.request_size_once(win_size, animate);
}

pub fn set_window_height(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) {
Expand All @@ -622,16 +624,15 @@ impl<W: LayoutElement> FloatingSpace<W> {
win_height = ensure_min_max_size(win_height, min_size.h, max_size.h);
win_height = max(1, win_height);

let win_width = win
.requested_size()
.map(|size| size.w)
// If we requested width = 0, then switch to the current width.
.filter(|w| *w != 0)
.unwrap_or_else(|| win.size().w);
let mut win_width = win.size_to_request().w;
// If we requested width = 0, then switch to the current width.
if win_width == 0 {
win_width = win.size().w;
}
let win_width = ensure_min_max_size(win_width, min_size.w, max_size.w);

let win_size = Size::from((win_width, win_height));
win.request_size(win_size, animate, None);
win.request_size_once(win_size, animate);
}

fn focus_directional(
Expand Down
26 changes: 25 additions & 1 deletion src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,24 @@ pub trait LayoutElement {
self.render(renderer, location, scale, alpha, target).popups
}

/// Requests the element to change its size.
///
/// The size request is stored and will be continuously sent to the element on any further
/// state changes.
fn request_size(
&mut self,
size: Size<i32, Logical>,
animate: bool,
transaction: Option<Transaction>,
);

/// Requests the element to change size once, clearing the request afterwards.
fn request_size_once(&mut self, size: Size<i32, Logical>, animate: bool) {
self.request_size(size, animate, None);
}

fn request_fullscreen(&mut self, size: Size<i32, Logical>);

fn min_size(&self) -> Size<i32, Logical>;
fn max_size(&self) -> Size<i32, Logical>;
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool;
Expand Down Expand Up @@ -186,6 +197,17 @@ pub trait LayoutElement {
/// Size previously requested through [`LayoutElement::request_size()`].
fn requested_size(&self) -> Option<Size<i32, Logical>>;

/// Size that we will request of this window.
///
/// This can be different from [`requested_size()`](LayoutElement::requested_size()). For
/// example, for floating windows this will generally return the current window size, rather
/// than the last size that we requested, since we want floating windows to be able to change
/// size freely. But not always: if we just requested a floating window to resize and it hasn't
/// responded to it yet, this will return the newly requested size.
fn size_to_request(&self) -> Size<i32, Logical> {
self.requested_size().unwrap_or_else(|| self.size())
}

fn is_child_of(&self, parent: &Self) -> bool;

fn rules(&self) -> &ResolvedWindowRules;
Expand Down Expand Up @@ -3255,10 +3277,12 @@ impl<W: LayoutElement> Layout<W> {
size.h = min_size.h;
}

win.request_size(size, true, None);
win.request_size_once(size, true);

// If we're unfullscreening to floating, default to the floating layout.
is_floating = tile.unfullscreen_to_floating();
} else {
win.request_size_once(win.size(), true);
}

let mut data = InteractiveMoveData {
Expand Down
112 changes: 108 additions & 4 deletions src/window/mapped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ pub struct Mapped {
/// Snapshot right before an animated commit.
animation_snapshot: Option<LayoutElementRenderSnapshot>,

request_size_once: Option<RequestSizeOnce>,

/// Transaction that the next configure should take part in, if any.
transaction_for_next_configure: Option<Transaction>,

Expand Down Expand Up @@ -140,6 +142,13 @@ impl InteractiveResize {
}
}

#[derive(Debug, Clone, Copy)]
enum RequestSizeOnce {
WaitingForConfigure,
WaitingForCommit(Serial),
UseWindowSize,
}

impl Mapped {
pub fn new(window: Window, rules: ResolvedWindowRules, hook: HookId) -> Self {
let surface = window.wl_surface().expect("no X11 support");
Expand All @@ -158,6 +167,7 @@ impl Mapped {
animate_next_configure: false,
animate_serials: Vec::new(),
animation_snapshot: None,
request_size_once: None,
transaction_for_next_configure: None,
pending_transactions: Vec::new(),
interactive_resize: None,
Expand Down Expand Up @@ -516,6 +526,8 @@ impl LayoutElement for Mapped {
self.animate_next_configure = true;
}

self.request_size_once = None;

// Store the transaction regardless of whether the size changed. This is because with 3+
// windows in a column, the size may change among windows 1 and 2 and then right away among
// windows 2 and 3, and we want all windows 1, 2 and 3 to use the last transaction, rather
Expand All @@ -526,11 +538,76 @@ impl LayoutElement for Mapped {
}
}

fn request_size_once(&mut self, size: Size<i32, Logical>, animate: bool) {
// Assume that when calling this function, the window is going floating, so it can no
// longer participate in any transactions with other windows.
self.transaction_for_next_configure = None;

// If our last requested size already matches the size we want to request-once, clear the
// size request right away. However, we must also check if we're unfullscreening, because
// in that case the window itself will restore its previous size upon receiving a (0, 0)
// configure, whereas what we potentially want is to unfullscreen the window into its
// fullscreen size.
let already_sent = with_toplevel_role(self.toplevel(), |role| {
let (last_sent, last_serial) = if let Some(configure) = role.pending_configures().last()
{
// FIXME: it would be more optimal to find the *oldest* pending configure that
// has the same size and fullscreen state to the last pending configure.
(&configure.state, configure.serial)
} else {
(
role.last_acked.as_ref().unwrap(),
role.configure_serial.unwrap(),
)
};

let same_size = last_sent.size.unwrap_or_default() == size;
let has_fullscreen = last_sent.states.contains(xdg_toplevel::State::Fullscreen);
(same_size && !has_fullscreen).then_some(last_serial)
});

if let Some(serial) = already_sent {
if let Some(current_serial) =
with_toplevel_role(self.toplevel(), |role| role.current_serial)
{
// God this triple negative...
if !current_serial.is_no_older_than(&serial) {
// We have already sent a request for the new size, but the surface has not
// committed in response yet, so we will wait for that commit.
self.request_size_once = Some(RequestSizeOnce::WaitingForCommit(serial));
} else {
// We have already sent a request for the new size, and the surface has
// committed in response, so we will start using the current size right away.
self.request_size_once = Some(RequestSizeOnce::UseWindowSize);
}
} else {
warn!("no current serial; did the surface not ack the initial configure?");
self.request_size_once = Some(RequestSizeOnce::UseWindowSize);
};
return;
}

let changed = self.toplevel().with_pending_state(|state| {
let changed = state.size != Some(size);
state.size = Some(size);
state.states.unset(xdg_toplevel::State::Fullscreen);
changed
});

if changed && animate {
self.animate_next_configure = true;
}

self.request_size_once = Some(RequestSizeOnce::WaitingForConfigure);
}

fn request_fullscreen(&mut self, size: Size<i32, Logical>) {
self.toplevel().with_pending_state(|state| {
state.size = Some(size);
state.states.set(xdg_toplevel::State::Fullscreen);
});

self.request_size_once = None;
}

fn min_size(&self) -> Size<i32, Logical> {
Expand Down Expand Up @@ -669,11 +746,19 @@ impl LayoutElement for Mapped {
}

fn send_pending_configure(&mut self) {
let toplevel = self.toplevel();
let _span =
trace_span!("send_pending_configure", surface = ?self.toplevel().wl_surface().id())
.entered();
trace_span!("send_pending_configure", surface = ?toplevel.wl_surface().id()).entered();

if toplevel.has_pending_changes() {
if let Some(RequestSizeOnce::UseWindowSize) = self.request_size_once {
let size = self.window.geometry().size;
toplevel.with_pending_state(|state| {
state.size = Some(size);
});
}

if let Some(serial) = self.toplevel().send_pending_configure() {
let serial = toplevel.send_configure();
trace!(?serial, "sending configure");

if self.animate_next_configure {
Expand All @@ -689,14 +774,18 @@ impl LayoutElement for Mapped {
Some(InteractiveResize::WaitingForLastCommit { data, serial })
}
x => x,
};

if let Some(RequestSizeOnce::WaitingForConfigure) = self.request_size_once {
self.request_size_once = Some(RequestSizeOnce::WaitingForCommit(serial));
}
} else {
self.interactive_resize = match self.interactive_resize.take() {
// We probably started and stopped resizing in the same loop cycle without anything
// changing.
Some(InteractiveResize::WaitingForLastConfigure { .. }) => None,
x => x,
}
};
}

self.animate_next_configure = false;
Expand All @@ -720,6 +809,16 @@ impl LayoutElement for Mapped {
self.toplevel().with_pending_state(|state| state.size)
}

fn size_to_request(&self) -> Size<i32, Logical> {
let current_size = self.window.geometry().size;

if let Some(RequestSizeOnce::UseWindowSize) = self.request_size_once {
return current_size;
}

self.requested_size().unwrap_or(current_size)
}

fn is_child_of(&self, parent: &Self) -> bool {
self.toplevel().parent().as_ref() == Some(parent.toplevel().wl_surface())
}
Expand Down Expand Up @@ -779,5 +878,10 @@ impl LayoutElement for Mapped {
}
}

if let Some(RequestSizeOnce::WaitingForCommit(serial)) = &self.request_size_once {
if commit_serial.is_no_older_than(serial) {
self.request_size_once = Some(RequestSizeOnce::UseWindowSize);
}
}
}
}

0 comments on commit 8459e53

Please sign in to comment.