Merge pull request #631 from mahkoh/jorth/show-workspace-on
config: add Seat::show_workspace_on
This commit is contained in:
commit
f17800517e
12 changed files with 131 additions and 13 deletions
|
|
@ -122,6 +122,7 @@ pub(crate) struct ConfigClient {
|
||||||
window_match_handlers: RefCell<HashMap<WindowMatcher, WindowMatchHandler>>,
|
window_match_handlers: RefCell<HashMap<WindowMatcher, WindowMatchHandler>>,
|
||||||
|
|
||||||
feat_mod_mask: Cell<bool>,
|
feat_mod_mask: Cell<bool>,
|
||||||
|
feat_show_workspace_on: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ClientMatchHandler {
|
struct ClientMatchHandler {
|
||||||
|
|
@ -266,6 +267,7 @@ pub unsafe extern "C" fn init(
|
||||||
client_match_handlers: Default::default(),
|
client_match_handlers: Default::default(),
|
||||||
window_match_handlers: Default::default(),
|
window_match_handlers: Default::default(),
|
||||||
feat_mod_mask: Cell::new(false),
|
feat_mod_mask: Cell::new(false),
|
||||||
|
feat_show_workspace_on: Cell::new(false),
|
||||||
});
|
});
|
||||||
let init = unsafe { slice::from_raw_parts(init, size) };
|
let init = unsafe { slice::from_raw_parts(init, size) };
|
||||||
client.handle_init_msg(init);
|
client.handle_init_msg(init);
|
||||||
|
|
@ -591,6 +593,18 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::ShowWorkspace { seat, workspace });
|
self.send(&ClientMessage::ShowWorkspace { seat, workspace });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn show_workspace_on(&self, seat: Seat, workspace: Workspace, connector: Connector) {
|
||||||
|
if self.feat_show_workspace_on.get() && connector.connected() {
|
||||||
|
self.send(&ClientMessage::ShowWorkspaceOn {
|
||||||
|
seat,
|
||||||
|
workspace,
|
||||||
|
connector,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.show_workspace(seat, workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_seat_workspace(&self, seat: Seat, workspace: Workspace) {
|
pub fn set_seat_workspace(&self, seat: Seat, workspace: Workspace) {
|
||||||
self.send(&ClientMessage::SetSeatWorkspace { seat, workspace });
|
self.send(&ClientMessage::SetSeatWorkspace { seat, workspace });
|
||||||
}
|
}
|
||||||
|
|
@ -2086,6 +2100,7 @@ impl ConfigClient {
|
||||||
match feat {
|
match feat {
|
||||||
ServerFeature::NONE => {}
|
ServerFeature::NONE => {}
|
||||||
ServerFeature::MOD_MASK => self.feat_mod_mask.set(true),
|
ServerFeature::MOD_MASK => self.feat_mod_mask.set(true),
|
||||||
|
ServerFeature::SHOW_WORKSPACE_ON => self.feat_show_workspace_on.set(true),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub struct ServerFeature(u16);
|
||||||
impl ServerFeature {
|
impl ServerFeature {
|
||||||
pub const NONE: Self = Self(0);
|
pub const NONE: Self = Self(0);
|
||||||
pub const MOD_MASK: Self = Self(1);
|
pub const MOD_MASK: Self = Self(1);
|
||||||
|
pub const SHOW_WORKSPACE_ON: Self = Self(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
@ -782,6 +783,11 @@ pub enum ClientMessage<'a> {
|
||||||
matcher: ClientMatcher,
|
matcher: ClientMatcher,
|
||||||
caps: ClientCapabilities,
|
caps: ClientCapabilities,
|
||||||
},
|
},
|
||||||
|
ShowWorkspaceOn {
|
||||||
|
seat: Seat,
|
||||||
|
workspace: Workspace,
|
||||||
|
connector: Connector,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,15 @@ impl Seat {
|
||||||
get!().show_workspace(self, workspace)
|
get!().show_workspace(self, workspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shows the workspace and sets the keyboard focus of the seat to that workspace.
|
||||||
|
///
|
||||||
|
/// If the workspace doesn't currently exist and the connector is connected, the
|
||||||
|
/// workspace is created on the given connector. If the connector is not connected,
|
||||||
|
/// the workspace is created on the output that contains the seat's cursor.
|
||||||
|
pub fn show_workspace_on(self, workspace: Workspace, connector: Connector) {
|
||||||
|
get!().show_workspace_on(self, workspace, connector)
|
||||||
|
}
|
||||||
|
|
||||||
/// Moves the currently focused window to the workspace.
|
/// Moves the currently focused window to the workspace.
|
||||||
pub fn set_workspace(self, workspace: Workspace) {
|
pub fn set_workspace(self, workspace: Workspace) {
|
||||||
get!().set_seat_workspace(self, workspace)
|
get!().set_seat_workspace(self, workspace)
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ impl ConfigProxy {
|
||||||
|
|
||||||
pub fn configure(&self, reload: bool) {
|
pub fn configure(&self, reload: bool) {
|
||||||
self.send(&ServerMessage::Features {
|
self.send(&ServerMessage::Features {
|
||||||
features: vec![ServerFeature::MOD_MASK],
|
features: vec![ServerFeature::MOD_MASK, ServerFeature::SHOW_WORKSPACE_ON],
|
||||||
});
|
});
|
||||||
self.send(&ServerMessage::Configure { reload });
|
self.send(&ServerMessage::Configure { reload });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1006,10 +1006,16 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_show_workspace(&self, seat: Seat, ws: Workspace) -> Result<(), CphError> {
|
fn handle_show_workspace(
|
||||||
|
&self,
|
||||||
|
seat: Seat,
|
||||||
|
ws: Workspace,
|
||||||
|
output: Option<Connector>,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
let seat = self.get_seat(seat)?;
|
let seat = self.get_seat(seat)?;
|
||||||
let name = self.get_workspace(ws)?;
|
let name = self.get_workspace(ws)?;
|
||||||
self.state.show_workspace(&seat, &name);
|
let output = output.map(|o| self.get_output_node(o)).transpose()?;
|
||||||
|
self.state.show_workspace(&seat, &name, output);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2725,7 +2731,7 @@ impl ConfigProxyHandler {
|
||||||
}
|
}
|
||||||
ClientMessage::GetWorkspace { name } => self.handle_get_workspace(name),
|
ClientMessage::GetWorkspace { name } => self.handle_get_workspace(name),
|
||||||
ClientMessage::ShowWorkspace { seat, workspace } => self
|
ClientMessage::ShowWorkspace { seat, workspace } => self
|
||||||
.handle_show_workspace(seat, workspace)
|
.handle_show_workspace(seat, workspace, None)
|
||||||
.wrn("show_workspace")?,
|
.wrn("show_workspace")?,
|
||||||
ClientMessage::SetSeatWorkspace { seat, workspace } => self
|
ClientMessage::SetSeatWorkspace { seat, workspace } => self
|
||||||
.handle_set_seat_workspace(seat, workspace)
|
.handle_set_seat_workspace(seat, workspace)
|
||||||
|
|
@ -3203,6 +3209,13 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetClientMatcherBoundingCapabilities { matcher, caps } => self
|
ClientMessage::SetClientMatcherBoundingCapabilities { matcher, caps } => self
|
||||||
.handle_set_client_matcher_bounding_capabilities(matcher, caps)
|
.handle_set_client_matcher_bounding_capabilities(matcher, caps)
|
||||||
.wrn("set_client_matcher_bounding_capabilities")?,
|
.wrn("set_client_matcher_bounding_capabilities")?,
|
||||||
|
ClientMessage::ShowWorkspaceOn {
|
||||||
|
seat,
|
||||||
|
workspace,
|
||||||
|
connector,
|
||||||
|
} => self
|
||||||
|
.handle_show_workspace(seat, workspace, Some(connector))
|
||||||
|
.wrn("show_workspace_on")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -906,11 +906,16 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_workspace(&self, seat: &Rc<WlSeatGlobal>, name: &str) {
|
pub fn show_workspace(
|
||||||
|
&self,
|
||||||
|
seat: &Rc<WlSeatGlobal>,
|
||||||
|
name: &str,
|
||||||
|
output: Option<Rc<OutputNode>>,
|
||||||
|
) {
|
||||||
let ws = match self.workspaces.get(name) {
|
let ws = match self.workspaces.get(name) {
|
||||||
Some(ws) => ws,
|
Some(ws) => ws,
|
||||||
_ => {
|
_ => {
|
||||||
let output = seat.get_output();
|
let output = output.unwrap_or_else(|| seat.get_output());
|
||||||
if output.is_dummy {
|
if output.is_dummy {
|
||||||
log::warn!("Not showing workspace because seat is on dummy output");
|
log::warn!("Not showing workspace because seat is on dummy output");
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ pub enum Action {
|
||||||
},
|
},
|
||||||
ShowWorkspace {
|
ShowWorkspace {
|
||||||
name: String,
|
name: String,
|
||||||
|
output: Option<OutputMatch>,
|
||||||
},
|
},
|
||||||
SimpleCommand {
|
SimpleCommand {
|
||||||
cmd: SimpleCommand,
|
cmd: SimpleCommand,
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ pub enum ActionParserError {
|
||||||
JumpToMark(#[source] MarkIdParserError),
|
JumpToMark(#[source] MarkIdParserError),
|
||||||
#[error("Could not parse a copy-mark action")]
|
#[error("Could not parse a copy-mark action")]
|
||||||
CopyMark(#[source] MarkIdParserError),
|
CopyMark(#[source] MarkIdParserError),
|
||||||
|
#[error("Could not parse a show-workspace action")]
|
||||||
|
ShowWorkspace(#[source] OutputMatchParserError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ActionParser<'a>(pub &'a Context<'a>);
|
pub struct ActionParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
@ -184,8 +186,15 @@ impl ActionParser<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_show_workspace(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
fn parse_show_workspace(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
let name = ext.extract(str("name"))?.value.to_string();
|
let (name, output) = ext.extract((str("name"), opt(val("output"))))?;
|
||||||
Ok(Action::ShowWorkspace { name })
|
let name = name.value.to_string();
|
||||||
|
let output = output
|
||||||
|
.map(|o| {
|
||||||
|
o.parse_map(&mut OutputMatchParser(self.0))
|
||||||
|
.map_spanned_err(ActionParserError::ShowWorkspace)
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
Ok(Action::ShowWorkspace { name, output })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_move_to_workspace(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
fn parse_move_to_workspace(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
|
|
|
||||||
|
|
@ -235,9 +235,26 @@ impl Action {
|
||||||
}
|
}
|
||||||
Action::Exec { exec } => b.new(move || create_command(&exec).spawn()),
|
Action::Exec { exec } => b.new(move || create_command(&exec).spawn()),
|
||||||
Action::SwitchToVt { num } => b.new(move || switch_to_vt(num)),
|
Action::SwitchToVt { num } => b.new(move || switch_to_vt(num)),
|
||||||
Action::ShowWorkspace { name } => {
|
Action::ShowWorkspace { name, output } => {
|
||||||
let workspace = get_workspace(&name);
|
let workspace = get_workspace(&name);
|
||||||
b.new(move || s.show_workspace(workspace))
|
let state = state.clone();
|
||||||
|
b.new(move || {
|
||||||
|
let output = 'get_output: {
|
||||||
|
let Some(output) = &output else {
|
||||||
|
break 'get_output None;
|
||||||
|
};
|
||||||
|
for connector in connectors() {
|
||||||
|
if connector.connected() && output.matches(connector, &state) {
|
||||||
|
break 'get_output Some(connector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
};
|
||||||
|
match output {
|
||||||
|
Some(o) => s.show_workspace_on(workspace, o),
|
||||||
|
_ => s.show_workspace(workspace),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Action::MoveToWorkspace { name } => {
|
Action::MoveToWorkspace { name } => {
|
||||||
let workspace = get_workspace(&name);
|
let workspace = get_workspace(&name);
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Switches to a workspace.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n ```\n",
|
"description": "Switches to a workspace.\n\n- Example 1:\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n ```\n\n- Example 2:\n\n ```toml\n [shortcuts]\n alt-F1 = { type = \"show-workspace\", name = \"1\", output.name = \"left\" }\n ```\n",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
|
|
@ -134,6 +134,10 @@
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name of the workspace."
|
"description": "The name of the workspace."
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"description": "The output to show a newly created workspace on. This has no effect on\nworkspaces that already exist.\n\nIf this is not set, then a new workspace is shown on the output that\ncontains the cursor.\n\nIf multiple outputs match, the workspace is shown on the first matching\noutput.\n",
|
||||||
|
"$ref": "#/$defs/OutputMatch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
||||||
|
|
@ -232,13 +232,20 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
||||||
|
|
||||||
Switches to a workspace.
|
Switches to a workspace.
|
||||||
|
|
||||||
- Example:
|
- Example 1:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[shortcuts]
|
[shortcuts]
|
||||||
alt-F1 = { type = "show-workspace", name = "1" }
|
alt-F1 = { type = "show-workspace", name = "1" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Example 2:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-F1 = { type = "show-workspace", name = "1", output.name = "left" }
|
||||||
|
```
|
||||||
|
|
||||||
The table has the following fields:
|
The table has the following fields:
|
||||||
|
|
||||||
- `name` (required):
|
- `name` (required):
|
||||||
|
|
@ -247,6 +254,19 @@ 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` (optional):
|
||||||
|
|
||||||
|
The output to show a newly created workspace on. This has no effect on
|
||||||
|
workspaces that already exist.
|
||||||
|
|
||||||
|
If this is not set, then a new workspace is shown on the output that
|
||||||
|
contains the cursor.
|
||||||
|
|
||||||
|
If multiple outputs match, the workspace is shown on the first matching
|
||||||
|
output.
|
||||||
|
|
||||||
|
The value of this field should be a [OutputMatch](#types-OutputMatch).
|
||||||
|
|
||||||
- `move-to-workspace`:
|
- `move-to-workspace`:
|
||||||
|
|
||||||
Moves the currently focused window to a workspace.
|
Moves the currently focused window to a workspace.
|
||||||
|
|
|
||||||
|
|
@ -239,17 +239,36 @@ Action:
|
||||||
description: |
|
description: |
|
||||||
Switches to a workspace.
|
Switches to a workspace.
|
||||||
|
|
||||||
- Example:
|
- Example 1:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[shortcuts]
|
[shortcuts]
|
||||||
alt-F1 = { type = "show-workspace", name = "1" }
|
alt-F1 = { type = "show-workspace", name = "1" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Example 2:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-F1 = { type = "show-workspace", name = "1", output.name = "left" }
|
||||||
|
```
|
||||||
fields:
|
fields:
|
||||||
name:
|
name:
|
||||||
description: The name of the workspace.
|
description: The name of the workspace.
|
||||||
required: true
|
required: true
|
||||||
kind: string
|
kind: string
|
||||||
|
output:
|
||||||
|
description: |
|
||||||
|
The output to show a newly created workspace on. This has no effect on
|
||||||
|
workspaces that already exist.
|
||||||
|
|
||||||
|
If this is not set, then a new workspace is shown on the output that
|
||||||
|
contains the cursor.
|
||||||
|
|
||||||
|
If multiple outputs match, the workspace is shown on the first matching
|
||||||
|
output.
|
||||||
|
required: false
|
||||||
|
ref: OutputMatch
|
||||||
move-to-workspace:
|
move-to-workspace:
|
||||||
description: |
|
description: |
|
||||||
Moves the currently focused window to a workspace.
|
Moves the currently focused window to a workspace.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue