Merge branch 'size' into 'master'
Fix panel sizing See merge request World/Phosh/squeekboard!594
This commit is contained in:
@ -29,9 +29,14 @@ pub mod c {
|
||||
pub struct WlOutput(*const c_void);
|
||||
|
||||
impl WlOutput {
|
||||
fn null() -> Self {
|
||||
const fn null() -> Self {
|
||||
Self(ptr::null())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub const fn dummy() -> Self {
|
||||
Self::null()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
||||
243
src/panel.rs
243
src/panel.rs
@ -94,7 +94,7 @@ impl PixelSize {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Size {
|
||||
width: u32,
|
||||
height: u32,
|
||||
@ -104,7 +104,7 @@ struct Size {
|
||||
/// the application asks for some size,
|
||||
/// and then receives a size that the compositor thought appropriate.
|
||||
/// Stores raw values passed to Wayland, i.e. scaled dimensions.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum State {
|
||||
Hidden,
|
||||
SizeRequested {
|
||||
@ -119,6 +119,18 @@ enum State {
|
||||
},
|
||||
}
|
||||
|
||||
/// A command to send out to the next layer of processing.
|
||||
/// Here, it's the C side of the panel.
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Update {
|
||||
Hide,
|
||||
Resize { height: u32 },
|
||||
RequestWidget {
|
||||
output: OutputId,
|
||||
height: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Command {
|
||||
Show {
|
||||
@ -153,7 +165,50 @@ impl Manager {
|
||||
eprintln!("Panel received configure {:?}", &size);
|
||||
}
|
||||
|
||||
self.state = match self.state.clone() {
|
||||
self.state = self.state.clone().configure(size);
|
||||
|
||||
if self.debug {
|
||||
eprintln!("Panel now {:?}", &self.state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(mgr: Wrapped<Manager>, cmd: Command) {
|
||||
let copied = mgr.clone();
|
||||
|
||||
let mgr = mgr.clone_ref();
|
||||
let mut mgr = mgr.borrow_mut();
|
||||
|
||||
if mgr.debug {
|
||||
eprintln!("Panel received {:?}", &cmd);
|
||||
}
|
||||
|
||||
let (state, updates) = mgr.state.clone().update(cmd);
|
||||
(*mgr).state = state;
|
||||
|
||||
for update in &updates {
|
||||
unsafe {
|
||||
match update {
|
||||
Update::Hide => c::panel_manager_hide(mgr.panel),
|
||||
Update::Resize { height }
|
||||
=> c::panel_manager_resize(mgr.panel, *height),
|
||||
Update::RequestWidget{output, height}
|
||||
=> c::panel_manager_request_widget(mgr.panel, output.0, *height, copied.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mgr.debug {
|
||||
for update in &updates {
|
||||
eprintln!("Panel updates: {:?}", &update);
|
||||
}
|
||||
eprintln!("Panel is now {:?}", &(*mgr).state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn configure(self, size: Size) -> Self {
|
||||
match self {
|
||||
State::Hidden => {
|
||||
// This may happen if a hide is scheduled immediately after a show.
|
||||
log_print!(
|
||||
@ -174,49 +229,34 @@ impl Manager {
|
||||
wanted_height: height,
|
||||
allocated: size,
|
||||
},
|
||||
};
|
||||
|
||||
if self.debug {
|
||||
eprintln!("Panel now {:?}", &self.state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(mgr: Wrapped<Manager>, cmd: Command) {
|
||||
let copied = mgr.clone();
|
||||
|
||||
let mgr = mgr.clone_ref();
|
||||
let mut mgr = mgr.borrow_mut();
|
||||
|
||||
if mgr.debug {
|
||||
eprintln!("Panel received {:?}", &cmd);
|
||||
}
|
||||
|
||||
(*mgr).state = match (cmd, mgr.state.clone()) {
|
||||
(Command::Hide, State::Hidden) => State::Hidden,
|
||||
(Command::Hide, State::SizeAllocated{..}) => {
|
||||
unsafe { c::panel_manager_hide(mgr.panel); }
|
||||
State::Hidden
|
||||
},
|
||||
(Command::Hide, State::SizeRequested{..}) => {
|
||||
unsafe { c::panel_manager_hide(mgr.panel); }
|
||||
State::Hidden
|
||||
},
|
||||
fn update(self, cmd: Command) -> (Self, Vec<Update>) {
|
||||
match (cmd, self) {
|
||||
(Command::Hide, State::Hidden) => (State::Hidden, Vec::new()),
|
||||
(Command::Hide, State::SizeAllocated{..}) => (
|
||||
State::Hidden, vec![Update::Hide],
|
||||
),
|
||||
(Command::Hide, State::SizeRequested{..}) => (
|
||||
State::Hidden, vec![Update::Hide],
|
||||
),
|
||||
(Command::Show{output, height}, State::Hidden) => {
|
||||
let height = height.as_scaled_ceiling();
|
||||
if mgr.debug {
|
||||
eprintln!("Panel requests widget {:?}", (&output.0, &height));
|
||||
}
|
||||
unsafe { c::panel_manager_request_widget(mgr.panel, output.0, height, copied); }
|
||||
State::SizeRequested{output, height}
|
||||
(
|
||||
State::SizeRequested{output, height},
|
||||
vec![Update::RequestWidget{ output, height }],
|
||||
)
|
||||
},
|
||||
(
|
||||
Command::Show{output, height},
|
||||
State::SizeRequested{output: req_output, height: req_height},
|
||||
) => {
|
||||
let height = height.as_scaled_ceiling();
|
||||
if output == req_output && height == req_height {
|
||||
State::SizeRequested{output: req_output, height: req_height}
|
||||
} else if output == req_output {
|
||||
if output == req_output && height == req_height {(
|
||||
State::SizeRequested{output: req_output, height: req_height},
|
||||
Vec::new(),
|
||||
)} else if output == req_output {(
|
||||
// I'm not sure about that.
|
||||
// This could cause a busy loop,
|
||||
// when two requests are being processed at the same time:
|
||||
@ -225,50 +265,119 @@ impl Manager {
|
||||
// the other from the state wanting height B',
|
||||
// causing the compositor to change size to B.
|
||||
// So better cut this short here, despite artifacts.
|
||||
// Out of simplicty, just ignore the new request.
|
||||
// If that causes problems, the request in flight could be stored
|
||||
// for the purpose of handling it better somehow.
|
||||
State::SizeRequested{output: req_output, height: req_height}
|
||||
} else {
|
||||
if mgr.debug {
|
||||
eprintln!("Panel requests widget {:?}", (&output.0, &height));
|
||||
}
|
||||
|
||||
// Doing nothing means that Squeekboard will occasionally use the stale size (see test),
|
||||
// so instead always listen to the higher layer and request a new size.
|
||||
// If this causes problems, maybe count requests/configures, or track what was allocated in response to what request.
|
||||
State::SizeRequested{output, height},
|
||||
vec![Update::Resize { height }],
|
||||
)} else {(
|
||||
// This looks weird, but should be safe.
|
||||
// The stack seems to handle
|
||||
// configure events on a dead surface.
|
||||
unsafe {
|
||||
c::panel_manager_hide(mgr.panel);
|
||||
c::panel_manager_request_widget(mgr.panel, output.0, height, copied);
|
||||
}
|
||||
State::SizeRequested{output, height}
|
||||
}
|
||||
State::SizeRequested{output, height},
|
||||
vec![
|
||||
Update::Hide,
|
||||
Update::RequestWidget { output, height },
|
||||
],
|
||||
)}
|
||||
},
|
||||
(
|
||||
Command::Show{output, height},
|
||||
State::SizeAllocated{output: alloc_output, allocated, wanted_height},
|
||||
) => {
|
||||
let height = height.as_scaled_ceiling();
|
||||
if output == alloc_output && height == wanted_height {
|
||||
State::SizeAllocated{output: alloc_output, wanted_height, allocated}
|
||||
} else if output == alloc_output && height == allocated.height {
|
||||
State::SizeAllocated{output: alloc_output, wanted_height: height, allocated}
|
||||
} else if output == alloc_output {
|
||||
if output == alloc_output && height == wanted_height {(
|
||||
State::SizeAllocated{output: alloc_output, wanted_height, allocated},
|
||||
Vec::new(),
|
||||
)} else if output == alloc_output && height == allocated.height {(
|
||||
State::SizeAllocated{output: alloc_output, wanted_height: height, allocated},
|
||||
Vec::new(),
|
||||
)} else if output == alloc_output {(
|
||||
// Should *all* other heights cause a resize?
|
||||
// What about those between wanted and allocated?
|
||||
unsafe { c::panel_manager_resize(mgr.panel, height); }
|
||||
State::SizeRequested{output, height}
|
||||
} else {
|
||||
unsafe {
|
||||
c::panel_manager_hide(mgr.panel);
|
||||
c::panel_manager_request_widget(mgr.panel, output.0, height, copied);
|
||||
}
|
||||
State::SizeRequested{output, height}
|
||||
}
|
||||
State::SizeRequested{output, height},
|
||||
vec![Update::Resize{height}],
|
||||
)} else {(
|
||||
State::SizeRequested{output, height},
|
||||
vec![
|
||||
Update::Hide,
|
||||
Update::RequestWidget{output, height},
|
||||
]
|
||||
)}
|
||||
},
|
||||
};
|
||||
|
||||
if mgr.debug {
|
||||
eprintln!("Panel is now {:?}", &(*mgr).state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::outputs::c::WlOutput;
|
||||
|
||||
#[test]
|
||||
fn resize_before_configured() {
|
||||
// allow to make typing fields easier
|
||||
#[allow(non_upper_case_globals)]
|
||||
const output: OutputId = OutputId(WlOutput::dummy());
|
||||
|
||||
let state = State::Hidden;
|
||||
|
||||
// Initial show
|
||||
let (state, cmds) = state.update(Command::Show {
|
||||
output,
|
||||
height: PixelSize { pixels: 100, scale_factor: 1 },
|
||||
});
|
||||
assert_eq!(
|
||||
cmds,
|
||||
vec![Update::RequestWidget { output, height: 100 }],
|
||||
);
|
||||
// layer shell requests a resize
|
||||
|
||||
// but another show comes before first can be confirmed
|
||||
let (state, cmds) = dbg!(state).update(Command::Show {
|
||||
output,
|
||||
height: PixelSize { pixels: 50, scale_factor: 1 },
|
||||
});
|
||||
assert_eq!(
|
||||
cmds,
|
||||
vec![Update::Resize { height: 50 }],
|
||||
"{:?}",
|
||||
state,
|
||||
);
|
||||
// This is too many layers of indirection, but as long as layer shell is tied to gtk widgets, there's not much to be done.
|
||||
// The main issue is that as the outputs change, we acknowledge the wrong (maintained) size:
|
||||
/*
|
||||
[346947.774] wl_output@31.geometry(0, 0, 65, 130, 0, "<Unknown>", "<Unknown>", 3)
|
||||
[346948.117] wl_output@17.geometry(0, 0, 65, 130, 0, "<Unknown>", "<Unknown>", 3)
|
||||
[346948.198] zwlr_layer_surface_v1@41.configure(1709, 720, 210)
|
||||
[346948.268] -> zwlr_layer_surface_v1@41.ack_configure(1709)
|
||||
*/
|
||||
// TODO: check if layer_shell allows not acknowledging a configure event, and which part of squeekboard is responsible for that
|
||||
// (there are no messages in between, so it's not PanelMgr; panel.c almost-unconditionally calls to Rust too; could it be layer-shell.c?).
|
||||
|
||||
// event we want
|
||||
let good_state = state.clone().configure(Size { width: 50, height: 50 });
|
||||
assert_eq!(
|
||||
good_state,
|
||||
State::SizeAllocated {
|
||||
output,
|
||||
wanted_height: 50,
|
||||
allocated: Size { width: 50, height: 50 },
|
||||
},
|
||||
);
|
||||
|
||||
// or stale event we do not want
|
||||
let state = state.configure(Size { width: 50, height: 100 });
|
||||
// followed by the good one
|
||||
let state = state.configure(Size { width: 50, height: 50 });
|
||||
assert_eq!(
|
||||
state,
|
||||
State::SizeAllocated {
|
||||
output,
|
||||
wanted_height: 50,
|
||||
allocated: Size { width: 50, height: 50 },
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user