mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
mapped: Add needs_configure flag
Allows to de-duplicate configures from requests that require one.
This commit is contained in:
+24
-15
@@ -433,8 +433,12 @@ impl XdgShellHandler for State {
|
|||||||
if let Some((mapped, current_output)) = self
|
if let Some((mapped, current_output)) = self
|
||||||
.niri
|
.niri
|
||||||
.layout
|
.layout
|
||||||
.find_window_and_output(toplevel.wl_surface())
|
.find_window_and_output_mut(toplevel.wl_surface())
|
||||||
{
|
{
|
||||||
|
// A configure is required in response to this event regardless if there are pending
|
||||||
|
// changes.
|
||||||
|
mapped.set_needs_configure();
|
||||||
|
|
||||||
let window = mapped.window.clone();
|
let window = mapped.window.clone();
|
||||||
|
|
||||||
if let Some(requested_output) = requested_output {
|
if let Some(requested_output) = requested_output {
|
||||||
@@ -446,10 +450,6 @@ impl XdgShellHandler for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.niri.layout.set_fullscreen(&window, true);
|
self.niri.layout.set_fullscreen(&window, true);
|
||||||
|
|
||||||
// A configure is required in response to this event regardless if there are pending
|
|
||||||
// changes.
|
|
||||||
toplevel.send_configure();
|
|
||||||
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
|
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
|
||||||
match &mut unmapped.state {
|
match &mut unmapped.state {
|
||||||
InitialConfigureState::NotConfigured { wants_fullscreen } => {
|
InitialConfigureState::NotConfigured { wants_fullscreen } => {
|
||||||
@@ -513,17 +513,14 @@ impl XdgShellHandler for State {
|
|||||||
if let Some((mapped, _)) = self
|
if let Some((mapped, _)) = self
|
||||||
.niri
|
.niri
|
||||||
.layout
|
.layout
|
||||||
.find_window_and_output(toplevel.wl_surface())
|
.find_window_and_output_mut(toplevel.wl_surface())
|
||||||
{
|
{
|
||||||
let window = mapped.window.clone();
|
|
||||||
self.niri.layout.set_fullscreen(&window, false);
|
|
||||||
|
|
||||||
// A configure is required in response to this event regardless if there are pending
|
// A configure is required in response to this event regardless if there are pending
|
||||||
// changes.
|
// changes.
|
||||||
//
|
mapped.set_needs_configure();
|
||||||
// FIXME: when unfullscreening to floating, this will send an extra configure with
|
|
||||||
// scrolling layout bounds. We should probably avoid it.
|
let window = mapped.window.clone();
|
||||||
toplevel.send_configure();
|
self.niri.layout.set_fullscreen(&window, false);
|
||||||
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
|
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
|
||||||
match &mut unmapped.state {
|
match &mut unmapped.state {
|
||||||
InitialConfigureState::NotConfigured { wants_fullscreen } => {
|
InitialConfigureState::NotConfigured { wants_fullscreen } => {
|
||||||
@@ -744,7 +741,13 @@ impl XdgDecorationHandler for State {
|
|||||||
// A configure is required in response to this event. However, if an initial configure
|
// A configure is required in response to this event. However, if an initial configure
|
||||||
// wasn't sent, then we will send this as part of the initial configure later.
|
// wasn't sent, then we will send this as part of the initial configure later.
|
||||||
if toplevel.is_initial_configure_sent() {
|
if toplevel.is_initial_configure_sent() {
|
||||||
toplevel.send_configure();
|
// If this is a mapped window, flag it as needs configure to avoid duplicate configures.
|
||||||
|
let surface = toplevel.wl_surface();
|
||||||
|
if let Some((mapped, _)) = self.niri.layout.find_window_and_output_mut(surface) {
|
||||||
|
mapped.set_needs_configure();
|
||||||
|
} else {
|
||||||
|
toplevel.send_configure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,7 +760,13 @@ impl XdgDecorationHandler for State {
|
|||||||
// A configure is required in response to this event. However, if an initial configure
|
// A configure is required in response to this event. However, if an initial configure
|
||||||
// wasn't sent, then we will send this as part of the initial configure later.
|
// wasn't sent, then we will send this as part of the initial configure later.
|
||||||
if toplevel.is_initial_configure_sent() {
|
if toplevel.is_initial_configure_sent() {
|
||||||
toplevel.send_configure();
|
// If this is a mapped window, flag it as needs configure to avoid duplicate configures.
|
||||||
|
let surface = toplevel.wl_surface();
|
||||||
|
if let Some((mapped, _)) = self.niri.layout.find_window_and_output_mut(surface) {
|
||||||
|
mapped.set_needs_configure();
|
||||||
|
} else {
|
||||||
|
toplevel.send_configure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -854,3 +854,30 @@ window-rule {
|
|||||||
@"size: 300 × 100, bounds: 1920 × 1080, states: [Activated]"
|
@"size: 300 × 100, bounds: 1920 × 1080, states: [Activated]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unfullscreen_to_floating_doesnt_send_extra_configure() {
|
||||||
|
let (mut f, id, surface) = set_up();
|
||||||
|
|
||||||
|
// Make it floating.
|
||||||
|
f.niri().layout.toggle_window_floating(None);
|
||||||
|
f.roundtrip(id);
|
||||||
|
|
||||||
|
// Fullscreen.
|
||||||
|
let window = f.client(id).window(&surface);
|
||||||
|
window.set_fullscreen(None);
|
||||||
|
f.double_roundtrip(id);
|
||||||
|
|
||||||
|
let _ = f.client(id).window(&surface).recent_configures();
|
||||||
|
|
||||||
|
// Unfullscreen via the window request which requires a configure response.
|
||||||
|
let window = f.client(id).window(&surface);
|
||||||
|
window.unset_fullscreen();
|
||||||
|
f.double_roundtrip(id);
|
||||||
|
|
||||||
|
// This should configure only once and not twice.
|
||||||
|
assert_snapshot!(
|
||||||
|
f.client(id).window(&surface).format_recent_configures(),
|
||||||
|
@"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
+34
-15
@@ -60,6 +60,11 @@ pub struct Mapped {
|
|||||||
/// immediately, rather than setting this flag.
|
/// immediately, rather than setting this flag.
|
||||||
need_to_recompute_rules: bool,
|
need_to_recompute_rules: bool,
|
||||||
|
|
||||||
|
/// Whether this window needs a configure this loop cycle.
|
||||||
|
///
|
||||||
|
/// Certain Wayland requests require a configure in response, like un/fullscreen.
|
||||||
|
needs_configure: bool,
|
||||||
|
|
||||||
/// Whether this window has the keyboard focus.
|
/// Whether this window has the keyboard focus.
|
||||||
is_focused: bool,
|
is_focused: bool,
|
||||||
|
|
||||||
@@ -172,6 +177,7 @@ impl Mapped {
|
|||||||
pre_commit_hook: hook,
|
pre_commit_hook: hook,
|
||||||
rules,
|
rules,
|
||||||
need_to_recompute_rules: false,
|
need_to_recompute_rules: false,
|
||||||
|
needs_configure: false,
|
||||||
is_focused: false,
|
is_focused: false,
|
||||||
is_active_in_column: true,
|
is_active_in_column: true,
|
||||||
is_floating: false,
|
is_floating: false,
|
||||||
@@ -223,6 +229,10 @@ impl Mapped {
|
|||||||
self.recompute_window_rules(rules, is_at_startup)
|
self.recompute_window_rules(rules, is_at_startup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_needs_configure(&mut self) {
|
||||||
|
self.needs_configure = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> MappedId {
|
pub fn id(&self) -> MappedId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
@@ -733,6 +743,11 @@ impl LayoutElement for Mapped {
|
|||||||
let _span =
|
let _span =
|
||||||
trace_span!("configure_intent", surface = ?self.toplevel().wl_surface().id()).entered();
|
trace_span!("configure_intent", surface = ?self.toplevel().wl_surface().id()).entered();
|
||||||
|
|
||||||
|
if self.needs_configure {
|
||||||
|
trace!("the window needs_configure");
|
||||||
|
return ConfigureIntent::ShouldSend;
|
||||||
|
}
|
||||||
|
|
||||||
with_toplevel_role(self.toplevel(), |attributes| {
|
with_toplevel_role(self.toplevel(), |attributes| {
|
||||||
if let Some(server_pending) = &attributes.server_pending {
|
if let Some(server_pending) = &attributes.server_pending {
|
||||||
let current_server = attributes.current_server_state();
|
let current_server = attributes.current_server_state();
|
||||||
@@ -783,24 +798,26 @@ impl LayoutElement for Mapped {
|
|||||||
let _span =
|
let _span =
|
||||||
trace_span!("send_pending_configure", surface = ?toplevel.wl_surface().id()).entered();
|
trace_span!("send_pending_configure", surface = ?toplevel.wl_surface().id()).entered();
|
||||||
|
|
||||||
// Check for pending changes manually to account for RequestSizeOnce::UseWindowSize.
|
// If the window needs a configure, send it regardless.
|
||||||
let has_pending_changes = with_toplevel_role(self.toplevel(), |role| {
|
let has_pending_changes = self.needs_configure
|
||||||
if role.server_pending.is_none() {
|
|| with_toplevel_role(self.toplevel(), |role| {
|
||||||
return false;
|
// Check for pending changes manually to account for RequestSizeOnce::UseWindowSize.
|
||||||
}
|
if role.server_pending.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let current_server_size = role.current_server_state().size;
|
let current_server_size = role.current_server_state().size;
|
||||||
let server_pending = role.server_pending.as_mut().unwrap();
|
let server_pending = role.server_pending.as_mut().unwrap();
|
||||||
|
|
||||||
// With UseWindowSize, we do not consider size-only changes, because we will
|
// With UseWindowSize, we do not consider size-only changes, because we will
|
||||||
// request the current window size and do not expect it to actually change.
|
// request the current window size and do not expect it to actually change.
|
||||||
if let Some(RequestSizeOnce::UseWindowSize) = self.request_size_once {
|
if let Some(RequestSizeOnce::UseWindowSize) = self.request_size_once {
|
||||||
server_pending.size = current_server_size;
|
server_pending.size = current_server_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
let server_pending = role.server_pending.as_ref().unwrap();
|
let server_pending = role.server_pending.as_ref().unwrap();
|
||||||
server_pending != role.current_server_state()
|
server_pending != role.current_server_state()
|
||||||
});
|
});
|
||||||
|
|
||||||
if has_pending_changes {
|
if has_pending_changes {
|
||||||
// If needed, replace the pending size with the current window size.
|
// If needed, replace the pending size with the current window size.
|
||||||
@@ -814,6 +831,8 @@ impl LayoutElement for Mapped {
|
|||||||
let serial = toplevel.send_configure();
|
let serial = toplevel.send_configure();
|
||||||
trace!(?serial, "sending configure");
|
trace!(?serial, "sending configure");
|
||||||
|
|
||||||
|
self.needs_configure = false;
|
||||||
|
|
||||||
if self.animate_next_configure {
|
if self.animate_next_configure {
|
||||||
self.animate_serials.push(serial);
|
self.animate_serials.push(serial);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user