config: add Window
This commit is contained in:
parent
ab095b89cf
commit
9977f9dfdf
19 changed files with 1172 additions and 203 deletions
|
|
@ -19,7 +19,9 @@ use {
|
|||
theme::{Color, ThemeSized},
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
||||
TearingMode, VrrMode, WsMoveConfig, move_ws_to_output,
|
||||
TearingMode, ToplevelNode, VrrMode, WorkspaceNode, WsMoveConfig, move_ws_to_output,
|
||||
toplevel_create_split, toplevel_parent_container, toplevel_set_floating,
|
||||
toplevel_set_workspace,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
|
|
@ -30,6 +32,7 @@ use {
|
|||
oserror::OsError,
|
||||
stack::Stack,
|
||||
timer::{TimerError, TimerFd},
|
||||
toplevel_identifier::ToplevelIdentifier,
|
||||
},
|
||||
},
|
||||
bincode::Options,
|
||||
|
|
@ -57,6 +60,7 @@ use {
|
|||
TearingMode as ConfigTearingMode, TransferFunction as ConfigTransferFunction,
|
||||
Transform, VrrMode as ConfigVrrMode,
|
||||
},
|
||||
window::Window,
|
||||
xwayland::XScalingMode,
|
||||
},
|
||||
libloading::Library,
|
||||
|
|
@ -89,6 +93,10 @@ pub(super) struct ConfigProxyHandler {
|
|||
|
||||
pub pollable_id: NumCell<u64>,
|
||||
pub pollables: CopyHashMap<PollableId, Rc<Pollable>>,
|
||||
|
||||
pub window_ids: NumCell<u64>,
|
||||
pub windows_from_tl_id: CopyHashMap<ToplevelIdentifier, Window>,
|
||||
pub windows_to_tl_id: CopyHashMap<Window, ToplevelIdentifier>,
|
||||
}
|
||||
|
||||
pub struct Pollable {
|
||||
|
|
@ -315,6 +323,24 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_fullscreen(&self, window: Window) -> Result<(), CphError> {
|
||||
let tl = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFullscreen {
|
||||
fullscreen: tl.tl_data().is_fullscreen.get(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_fullscreen(
|
||||
&self,
|
||||
window: Window,
|
||||
fullscreen: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let tl = self.get_window(window)?;
|
||||
tl.tl_set_fullscreen(fullscreen);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_keymap(&self, seat: Seat, keymap: Keymap) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let keymap = if keymap.is_invalid() {
|
||||
|
|
@ -492,6 +518,12 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_close(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
window.tl_close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_focus(&self, seat: Seat, direction: Direction) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_focus(direction.into());
|
||||
|
|
@ -504,6 +536,14 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_move(&self, window: Window, direction: Direction) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.move_child(window, direction.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_repeat_rate(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let (rate, delay) = seat.get_rate();
|
||||
|
|
@ -530,6 +570,11 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_existing_workspace(&self, ws: Workspace) -> Result<Option<Rc<WorkspaceNode>>, CphError> {
|
||||
self.get_workspace(ws)
|
||||
.map(|ws| self.state.workspaces.get(&*ws))
|
||||
}
|
||||
|
||||
fn get_device_handler_data(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
|
|
@ -720,8 +765,8 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_get_workspace_capture(&self, workspace: Workspace) -> Result<(), CphError> {
|
||||
let name = self.get_workspace(workspace)?;
|
||||
let capture = match self.state.workspaces.get(name.as_str()) {
|
||||
let ws = self.get_existing_workspace(workspace)?;
|
||||
let capture = match ws {
|
||||
Some(ws) => ws.may_capture.get(),
|
||||
None => self.state.default_workspace_capture.get(),
|
||||
};
|
||||
|
|
@ -734,8 +779,7 @@ impl ConfigProxyHandler {
|
|||
workspace: Workspace,
|
||||
capture: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let name = self.get_workspace(workspace)?;
|
||||
if let Some(ws) = self.state.workspaces.get(name.as_str()) {
|
||||
if let Some(ws) = self.get_existing_workspace(workspace)? {
|
||||
ws.may_capture.set(capture);
|
||||
ws.update_has_captures();
|
||||
}
|
||||
|
|
@ -856,6 +900,20 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_workspace(&self, window: Window, ws: Workspace) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
let name = self.get_workspace(ws)?;
|
||||
let workspace = match self.state.workspaces.get(name.deref()) {
|
||||
Some(ws) => ws,
|
||||
_ => match window.node_output() {
|
||||
Some(o) => o.create_workspace(name.deref()),
|
||||
_ => return Ok(()),
|
||||
},
|
||||
};
|
||||
toplevel_set_workspace(&self.state, window, &workspace);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_device_name(&self, device: InputDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let name = dev.device.name();
|
||||
|
|
@ -888,13 +946,10 @@ impl ConfigProxyHandler {
|
|||
) -> Result<(), CphError> {
|
||||
let output = self.get_output_node(connector)?;
|
||||
let ws = match workspace {
|
||||
WorkspaceSource::Explicit(ws) => {
|
||||
let name = self.get_workspace(ws)?;
|
||||
match self.state.workspaces.get(name.as_str()) {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
WorkspaceSource::Explicit(ws) => match self.get_existing_workspace(ws)? {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
},
|
||||
WorkspaceSource::Seat(s) => match self.get_seat(s)?.get_output().workspace.get() {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
|
|
@ -1180,6 +1235,20 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_float_pinned(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFloatPinned {
|
||||
pinned: window.tl_pinned(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_float_pinned(&self, window: Window, pinned: bool) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
window.tl_set_pinned(true, pinned);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_vrr_mode(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
|
|
@ -1360,6 +1429,24 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_mono(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowMono {
|
||||
mono: toplevel_parent_container(&*window)
|
||||
.map(|c| c.mono_child.is_some())
|
||||
.unwrap_or(false),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_mono(&self, window: Window, mono: bool) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_mono(mono.then_some(window.as_ref()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_seat_split(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::GetSplit {
|
||||
|
|
@ -1377,6 +1464,25 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_split(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowSplit {
|
||||
axis: toplevel_parent_container(&*window)
|
||||
.map(|c| c.split.get())
|
||||
.unwrap_or(ContainerSplit::Horizontal)
|
||||
.into(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_split(&self, window: Window, axis: Axis) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_split(axis.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_add_shortcut(
|
||||
&self,
|
||||
seat: Seat,
|
||||
|
|
@ -1480,6 +1586,12 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_create_window_split(&self, window: Window, axis: Axis) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_create_split(&self.state, window, axis.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_focus_seat_parent(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_parent();
|
||||
|
|
@ -1509,6 +1621,20 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_floating(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFloating {
|
||||
floating: window.tl_data().is_floating.get(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_floating(&self, window: Window, floating: bool) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_set_floating(&self.state, window, floating);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_add_pollable(self: &Rc<Self>, fd: i32) -> Result<(), CphError> {
|
||||
let fd = match fcntl_dupfd_cloexec(fd, 0) {
|
||||
Ok(fd) => Rc::new(fd),
|
||||
|
|
@ -1577,6 +1703,28 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn tl_to_window(&self, tl: &dyn ToplevelNode) -> Window {
|
||||
self.tl_id_to_window(tl.tl_data().identifier.get())
|
||||
}
|
||||
|
||||
fn tl_id_to_window(&self, tl: ToplevelIdentifier) -> Window {
|
||||
if let Some(win) = self.windows_from_tl_id.get(&tl) {
|
||||
return win;
|
||||
}
|
||||
let id = Window(self.window_ids.fetch_add(1));
|
||||
self.windows_from_tl_id.set(tl, id);
|
||||
self.windows_to_tl_id.set(id, tl);
|
||||
id
|
||||
}
|
||||
|
||||
fn get_window(&self, window: Window) -> Result<Rc<dyn ToplevelNode>, CphError> {
|
||||
self.windows_to_tl_id
|
||||
.get(&window)
|
||||
.and_then(|id| self.state.toplevels.get(&id))
|
||||
.and_then(|tl| tl.upgrade())
|
||||
.ok_or(CphError::WindowDoesNotExist(window))
|
||||
}
|
||||
|
||||
fn spaces_change(&self) {
|
||||
struct V;
|
||||
impl NodeVisitorBase for V {
|
||||
|
|
@ -1752,6 +1900,123 @@ impl ConfigProxyHandler {
|
|||
self.state.clients.kill(ClientId::from_raw(client.0));
|
||||
}
|
||||
|
||||
fn handle_get_workspace_window(&self, ws: Workspace) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_existing_workspace(ws)?
|
||||
.and_then(|ws| ws.container.get())
|
||||
.map(|c| self.tl_to_window(&*c))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetWorkspaceWindow { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_seat_keyboard_window(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_seat(seat)?
|
||||
.get_keyboard_node()
|
||||
.node_toplevel()
|
||||
.map(|tl| self.tl_to_window(&*tl))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetSeatKeyboardWindow { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_focus_window(&self, seat: Seat, window_id: Window) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let window = self.get_window(window_id)?;
|
||||
if !window.node_visible() {
|
||||
return Err(CphError::WindowNotVisible(window_id));
|
||||
}
|
||||
seat.focus_toplevel(window);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_title(&self, window: Window) -> Result<(), CphError> {
|
||||
let title = self.get_window(window)?.tl_data().title.borrow().clone();
|
||||
self.respond(Response::GetWindowTitle { title });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_type(&self, window: Window) -> Result<(), CphError> {
|
||||
let kind = self.get_window(window)?.tl_data().kind.to_window_type();
|
||||
self.respond(Response::GetWindowType { kind });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_exists(&self, window: Window) {
|
||||
self.respond(Response::WindowExists {
|
||||
exists: self.get_window(window).is_ok(),
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_get_window_id(&self, window: Window) -> Result<(), CphError> {
|
||||
let id = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.identifier
|
||||
.get()
|
||||
.to_string();
|
||||
self.respond(Response::GetWindowId { id: id.to_string() });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_is_visible(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowIsVisible {
|
||||
visible: window.node_visible(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_client(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowClient {
|
||||
client: window
|
||||
.tl_data()
|
||||
.client
|
||||
.as_ref()
|
||||
.map(|c| ConfigClient(c.id.raw()))
|
||||
.unwrap_or(ConfigClient(0)),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_parent(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|tl| tl.node_into_toplevel())
|
||||
.map(|tl| self.tl_to_window(&*tl))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetWindowParent { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_workspace(&self, window: Window) -> Result<(), CphError> {
|
||||
let workspace = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.workspace
|
||||
.get()
|
||||
.map(|ws| self.get_workspace_by_name(&ws.name))
|
||||
.unwrap_or(Workspace(0));
|
||||
self.respond(Response::GetWindowWorkspace { workspace });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_children(&self, window: Window) -> Result<(), CphError> {
|
||||
let mut windows = vec![];
|
||||
if let Some(c) = self.get_window(window)?.node_into_container() {
|
||||
for c in c.children.iter() {
|
||||
windows.push(self.tl_to_window(&*c.node));
|
||||
}
|
||||
}
|
||||
self.respond(Response::GetWindowChildren { windows });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_request(self: &Rc<Self>, msg: &[u8]) {
|
||||
if let Err(e) = self.handle_request_(msg) {
|
||||
log::error!("Could not handle client request: {}", ErrorFmt(e));
|
||||
|
|
@ -2171,6 +2436,82 @@ impl ConfigProxyHandler {
|
|||
.handle_client_is_xwayland(client)
|
||||
.wrn("client_is_xwayland")?,
|
||||
ClientMessage::ClientKill { client } => self.handle_client_kill(client),
|
||||
ClientMessage::WindowExists { window } => self.handle_window_exists(window),
|
||||
ClientMessage::GetWorkspaceWindow { workspace } => self
|
||||
.handle_get_workspace_window(workspace)
|
||||
.wrn("get_workspace_window")?,
|
||||
ClientMessage::GetSeatKeyboardWindow { seat } => self
|
||||
.handle_get_seat_keyboard_window(seat)
|
||||
.wrn("get_seat_keyboard_window")?,
|
||||
ClientMessage::SeatFocusWindow { seat, window } => self
|
||||
.handle_seat_focus_window(seat, window)
|
||||
.wrn("seat_focus_window")?,
|
||||
ClientMessage::GetWindowTitle { window } => self
|
||||
.handle_get_window_title(window)
|
||||
.wrn("get_window_title")?,
|
||||
ClientMessage::GetWindowType { window } => {
|
||||
self.handle_get_window_type(window).wrn("get_window_type")?
|
||||
}
|
||||
ClientMessage::GetWindowId { window } => {
|
||||
self.handle_get_window_id(window).wrn("get_window_id")?
|
||||
}
|
||||
ClientMessage::GetWindowParent { window } => self
|
||||
.handle_get_window_parent(window)
|
||||
.wrn("get_window_parent")?,
|
||||
ClientMessage::GetWindowWorkspace { window } => self
|
||||
.handle_get_window_workspace(window)
|
||||
.wrn("get_window_workspace")?,
|
||||
ClientMessage::GetWindowChildren { window } => self
|
||||
.handle_get_window_children(window)
|
||||
.wrn("get_window_children")?,
|
||||
ClientMessage::GetWindowSplit { window } => self
|
||||
.handle_get_window_split(window)
|
||||
.wrn("get_window_split")?,
|
||||
ClientMessage::SetWindowSplit { window, axis } => self
|
||||
.handle_set_window_split(window, axis)
|
||||
.wrn("set_window_split")?,
|
||||
ClientMessage::GetWindowMono { window } => {
|
||||
self.handle_get_window_mono(window).wrn("get_window_mono")?
|
||||
}
|
||||
ClientMessage::SetWindowMono { window, mono } => self
|
||||
.handle_set_window_mono(window, mono)
|
||||
.wrn("set_window_mono")?,
|
||||
ClientMessage::WindowMove { window, direction } => self
|
||||
.handle_window_move(window, direction)
|
||||
.wrn("window_move")?,
|
||||
ClientMessage::CreateWindowSplit { window, axis } => self
|
||||
.handle_create_window_split(window, axis)
|
||||
.wrn("create_window_split")?,
|
||||
ClientMessage::WindowClose { window } => {
|
||||
self.handle_window_close(window).wrn("close_window")?
|
||||
}
|
||||
ClientMessage::GetWindowFloating { window } => self
|
||||
.handle_get_window_floating(window)
|
||||
.wrn("get_window_floating")?,
|
||||
ClientMessage::SetWindowFloating { window, floating } => self
|
||||
.handle_set_window_floating(window, floating)
|
||||
.wrn("set_window_floating")?,
|
||||
ClientMessage::SetWindowWorkspace { window, workspace } => self
|
||||
.handle_set_window_workspace(window, workspace)
|
||||
.wrn("set_window_workspace")?,
|
||||
ClientMessage::SetWindowFullscreen { window, fullscreen } => self
|
||||
.handle_set_window_fullscreen(window, fullscreen)
|
||||
.wrn("set_window_fullscreen")?,
|
||||
ClientMessage::GetWindowFullscreen { window } => self
|
||||
.handle_get_window_fullscreen(window)
|
||||
.wrn("get_window_fullscreen")?,
|
||||
ClientMessage::GetWindowFloatPinned { window } => self
|
||||
.handle_get_window_float_pinned(window)
|
||||
.wrn("get_window_float_pinned")?,
|
||||
ClientMessage::SetWindowFloatPinned { window, pinned } => self
|
||||
.handle_set_window_float_pinned(window, pinned)
|
||||
.wrn("set_window_float_pinned")?,
|
||||
ClientMessage::GetWindowIsVisible { window } => self
|
||||
.handle_get_window_is_visible(window)
|
||||
.wrn("get_window_is_visible")?,
|
||||
ClientMessage::GetWindowClient { window } => self
|
||||
.handle_get_window_client(window)
|
||||
.wrn("get_window_client")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -2248,6 +2589,10 @@ enum CphError {
|
|||
UnknownTransferFunction(ConfigTransferFunction),
|
||||
#[error("Client {0:?} does not exist")]
|
||||
ClientDoesNotExist(ConfigClient),
|
||||
#[error("Window {0:?} does not exist")]
|
||||
WindowDoesNotExist(Window),
|
||||
#[error("Window {0:?} is not visible")]
|
||||
WindowNotVisible(Window),
|
||||
}
|
||||
|
||||
trait WithRequestName {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue