Skip to content

Commit 999a8f4

Browse files
committed
fix set-position constraints
1 parent c6c02a9 commit 999a8f4

12 files changed

Lines changed: 149 additions & 135 deletions

File tree

jay-config/src/_private/client.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ use {
3434
connector_type::{CON_UNKNOWN, ConnectorType},
3535
},
3636
window::{
37-
ContentType, MatchedWindow, TileState, Window, WindowCriterion, WindowMatcher,
38-
WindowType,
37+
ContentType, Coordinate, MatchedWindow, TileState, Window, WindowCriterion,
38+
WindowMatcher, WindowType,
3939
},
4040
workspace::WorkspaceDisplayOrder,
4141
xwayland::XScalingMode,
@@ -611,12 +611,12 @@ impl ConfigClient {
611611
pub fn set_window_position(
612612
&self,
613613
window: Window,
614-
x1: Option<i32>,
615-
y1: Option<i32>,
616-
x2: Option<i32>,
617-
y2: Option<i32>,
618-
width: Option<i32>,
619-
height: Option<i32>,
614+
x1: Option<Coordinate>,
615+
y1: Option<Coordinate>,
616+
x2: Option<Coordinate>,
617+
y2: Option<Coordinate>,
618+
width: Option<Coordinate>,
619+
height: Option<Coordinate>,
620620
) {
621621
self.send(&ClientMessage::SetWindowPosition {
622622
window,

jay-config/src/_private/ipc.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use {
1919
BlendSpace, ColorSpace, Connector, DrmDevice, Eotf, Format, GfxApi, ScalingFilter,
2020
TearingMode, Transform, VrrMode, connector_type::ConnectorType,
2121
},
22-
window::{ContentType, TileState, Window, WindowMatcher, WindowType},
22+
window::{ContentType, Coordinate, TileState, Window, WindowMatcher, WindowType},
2323
workspace::WorkspaceDisplayOrder,
2424
xwayland::XScalingMode,
2525
},
@@ -986,12 +986,12 @@ pub enum ClientMessage<'a> {
986986
},
987987
SetWindowPosition {
988988
window: Window,
989-
x1: Option<i32>,
990-
y1: Option<i32>,
991-
x2: Option<i32>,
992-
y2: Option<i32>,
993-
width: Option<i32>,
994-
height: Option<i32>,
989+
x1: Option<Coordinate>,
990+
y1: Option<Coordinate>,
991+
x2: Option<Coordinate>,
992+
y2: Option<Coordinate>,
993+
width: Option<Coordinate>,
994+
height: Option<Coordinate>,
995995
},
996996
GetWorkspacePosition {
997997
workspace: Workspace,

jay-config/src/input.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use {
1616
},
1717
keyboard::{Keymap, mods::Modifiers, syms::KeySym},
1818
video::Connector,
19-
window::Window,
19+
window::{Coordinate, Window},
2020
},
2121
serde::{Deserialize, Serialize},
2222
std::time::Duration,
@@ -718,12 +718,12 @@ impl Seat {
718718
/// See [`Window::set_position`](crate::window::Window::set_position) for details.
719719
pub fn set_position(
720720
self,
721-
x1: Option<i32>,
722-
y1: Option<i32>,
723-
x2: Option<i32>,
724-
y2: Option<i32>,
725-
width: Option<i32>,
726-
height: Option<i32>,
721+
x1: Option<Coordinate>,
722+
y1: Option<Coordinate>,
723+
x2: Option<Coordinate>,
724+
y2: Option<Coordinate>,
725+
width: Option<Coordinate>,
726+
height: Option<Coordinate>,
727727
) {
728728
self.window().set_position(x1, y1, x2, y2, width, height);
729729
}

jay-config/src/window.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
use {
44
crate::{
5-
Axis, Direction, Workspace,
65
client::{Client, ClientCriterion},
76
input::Seat,
7+
Axis, Direction, Workspace,
88
},
99
serde::{Deserialize, Serialize},
1010
std::ops::Deref,
@@ -66,6 +66,15 @@ pub enum TileState {
6666
Floating,
6767
}
6868

69+
/// A constraint on a coordinate or size passed to [`Window::set_position`].
70+
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
71+
pub enum Coordinate {
72+
/// Constrain this field to the window's current value for it.
73+
Keep,
74+
/// Constrain this field to this value.
75+
Set(i32),
76+
}
77+
6978
/// A window created by a client.
7079
///
7180
/// This is the same as `XDG_TOPLEVEL | X_WINDOW`.
@@ -256,25 +265,17 @@ impl Window {
256265
///
257266
/// This only has an effect if the window is floating.
258267
///
259-
/// Every argument that is `None` is left unconstrained by this call. Every argument that
260-
/// is `Some(_)` is constrained to that value.
261-
///
262-
/// Since `x2 = x1 + width` (and analogously for the y axis), not every combination of
263-
/// constraints is satisfiable. If `x1`, `x2`, and `width` are all constrained but not
264-
/// self-consistent (and analogously for `y1`, `y2`, `height`), this call has no effect.
265-
///
266-
/// If fewer than two of `x1`, `x2`, `width` are constrained (and analogously for `y1`,
267-
/// `y2`, `height`), the missing values default to preserving the window's current size,
268-
/// e.g. setting only `width` keeps `x1` fixed and moves `x2`, while setting only `x1` keeps
269-
/// the width fixed and moves `x2` along with it.
268+
/// For each axis, missing constraints (fewer than two given) are inferred,
269+
/// preserving the window's current position rather than its size; if all
270+
/// three are given but unsatisfiable for an axis, this is a no-op.
270271
pub fn set_position(
271272
self,
272-
x1: Option<i32>,
273-
y1: Option<i32>,
274-
x2: Option<i32>,
275-
y2: Option<i32>,
276-
width: Option<i32>,
277-
height: Option<i32>,
273+
x1: Option<Coordinate>,
274+
y1: Option<Coordinate>,
275+
x2: Option<Coordinate>,
276+
y2: Option<Coordinate>,
277+
width: Option<Coordinate>,
278+
height: Option<Coordinate>,
278279
) {
279280
get!().set_window_position(self, x1, y1, x2, y2, width, height);
280281
}

src/config/handler.rs

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ use {
8383
Format as ConfigFormat, GfxApi, ScalingFilter as ConfigScalingFilter,
8484
TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode,
8585
},
86-
window::{TileState as ConfigTileState, Window, WindowMatcher},
86+
window::{Coordinate, TileState as ConfigTileState, Window, WindowMatcher},
8787
workspace::WorkspaceDisplayOrder,
8888
xwayland::XScalingMode,
8989
},
@@ -3049,12 +3049,12 @@ impl ConfigProxyHandler {
30493049
fn handle_set_window_position(
30503050
&self,
30513051
window: Window,
3052-
x1: Option<i32>,
3053-
y1: Option<i32>,
3054-
x2: Option<i32>,
3055-
y2: Option<i32>,
3056-
width: Option<i32>,
3057-
height: Option<i32>,
3052+
x1: Option<Coordinate>,
3053+
y1: Option<Coordinate>,
3054+
x2: Option<Coordinate>,
3055+
y2: Option<Coordinate>,
3056+
width: Option<Coordinate>,
3057+
height: Option<Coordinate>,
30583058
) -> Result<(), CphError> {
30593059
let tl = self.get_window(window)?;
30603060
let pos = tl.node_absolute_position(LiveTL);
@@ -4179,24 +4179,22 @@ impl ConfigProxyHandler {
41794179
}
41804180
}
41814181

4182-
/// Resolves the constraints on a single axis (x1/x2/width or y1/y2/height) of
4183-
/// [`ConfigProxyHandler::handle_set_window_position`] into a concrete `(c1, c2)` pair.
4184-
///
4185-
/// `c1`, `c2` and `size` are constrained to their contained value if they are `Some(_)` and
4186-
/// are otherwise unconstrained.
4187-
///
4188-
/// If two of the three are constrained, the third is derived from them. If only one is
4189-
/// constrained, the window keeps its current size (if `c1` or `c2` is constrained) or its
4190-
/// current position (if only `size` is constrained). If none are constrained, the axis is
4191-
/// left unchanged. If all three are constrained but not self-consistent, an error is returned.
41924182
fn resolve_geometry_axis(
4193-
c1: Option<i32>,
4194-
c2: Option<i32>,
4195-
size: Option<i32>,
4183+
c1: Option<Coordinate>,
4184+
c2: Option<Coordinate>,
4185+
size: Option<Coordinate>,
41964186
cur_c1: i32,
41974187
cur_c2: i32,
41984188
) -> Result<(i32, i32), CphError> {
41994189
let cur_size = cur_c2.saturating_sub(cur_c1);
4190+
let resolve = |c: Option<Coordinate>, cur: i32| match c {
4191+
Some(Coordinate::Set(v)) => Some(v),
4192+
Some(Coordinate::Keep) => Some(cur),
4193+
None => None,
4194+
};
4195+
let c1 = resolve(c1, cur_c1);
4196+
let c2 = resolve(c2, cur_c2);
4197+
let size = resolve(size, cur_size);
42004198
let res = match (c1, c2, size) {
42014199
(Some(c1), Some(c2), Some(size)) => {
42024200
if c2.saturating_sub(c1) != size {
@@ -4207,8 +4205,8 @@ fn resolve_geometry_axis(
42074205
(Some(c1), Some(c2), None) => (c1, c2),
42084206
(Some(c1), None, Some(size)) => (c1, c1.saturating_add(size)),
42094207
(None, Some(c2), Some(size)) => (c2.saturating_sub(size), c2),
4210-
(Some(c1), None, None) => (c1, c1.saturating_add(cur_size)),
4211-
(None, Some(c2), None) => (c2.saturating_sub(cur_size), c2),
4208+
(Some(c1), None, None) => (c1, cur_c2),
4209+
(None, Some(c2), None) => (cur_c1, c2),
42124210
(None, None, Some(size)) => (cur_c1, cur_c1.saturating_add(size)),
42134211
(None, None, None) => (cur_c1, cur_c2),
42144212
};

toml-config/src/config.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use {
4242
BlendSpace, ColorSpace, Connector, Eotf, Format, GfxApi, ScalingFilter, TearingMode,
4343
Transform, VrrMode,
4444
},
45-
window::{ContentType, TileState, WindowType},
45+
window::{ContentType, Coordinate, TileState, WindowType},
4646
workspace::WorkspaceDisplayOrder,
4747
xwayland::XScalingMode,
4848
},
@@ -215,12 +215,12 @@ pub enum Action {
215215
ws: Rc<WorkspaceSlot>,
216216
},
217217
SetPosition {
218-
x1: Option<i32>,
219-
y1: Option<i32>,
220-
x2: Option<i32>,
221-
y2: Option<i32>,
222-
width: Option<i32>,
223-
height: Option<i32>,
218+
x1: Option<Coordinate>,
219+
y1: Option<Coordinate>,
220+
x2: Option<Coordinate>,
221+
y2: Option<Coordinate>,
222+
width: Option<Coordinate>,
223+
height: Option<Coordinate>,
224224
},
225225
}
226226

toml-config/src/config/parsers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod config;
1818
mod connector;
1919
mod connector_match;
2020
mod content_type;
21+
mod coordinate;
2122
mod drm_device;
2223
mod drm_device_match;
2324
mod egui;

toml-config/src/config/parsers/action.rs

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use {
88
parsers::{
99
StringParser, StringParserError,
1010
connector::{ConnectorParser, ConnectorParserError},
11+
coordinate::{CoordinateParser, CoordinateParserError},
1112
drm_device::{DrmDeviceParser, DrmDeviceParserError},
1213
drm_device_match::{DrmDeviceMatchParser, DrmDeviceMatchParserError},
1314
env::{EnvParser, EnvParserError},
@@ -97,6 +98,8 @@ pub enum ActionParserError {
9798
ShowOverlay(#[source] ShowWorkspaceError),
9899
#[error("Could not parse a toggle-overlay action")]
99100
ToggleOverlay(#[source] ShowWorkspaceError),
101+
#[error("Could not parse a coordinate")]
102+
Coordinate(#[source] CoordinateParserError),
100103
#[error("Unknown direction {0}")]
101104
UnknownDirection(String),
102105
#[error("Exactly one of `output` or `direction` must be specified")]
@@ -111,28 +114,6 @@ pub enum ShowWorkspaceError {
111114
FallbackOutputModeParser(FallbackOutputModeParserError),
112115
}
113116

114-
/// Extracts a field that should either be an integer or the string `"keep"`.
115-
///
116-
/// `"keep"` is represented as `None`, an integer `n` is represented as `Some(n)`.
117-
fn coordinate(
118-
name: &'static str,
119-
) -> impl for<'v, 'w> FnOnce(
120-
&mut Extractor<'v, 'w>,
121-
) -> Result<Spanned<Option<i32>>, Spanned<ExtractorError>> {
122-
move |extractor: &mut Extractor| {
123-
val(name)(extractor).and_then(|v| match v.value {
124-
Value::String(s) if s.as_str() == "keep" => Ok(None.spanned(v.span)),
125-
Value::Integer(i) => match i32::try_from(*i) {
126-
Ok(n) => Ok(Some(n).spanned(v.span)),
127-
Err(_) => Err(ExtractorError::I32.spanned(v.span)),
128-
},
129-
_ => Err(
130-
ExtractorError::Expected("an integer or \"keep\"", v.value.name()).spanned(v.span),
131-
),
132-
})
133-
}
134-
}
135-
136117
pub struct ActionParser<'a, 'b>(pub &'a Context<'b>);
137118

138119
impl ActionParser<'_, '_> {
@@ -625,20 +606,27 @@ impl ActionParser<'_, '_> {
625606

626607
fn parse_set_position(&mut self, ext: &mut Extractor<'_, '_>) -> ParseResult<Self> {
627608
let (x1, y1, x2, y2, width, height) = ext.extract((
628-
opt(coordinate("x1")),
629-
opt(coordinate("y1")),
630-
opt(coordinate("x2")),
631-
opt(coordinate("y2")),
632-
opt(coordinate("width")),
633-
opt(coordinate("height")),
609+
opt(val("x1")),
610+
opt(val("y1")),
611+
opt(val("x2")),
612+
opt(val("y2")),
613+
opt(val("width")),
614+
opt(val("height")),
634615
))?;
616+
let coordinate = |v: Option<Spanned<&Value>>| match v {
617+
None => Ok(None),
618+
Some(v) => v
619+
.parse_map(&mut CoordinateParser)
620+
.map_spanned_err(ActionParserError::Coordinate)
621+
.map(Some),
622+
};
635623
Ok(Action::SetPosition {
636-
x1: x1.despan().flatten(),
637-
y1: y1.despan().flatten(),
638-
x2: x2.despan().flatten(),
639-
y2: y2.despan().flatten(),
640-
width: width.despan().flatten(),
641-
height: height.despan().flatten(),
624+
x1: coordinate(x1)?,
625+
y1: coordinate(y1)?,
626+
x2: coordinate(x2)?,
627+
y2: coordinate(y2)?,
628+
width: coordinate(width)?,
629+
height: coordinate(height)?,
642630
})
643631
}
644632

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use {
2+
crate::{
3+
config::parser::{DataType, ParseResult, Parser, UnexpectedDataType},
4+
toml::toml_span::{Span, SpannedExt},
5+
},
6+
jay_config::window::Coordinate,
7+
thiserror::Error,
8+
};
9+
10+
#[derive(Debug, Error)]
11+
pub enum CoordinateParserError {
12+
#[error(transparent)]
13+
Expected(#[from] UnexpectedDataType),
14+
#[error("Expected the string \"keep\" but found \"{0}\"")]
15+
UnknownKeyword(String),
16+
#[error("Value must fit in a i32")]
17+
NotI32,
18+
}
19+
20+
pub struct CoordinateParser;
21+
22+
impl Parser for CoordinateParser {
23+
type Value = Coordinate;
24+
type Error = CoordinateParserError;
25+
const EXPECTED: &'static [DataType] = &[DataType::String, DataType::Integer];
26+
27+
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
28+
match string {
29+
"keep" => Ok(Coordinate::Keep),
30+
_ => Err(CoordinateParserError::UnknownKeyword(string.to_string()).spanned(span)),
31+
}
32+
}
33+
34+
fn parse_integer(&mut self, span: Span, integer: i64) -> ParseResult<Self> {
35+
match i32::try_from(integer) {
36+
Ok(n) => Ok(Coordinate::Set(n)),
37+
Err(_) => Err(CoordinateParserError::NotI32.spanned(span)),
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)