feat(config): Add directional output selection via separate direction field
Add support for directional output selection in the move-to-output action
using a separate `direction` field instead of overloading OutputMatch.
API additions:
- Add Workspace::connector() to get the connector showing a workspace
- Add Connector::connector_in_direction() to find outputs directionally
Implementation:
- Move directional finding logic from toml-config to compositor
- Algorithm uses center-to-center distance with axis-aligned preference
- Add GetWorkspaceConnector and GetConnectorInDirection IPC messages
Configuration changes:
- Add optional `direction` field to move-to-output action
- Either `output` or `direction` must be specified (not both)
- Valid directions: "left", "right", "up", "down"
Example usage:
logo+control+shift+right = { type = "move-to-output", direction = "right" }
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
This commit is contained in:
parent
e81b31b452
commit
5529306c67
13 changed files with 348 additions and 25 deletions
|
|
@ -1577,6 +1577,32 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_workspace_connector(&self, workspace: Workspace) -> Result<(), CphError> {
|
||||
let connector = self
|
||||
.get_existing_workspace(workspace)?
|
||||
.map(|ws| ws.output.get())
|
||||
.filter(|o| !o.is_dummy)
|
||||
.map(|o| Connector(o.global.connector.id.raw() as _))
|
||||
.unwrap_or(Connector(0));
|
||||
self.respond(Response::GetWorkspaceConnector { connector });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_connector_in_direction(
|
||||
&self,
|
||||
connector: Connector,
|
||||
direction: Direction,
|
||||
) -> Result<(), CphError> {
|
||||
let source_output = self.get_output_node(connector)?;
|
||||
let connector = self
|
||||
.state
|
||||
.find_connector_in_direction(&source_output, direction.into())
|
||||
.map(|o| Connector(o.global.connector.id.raw() as u64))
|
||||
.unwrap_or(Connector(0));
|
||||
self.respond(Response::GetConnectorInDirection { connector });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_has_capability(&self, device: InputDevice, cap: Capability) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let mut is_unknown = false;
|
||||
|
|
@ -3073,6 +3099,15 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::GetConnectorWorkspaces { connector } => self
|
||||
.handle_get_connector_workspaces(connector)
|
||||
.wrn("get_connector_workspaces")?,
|
||||
ClientMessage::GetWorkspaceConnector { workspace } => self
|
||||
.handle_get_workspace_connector(workspace)
|
||||
.wrn("get_workspace_connector")?,
|
||||
ClientMessage::GetConnectorInDirection {
|
||||
connector,
|
||||
direction,
|
||||
} => self
|
||||
.handle_get_connector_in_direction(connector, direction)
|
||||
.wrn("get_connector_in_direction")?,
|
||||
ClientMessage::GetClients => self.handle_get_clients(),
|
||||
ClientMessage::ClientExists { client } => self.handle_client_exists(client),
|
||||
ClientMessage::ClientIsXwayland { client } => self
|
||||
|
|
|
|||
63
src/state.rs
63
src/state.rs
|
|
@ -1560,6 +1560,69 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_connector_in_direction(
|
||||
&self,
|
||||
source_output: &OutputNode,
|
||||
direction: Direction,
|
||||
) -> Option<Rc<OutputNode>> {
|
||||
let outputs = self.root.outputs.lock();
|
||||
|
||||
let ref_box = source_output.global.pos.get();
|
||||
let ref_x1 = ref_box.x1();
|
||||
let ref_y1 = ref_box.y1();
|
||||
let ref_x2 = ref_box.x2();
|
||||
let ref_y2 = ref_box.y2();
|
||||
|
||||
// Use the center of the source output as the reference point (like wlroots)
|
||||
let (ref_lx, ref_ly) = ref_box.center();
|
||||
|
||||
// Find the closest output in the given direction using wlroots-style algorithm
|
||||
let mut min_distance = i64::MAX;
|
||||
let mut closest_output = None;
|
||||
|
||||
for output in outputs.values() {
|
||||
if output.id == source_output.id {
|
||||
continue;
|
||||
}
|
||||
|
||||
let box_pos = output.global.pos.get();
|
||||
let box_x1 = box_pos.x1();
|
||||
let box_y1 = box_pos.y1();
|
||||
let box_x2 = box_pos.x2();
|
||||
let box_y2 = box_pos.y2();
|
||||
|
||||
// Edge-based direction check (like wlroots)
|
||||
// Test to make sure this output is in the given direction
|
||||
let is_in_direction = match direction {
|
||||
Direction::Left => box_x2 <= ref_x1,
|
||||
Direction::Right => box_x1 >= ref_x2,
|
||||
Direction::Up => box_y2 <= ref_y1,
|
||||
Direction::Down => box_y1 >= ref_y2,
|
||||
Direction::Unspecified => false,
|
||||
};
|
||||
|
||||
if !is_in_direction {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate distance from reference point to closest point on this output
|
||||
// This mimics wlr_box_closest_point + squared Euclidean distance
|
||||
let closest_x = ref_lx.clamp(box_x1, box_x2);
|
||||
let closest_y = ref_ly.clamp(box_y1, box_y2);
|
||||
|
||||
let dx = (closest_x - ref_lx) as i64;
|
||||
let dy = (closest_y - ref_ly) as i64;
|
||||
let distance = dx * dx + dy * dy;
|
||||
|
||||
if distance < min_distance {
|
||||
min_distance = distance;
|
||||
closest_output = Some(output);
|
||||
}
|
||||
}
|
||||
|
||||
closest_output.cloned()
|
||||
}
|
||||
|
||||
pub fn node_at(&self, x: i32, y: i32) -> FoundNode {
|
||||
let mut found_tree = self.node_at_tree.borrow_mut();
|
||||
found_tree.push(FoundNode {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue