Merge pull request #664 from ArthurHeymans/DirectionMoveWorkspace
feat(toml-config): Add directional output selection for move-to-output action
This commit is contained in:
commit
0e49b33a7f
13 changed files with 348 additions and 25 deletions
|
|
@ -1385,6 +1385,31 @@ impl ConfigClient {
|
||||||
workspaces
|
workspaces
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the connector that the workspace is currently on.
|
||||||
|
/// Returns `Connector(0)` (invalid connector) if the workspace doesn't exist or
|
||||||
|
/// isn't assigned to any connector.
|
||||||
|
pub fn get_workspace_connector(&self, workspace: Workspace) -> Connector {
|
||||||
|
let res = self.send_with_response(&ClientMessage::GetWorkspaceConnector { workspace });
|
||||||
|
get_response!(res, Connector(0), GetWorkspaceConnector { connector });
|
||||||
|
connector
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the connector in the specified direction from the given connector.
|
||||||
|
/// Returns `Connector(0)` (invalid connector) if no connector exists in that direction
|
||||||
|
/// or if the source connector is invalid.
|
||||||
|
pub fn get_connector_in_direction(
|
||||||
|
&self,
|
||||||
|
connector: Connector,
|
||||||
|
direction: Direction,
|
||||||
|
) -> Connector {
|
||||||
|
let res = self.send_with_response(&ClientMessage::GetConnectorInDirection {
|
||||||
|
connector,
|
||||||
|
direction,
|
||||||
|
});
|
||||||
|
get_response!(res, Connector(0), GetConnectorInDirection { connector });
|
||||||
|
connector
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_client_matcher_capabilities(
|
pub fn set_client_matcher_capabilities(
|
||||||
&self,
|
&self,
|
||||||
matcher: ClientMatcher,
|
matcher: ClientMatcher,
|
||||||
|
|
|
||||||
|
|
@ -805,6 +805,13 @@ pub enum ClientMessage<'a> {
|
||||||
show: bool,
|
show: bool,
|
||||||
},
|
},
|
||||||
GetShowTitles,
|
GetShowTitles,
|
||||||
|
GetWorkspaceConnector {
|
||||||
|
workspace: Workspace,
|
||||||
|
},
|
||||||
|
GetConnectorInDirection {
|
||||||
|
connector: Connector,
|
||||||
|
direction: Direction,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
@ -1043,6 +1050,12 @@ pub enum Response {
|
||||||
GetShowTitles {
|
GetShowTitles {
|
||||||
show: bool,
|
show: bool,
|
||||||
},
|
},
|
||||||
|
GetWorkspaceConnector {
|
||||||
|
connector: Connector,
|
||||||
|
},
|
||||||
|
GetConnectorInDirection {
|
||||||
|
connector: Connector,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,13 @@ impl Workspace {
|
||||||
pub fn window(self) -> Window {
|
pub fn window(self) -> Window {
|
||||||
get!(Window(0)).get_workspace_window(self)
|
get!(Window(0)).get_workspace_window(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the connector that contains this workspace.
|
||||||
|
///
|
||||||
|
/// If no such connector exists, [`Connector::exists`] returns false.
|
||||||
|
pub fn connector(self) -> Connector {
|
||||||
|
get!(Connector(0)).get_workspace_connector(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the workspace with the given name.
|
/// Returns the workspace with the given name.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
_private::WireMode,
|
_private::WireMode,
|
||||||
PciId, Workspace,
|
Direction, PciId, Workspace,
|
||||||
video::connector_type::{
|
video::connector_type::{
|
||||||
CON_9PIN_DIN, CON_COMPONENT, CON_COMPOSITE, CON_DISPLAY_PORT, CON_DPI, CON_DSI,
|
CON_9PIN_DIN, CON_COMPONENT, CON_COMPOSITE, CON_DISPLAY_PORT, CON_DPI, CON_DSI,
|
||||||
CON_DVIA, CON_DVID, CON_DVII, CON_EDP, CON_EMBEDDED_WINDOW, CON_HDMIA, CON_HDMIB,
|
CON_DVIA, CON_DVID, CON_DVII, CON_EDP, CON_EMBEDDED_WINDOW, CON_HDMIA, CON_HDMIB,
|
||||||
|
|
@ -327,6 +327,17 @@ impl Connector {
|
||||||
pub fn workspaces(self) -> Vec<Workspace> {
|
pub fn workspaces(self) -> Vec<Workspace> {
|
||||||
get!().get_connector_workspaces(self)
|
get!().get_connector_workspaces(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the closest connector in the given direction.
|
||||||
|
///
|
||||||
|
/// Uses center-to-center distance calculation and prefers outputs better aligned
|
||||||
|
/// with the movement axis.
|
||||||
|
///
|
||||||
|
/// If no connector exists in the given direction, returns a connector whose
|
||||||
|
/// `exists()` returns false.
|
||||||
|
pub fn connector_in_direction(self, direction: Direction) -> Connector {
|
||||||
|
get!(Connector(0)).get_connector_in_direction(self, direction)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all available DRM devices.
|
/// Returns all available DRM devices.
|
||||||
|
|
|
||||||
|
|
@ -1577,6 +1577,32 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
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> {
|
fn handle_has_capability(&self, device: InputDevice, cap: Capability) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
let mut is_unknown = false;
|
let mut is_unknown = false;
|
||||||
|
|
@ -3073,6 +3099,15 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::GetConnectorWorkspaces { connector } => self
|
ClientMessage::GetConnectorWorkspaces { connector } => self
|
||||||
.handle_get_connector_workspaces(connector)
|
.handle_get_connector_workspaces(connector)
|
||||||
.wrn("get_connector_workspaces")?,
|
.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::GetClients => self.handle_get_clients(),
|
||||||
ClientMessage::ClientExists { client } => self.handle_client_exists(client),
|
ClientMessage::ClientExists { client } => self.handle_client_exists(client),
|
||||||
ClientMessage::ClientIsXwayland { client } => self
|
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 {
|
pub fn node_at(&self, x: i32, y: i32) -> FoundNode {
|
||||||
let mut found_tree = self.node_at_tree.borrow_mut();
|
let mut found_tree = self.node_at_tree.borrow_mut();
|
||||||
found_tree.push(FoundNode {
|
found_tree.push(FoundNode {
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,8 @@ pub enum Action {
|
||||||
},
|
},
|
||||||
MoveToOutput {
|
MoveToOutput {
|
||||||
workspace: Option<Workspace>,
|
workspace: Option<Workspace>,
|
||||||
output: OutputMatch,
|
output: Option<OutputMatch>,
|
||||||
|
direction: Option<Direction>,
|
||||||
},
|
},
|
||||||
SetRepeatRate {
|
SetRepeatRate {
|
||||||
rate: RepeatRate,
|
rate: RepeatRate,
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@ impl<'v> Extractor<'v> {
|
||||||
self.log_unused = false;
|
self.log_unused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
self.span
|
||||||
|
}
|
||||||
|
|
||||||
pub fn extract<E: Extractable<'v>, U>(&mut self, e: E) -> Result<E::Output, Spanned<U>>
|
pub fn extract<E: Extractable<'v>, U>(&mut self, e: E) -> Result<E::Output, Spanned<U>>
|
||||||
where
|
where
|
||||||
ExtractorError: Into<U>,
|
ExtractorError: Into<U>,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ use {
|
||||||
indexmap::IndexMap,
|
indexmap::IndexMap,
|
||||||
jay_config::{
|
jay_config::{
|
||||||
Axis::{Horizontal, Vertical},
|
Axis::{Horizontal, Vertical},
|
||||||
get_workspace,
|
Direction, get_workspace,
|
||||||
input::{LayerDirection, Timeline},
|
input::{LayerDirection, Timeline},
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
|
@ -90,6 +90,10 @@ pub enum ActionParserError {
|
||||||
CopyMark(#[source] MarkIdParserError),
|
CopyMark(#[source] MarkIdParserError),
|
||||||
#[error("Could not parse a show-workspace action")]
|
#[error("Could not parse a show-workspace action")]
|
||||||
ShowWorkspace(#[source] OutputMatchParserError),
|
ShowWorkspace(#[source] OutputMatchParserError),
|
||||||
|
#[error("Unknown direction {0}")]
|
||||||
|
UnknownDirection(String),
|
||||||
|
#[error("Exactly one of `output` or `direction` must be specified")]
|
||||||
|
OutputAndDirectionMutuallyExclusive,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ActionParser<'a>(pub &'a Context<'a>);
|
pub struct ActionParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
@ -356,14 +360,40 @@ impl ActionParser<'_> {
|
||||||
Ok(Action::ConfigureDrmDevice { dev })
|
Ok(Action::ConfigureDrmDevice { dev })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_direction(v: Spanned<&str>) -> Result<Direction, Spanned<ActionParserError>> {
|
||||||
|
use Direction::*;
|
||||||
|
match v.value {
|
||||||
|
"left" => Ok(Left),
|
||||||
|
"right" => Ok(Right),
|
||||||
|
"up" => Ok(Up),
|
||||||
|
"down" => Ok(Down),
|
||||||
|
_ => Err(ActionParserError::UnknownDirection(v.value.to_string()).spanned(v.span)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_move_to_output(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
fn parse_move_to_output(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
let (ws, output) = ext.extract((opt(str("workspace")), val("output")))?;
|
let (ws, output_val, direction_val) = ext.extract((
|
||||||
let output = output
|
opt(str("workspace")),
|
||||||
.parse_map(&mut OutputMatchParser(self.0))
|
opt(val("output")),
|
||||||
.map_spanned_err(ActionParserError::MoveToOutput)?;
|
opt(str("direction")),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// Validate that exactly one of output or direction is specified
|
||||||
|
if output_val.is_some() == direction_val.is_some() {
|
||||||
|
return Err(ActionParserError::OutputAndDirectionMutuallyExclusive.spanned(ext.span()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = output_val
|
||||||
|
.map(|v| {
|
||||||
|
v.parse(&mut OutputMatchParser(self.0))
|
||||||
|
.map_spanned_err(ActionParserError::MoveToOutput)
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
let direction = direction_val.map(Self::parse_direction).transpose()?;
|
||||||
Ok(Action::MoveToOutput {
|
Ok(Action::MoveToOutput {
|
||||||
workspace: ws.despan().map(get_workspace),
|
workspace: ws.despan().map(get_workspace),
|
||||||
output,
|
output,
|
||||||
|
direction,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -360,20 +360,52 @@ impl Action {
|
||||||
set_idle_grace_period(period)
|
set_idle_grace_period(period)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Action::MoveToOutput { output, workspace } => {
|
Action::MoveToOutput {
|
||||||
|
output,
|
||||||
|
workspace,
|
||||||
|
direction,
|
||||||
|
} => {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
b.new(move || {
|
b.new(move || {
|
||||||
let output = 'get_output: {
|
let target_output = {
|
||||||
for connector in connectors() {
|
// Handle directional output selection
|
||||||
if connector.connected() && output.matches(connector, &state) {
|
if let Some(direction) = direction {
|
||||||
break 'get_output connector;
|
// Get the current workspace to determine the source output
|
||||||
|
let current_ws = match workspace {
|
||||||
|
Some(ws) => ws,
|
||||||
|
None => s.get_workspace(),
|
||||||
|
};
|
||||||
|
if !current_ws.exists() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
// Get the connector that currently has this workspace
|
||||||
|
let source_connector = current_ws.connector();
|
||||||
|
if !source_connector.exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Find the connector in the given direction
|
||||||
|
let target = source_connector.connector_in_direction(direction);
|
||||||
|
if !target.exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target
|
||||||
|
} else if let Some(output) = &output {
|
||||||
|
// Handle normal output matching
|
||||||
|
'match_output: {
|
||||||
|
for connector in connectors() {
|
||||||
|
if connector.connected() && output.matches(connector, &state) {
|
||||||
|
break 'match_output connector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
match workspace {
|
match workspace {
|
||||||
Some(ws) => ws.move_to_output(output),
|
Some(ws) => ws.move_to_output(target_output),
|
||||||
None => s.move_to_output(output),
|
None => s.move_to_output(target_output),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Moves a workspace to a different output.\n\n- Example 1:\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"move-to-output\", workspace = \"1\", output.name = \"right\" }\n ```\n\n- Example 2:\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"move-to-output\", output.name = \"right\" }\n ```\n",
|
"description": "Moves a workspace to a different output.\n\n- Example 1: Move a specific workspace to a named output\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"move-to-output\", workspace = \"1\", output.name = \"right\" }\n ```\n\n- Example 2: Move the current workspace to a named output\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"move-to-output\", output.name = \"right\" }\n ```\n\n- Example 3: Move the current workspace to the output on the right (directional)\n\n ```toml\n [shortcuts]\n \"logo+ctrl+shift+Right\" = { type = \"move-to-output\", direction = \"right\" }\n \"logo+ctrl+shift+Left\" = { type = \"move-to-output\", direction = \"left\" }\n \"logo+ctrl+shift+Up\" = { type = \"move-to-output\", direction = \"up\" }\n \"logo+ctrl+shift+Down\" = { type = \"move-to-output\", direction = \"down\" }\n ```\n",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
|
|
@ -174,13 +174,16 @@
|
||||||
"description": "The name of the workspace.\n\nIf this is omitted, the currently active workspace is moved.\n"
|
"description": "The name of the workspace.\n\nIf this is omitted, the currently active workspace is moved.\n"
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
"description": "The output to move to.\n\nIf multiple outputs match, the workspace is moved to the first matching\noutput.\n",
|
"description": "The output to move to.\n\nIf multiple outputs match, the workspace is moved to the first matching\noutput.\n\nEither `output` or `direction` must be specified, but not both.\n",
|
||||||
"$ref": "#/$defs/OutputMatch"
|
"$ref": "#/$defs/OutputMatch"
|
||||||
|
},
|
||||||
|
"direction": {
|
||||||
|
"description": "The direction to search for the next output.\n\nFinds the closest output in the specified direction based on\ncenter-to-center distance, with preference for outputs better aligned\nwith the movement axis.\n\nEither `output` or `direction` must be specified, but not both.\n",
|
||||||
|
"$ref": "#/$defs/Direction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"type",
|
"type"
|
||||||
"output"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -1126,6 +1129,16 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Direction": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A directional value used for output selection.\n",
|
||||||
|
"enum": [
|
||||||
|
"left",
|
||||||
|
"right",
|
||||||
|
"up",
|
||||||
|
"down"
|
||||||
|
]
|
||||||
|
},
|
||||||
"DrmDevice": {
|
"DrmDevice": {
|
||||||
"description": "Describes configuration to apply to a DRM device (graphics card).\n\n- Example: To disable direct scanout on a device:\n\n ```toml\n [[drm-devices]]\n match = { pci-vendor = 0x1002, pci-model = 0x73ff }\n direct-scanout = false\n ```\n",
|
"description": "Describes configuration to apply to a DRM device (graphics card).\n\n- Example: To disable direct scanout on a device:\n\n ```toml\n [[drm-devices]]\n match = { pci-vendor = 0x1002, pci-model = 0x73ff }\n direct-scanout = false\n ```\n",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
||||||
|
|
@ -290,19 +290,29 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
||||||
|
|
||||||
Moves a workspace to a different output.
|
Moves a workspace to a different output.
|
||||||
|
|
||||||
- Example 1:
|
- Example 1: Move a specific workspace to a named output
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[shortcuts]
|
[shortcuts]
|
||||||
alt-F1 = { type = "move-to-output", workspace = "1", output.name = "right" }
|
alt-F1 = { type = "move-to-output", workspace = "1", output.name = "right" }
|
||||||
```
|
```
|
||||||
|
|
||||||
- Example 2:
|
- Example 2: Move the current workspace to a named output
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[shortcuts]
|
[shortcuts]
|
||||||
alt-F1 = { type = "move-to-output", output.name = "right" }
|
alt-F1 = { type = "move-to-output", output.name = "right" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Example 3: Move the current workspace to the output on the right (directional)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
"logo+ctrl+shift+Right" = { type = "move-to-output", direction = "right" }
|
||||||
|
"logo+ctrl+shift+Left" = { type = "move-to-output", direction = "left" }
|
||||||
|
"logo+ctrl+shift+Up" = { type = "move-to-output", direction = "up" }
|
||||||
|
"logo+ctrl+shift+Down" = { type = "move-to-output", direction = "down" }
|
||||||
|
```
|
||||||
|
|
||||||
The table has the following fields:
|
The table has the following fields:
|
||||||
|
|
||||||
|
|
@ -314,15 +324,29 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
||||||
|
|
||||||
The value of this field should be a string.
|
The value of this field should be a string.
|
||||||
|
|
||||||
- `output` (required):
|
- `output` (optional):
|
||||||
|
|
||||||
The output to move to.
|
The output to move to.
|
||||||
|
|
||||||
If multiple outputs match, the workspace is moved to the first matching
|
If multiple outputs match, the workspace is moved to the first matching
|
||||||
output.
|
output.
|
||||||
|
|
||||||
|
Either `output` or `direction` must be specified, but not both.
|
||||||
|
|
||||||
The value of this field should be a [OutputMatch](#types-OutputMatch).
|
The value of this field should be a [OutputMatch](#types-OutputMatch).
|
||||||
|
|
||||||
|
- `direction` (optional):
|
||||||
|
|
||||||
|
The direction to search for the next output.
|
||||||
|
|
||||||
|
Finds the closest output in the specified direction based on
|
||||||
|
center-to-center distance, with preference for outputs better aligned
|
||||||
|
with the movement axis.
|
||||||
|
|
||||||
|
Either `output` or `direction` must be specified, but not both.
|
||||||
|
|
||||||
|
The value of this field should be a [Direction](#types-Direction).
|
||||||
|
|
||||||
- `configure-connector`:
|
- `configure-connector`:
|
||||||
|
|
||||||
Applies a configuration to connectors.
|
Applies a configuration to connectors.
|
||||||
|
|
@ -2285,6 +2309,33 @@ An array of masks that are OR'd.
|
||||||
Each element of this array should be a [ContentTypeMask](#types-ContentTypeMask).
|
Each element of this array should be a [ContentTypeMask](#types-ContentTypeMask).
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-Direction"></a>
|
||||||
|
### `Direction`
|
||||||
|
|
||||||
|
A directional value used for output selection.
|
||||||
|
|
||||||
|
Values of this type should be strings.
|
||||||
|
|
||||||
|
The string should have one of the following values:
|
||||||
|
|
||||||
|
- `left`:
|
||||||
|
|
||||||
|
The left direction.
|
||||||
|
|
||||||
|
- `right`:
|
||||||
|
|
||||||
|
The right direction.
|
||||||
|
|
||||||
|
- `up`:
|
||||||
|
|
||||||
|
The up direction.
|
||||||
|
|
||||||
|
- `down`:
|
||||||
|
|
||||||
|
The down direction.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="types-DrmDevice"></a>
|
<a name="types-DrmDevice"></a>
|
||||||
### `DrmDevice`
|
### `DrmDevice`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -288,19 +288,29 @@ Action:
|
||||||
description: |
|
description: |
|
||||||
Moves a workspace to a different output.
|
Moves a workspace to a different output.
|
||||||
|
|
||||||
- Example 1:
|
- Example 1: Move a specific workspace to a named output
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[shortcuts]
|
[shortcuts]
|
||||||
alt-F1 = { type = "move-to-output", workspace = "1", output.name = "right" }
|
alt-F1 = { type = "move-to-output", workspace = "1", output.name = "right" }
|
||||||
```
|
```
|
||||||
|
|
||||||
- Example 2:
|
- Example 2: Move the current workspace to a named output
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[shortcuts]
|
[shortcuts]
|
||||||
alt-F1 = { type = "move-to-output", output.name = "right" }
|
alt-F1 = { type = "move-to-output", output.name = "right" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Example 3: Move the current workspace to the output on the right (directional)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
"logo+ctrl+shift+Right" = { type = "move-to-output", direction = "right" }
|
||||||
|
"logo+ctrl+shift+Left" = { type = "move-to-output", direction = "left" }
|
||||||
|
"logo+ctrl+shift+Up" = { type = "move-to-output", direction = "up" }
|
||||||
|
"logo+ctrl+shift+Down" = { type = "move-to-output", direction = "down" }
|
||||||
|
```
|
||||||
fields:
|
fields:
|
||||||
workspace:
|
workspace:
|
||||||
description: |
|
description: |
|
||||||
|
|
@ -315,8 +325,21 @@ Action:
|
||||||
|
|
||||||
If multiple outputs match, the workspace is moved to the first matching
|
If multiple outputs match, the workspace is moved to the first matching
|
||||||
output.
|
output.
|
||||||
required: true
|
|
||||||
|
Either `output` or `direction` must be specified, but not both.
|
||||||
|
required: false
|
||||||
ref: OutputMatch
|
ref: OutputMatch
|
||||||
|
direction:
|
||||||
|
description: |
|
||||||
|
The direction to search for the next output.
|
||||||
|
|
||||||
|
Finds the closest output in the specified direction based on
|
||||||
|
center-to-center distance, with preference for outputs better aligned
|
||||||
|
with the movement axis.
|
||||||
|
|
||||||
|
Either `output` or `direction` must be specified, but not both.
|
||||||
|
required: false
|
||||||
|
ref: Direction
|
||||||
configure-connector:
|
configure-connector:
|
||||||
description: |
|
description: |
|
||||||
Applies a configuration to connectors.
|
Applies a configuration to connectors.
|
||||||
|
|
@ -4193,6 +4216,21 @@ BlendSpace:
|
||||||
description: Linear color space. This is the physically correct blend space.
|
description: Linear color space. This is the physically correct blend space.
|
||||||
|
|
||||||
|
|
||||||
|
Direction:
|
||||||
|
kind: string
|
||||||
|
description: |
|
||||||
|
A directional value used for output selection.
|
||||||
|
values:
|
||||||
|
- value: left
|
||||||
|
description: The left direction.
|
||||||
|
- value: right
|
||||||
|
description: The right direction.
|
||||||
|
- value: up
|
||||||
|
description: The up direction.
|
||||||
|
- value: down
|
||||||
|
description: The down direction.
|
||||||
|
|
||||||
|
|
||||||
ClientCapabilities:
|
ClientCapabilities:
|
||||||
description: |
|
description: |
|
||||||
A mask of client capabilities.
|
A mask of client capabilities.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue