use crate::async_engine::SpawnedFuture;
use crate::backend;
use crate::backend::BackendColorSpace;
use crate::backend::BackendEotfs;
use crate::backend::ConnectorId;
use crate::backend::DrmDeviceId;
use crate::backend::InputDeviceAccelProfile;
use crate::backend::InputDeviceCapability;
use crate::backend::InputDeviceClickMethod;
use crate::backend::InputDeviceId;
use crate::backend::InputDeviceScrollMethod;
use crate::backend::MonitorInfo;
use crate::backend::transaction::BackendConnectorTransactionError;
use crate::client::CAP_JAY_COMPOSITOR;
use crate::client::Client;
use crate::client::ClientCaps;
use crate::client::ClientId;
use crate::cmm::cmm_eotf::Eotf;
use crate::compositor::MAX_EXTENTS;
use crate::criteria::CritLiteralOrRegex;
use crate::criteria::CritMgrExt;
use crate::criteria::CritTarget;
use crate::criteria::CritUpstreamNode;
use crate::criteria::clm::ClmLeafMatcher;
use crate::criteria::tlm::TlmLeafMatcher;
use crate::criteria::tlm::TlmUpstreamNode;
use crate::env::WAYLAND_DISPLAY;
use crate::evdev::input_event_codes::InputEventCode;
use crate::format::config_formats;
use crate::gfx_api::ScalingFilter;
use crate::ifs::wl_output::BlendSpace;
use crate::ifs::wl_output::PersistentOutputState;
use crate::ifs::wl_seat::SeatId;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wp_content_type_v1::ContentTypeExt;
use crate::io_uring::TaskResultExt;
use crate::kbvm::KbvmError;
use crate::kbvm::KbvmMap;
use crate::output_schedule::map_cursor_hz;
use crate::scale::Scale;
use crate::state::ConnectorData;
use crate::state::DeviceHandlerData;
use crate::state::DrmDevData;
use crate::state::OutputData;
use crate::state::State;
use crate::tagged_acceptor::TaggedAcceptorError;
use crate::theme::ThemeColored;
use crate::theme::ThemeSized;
use crate::theme::ToplevelTheme;
use crate::theme::ToplevelThemeColored;
use crate::theme::ToplevelThemeSized;
use crate::tree::ContainerSplit;
use crate::tree::ContainerTarget;
use crate::tree::NodeBase;
use crate::tree::OutputNode;
use crate::tree::OutputNodeOrPersistent;
use crate::tree::RelativeAxis;
use crate::tree::TearingMode;
use crate::tree::TileState;
use crate::tree::ToplevelData;
use crate::tree::ToplevelIdentifier;
use crate::tree::ToplevelNode;
use crate::tree::ToplevelThemeType;
use crate::tree::TreeTimeline::LiveTL;
use crate::tree::VrrMode;
use crate::tree::WorkspaceNode;
use crate::tree::WorkspaceType;
use crate::tree::WsMoveConfig;
use crate::tree::move_ws_to_output;
use crate::tree::toplevel_create_split;
use crate::tree::toplevel_parent_container;
use crate::tree::toplevel_set_floating;
use crate::tree::toplevel_set_target_mono;
use crate::tree::toplevel_set_workspace;
use crate::tree::toplevel_target_container;
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::markers::JayHash;
use crate::utils::numcell::NumCell;
use crate::utils::reset_immutable::ResetImmutable;
use crate::utils::stack::Stack;
use crate::utils::timer::TimerError;
use crate::utils::timer::TimerFd;
use az::SaturatingCast;
use jay_algorithms::oserror::OsErrorExt;
use jay_config::_private::ClientCriterionIpc;
use jay_config::_private::ClientCriterionStringField;
use jay_config::_private::GenericCriterionIpc;
use jay_config::_private::KeymapBuildParamsV1;
use jay_config::_private::KeymapBuildParamsV1Kind;
use jay_config::_private::PollableId;
use jay_config::_private::WindowCriterionIpc;
use jay_config::_private::WindowCriterionStringField;
use jay_config::_private::WindowThemeKind;
use jay_config::_private::WireMode;
use jay_config::_private::WorkspaceShowOpV1;
use jay_config::_private::WorkspaceShowOpV2;
use jay_config::_private::deserialize_client_message;
use jay_config::_private::ipc::ClientMessage;
use jay_config::_private::ipc::Response;
use jay_config::_private::ipc::ServerMessage;
use jay_config::_private::ipc::WorkspaceSource;
use jay_config::_private::serialize_server_message;
use jay_config::Axis;
use jay_config::Direction;
use jay_config::JcContainerTarget;
use jay_config::JcRelativeAxis;
use jay_config::JcWorkspaceKind;
use jay_config::Workspace;
use jay_config::client::Client as ConfigClient;
use jay_config::client::ClientCapabilities;
use jay_config::client::ClientMatcher;
use jay_config::input::FocusFollowsMouseMode;
use jay_config::input::InputDevice;
use jay_config::input::InputEventCode as ConfigInputEventCode;
use jay_config::input::JcFallbackOutputMode;
use jay_config::input::LayerDirection;
use jay_config::input::Seat;
use jay_config::input::Timeline;
use jay_config::input::acceleration::ACCEL_PROFILE_ADAPTIVE;
use jay_config::input::acceleration::ACCEL_PROFILE_FLAT;
use jay_config::input::acceleration::AccelProfile;
use jay_config::input::capability::CAP_GESTURE;
use jay_config::input::capability::CAP_KEYBOARD;
use jay_config::input::capability::CAP_POINTER;
use jay_config::input::capability::CAP_SWITCH;
use jay_config::input::capability::CAP_TABLET_PAD;
use jay_config::input::capability::CAP_TABLET_TOOL;
use jay_config::input::capability::CAP_TOUCH;
use jay_config::input::capability::Capability;
use jay_config::input::clickmethod::CLICK_METHOD_BUTTON_AREAS;
use jay_config::input::clickmethod::CLICK_METHOD_CLICKFINGER;
use jay_config::input::clickmethod::CLICK_METHOD_NONE;
use jay_config::input::clickmethod::ClickMethod;
use jay_config::input::scrollmethod::SCROLL_METHOD_EDGE;
use jay_config::input::scrollmethod::SCROLL_METHOD_NO_SCROLL;
use jay_config::input::scrollmethod::SCROLL_METHOD_ON_BUTTON_DOWN;
use jay_config::input::scrollmethod::SCROLL_METHOD_TWO_FINGERS;
use jay_config::input::scrollmethod::ScrollMethod;
use jay_config::keyboard::Group;
use jay_config::keyboard::Keymap;
use jay_config::keyboard::mods::Modifiers;
use jay_config::keyboard::syms::KeySym;
use jay_config::logging::LogLevel as ConfigLogLevel;
use jay_config::theme::JcBarPosition;
use jay_config::theme::JcContainerBorders;
use jay_config::theme::colors::Colorable;
use jay_config::theme::sized::Resizable;
use jay_config::timer::Timer as JayTimer;
use jay_config::video::BlendSpace as ConfigBlendSpace;
use jay_config::video::ColorSpace;
use jay_config::video::Connector;
use jay_config::video::DrmDevice;
use jay_config::video::Eotf as ConfigEotf;
use jay_config::video::Format as ConfigFormat;
use jay_config::video::JcGfxApi;
use jay_config::video::ScalingFilter as ConfigScalingFilter;
use jay_config::video::TearingMode as ConfigTearingMode;
use jay_config::video::Transform;
use jay_config::video::VrrMode as ConfigVrrMode;
use jay_config::window::JcTileState;
use jay_config::window::Window;
use jay_config::window::WindowMatcher;
use jay_config::workspace::WorkspaceDisplayOrder;
use jay_config::xwayland::XScalingMode;
use kbvm::GroupIndex;
use kbvm::Keycode;
use libloading::Library;
use log::Level;
use regex::Regex;
use smallvec::SmallVec;
use std::cell::Cell;
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;
use std::rc::Weak;
use std::time::Duration;
use std::time::SystemTime;
use thiserror::Error;
use uapi::OwnedFd;
use uapi::c;
use uapi::fcntl_dupfd_cloexec;
pub(super) struct ConfigProxyHandler {
pub client_data: Cell<*const u8>,
pub dropped: Cell<bool>,
pub _lib: Option<Library>,
pub _version: u32,
pub unref: unsafe extern "C" fn(data: *const u8),
pub handle_msg: unsafe extern "C" fn(data: *const u8, msg: *const u8, size: usize),
pub state: Rc<State>,
pub next_id: NumCell<u64>,
pub keymaps: CopyHashMap<Keymap, Rc<KbvmMap>>,
pub bufs: Stack<Vec<u8>>,
pub workspace_ids: NumCell<u64>,
pub workspaces_by_name: CopyHashMap<String, Rc<ConfigWorkspace>>,
pub workspaces_by_id: CopyHashMap<u64, Rc<ConfigWorkspace>>,
pub timer_ids: NumCell<u64>,
pub timers_by_name: CopyHashMap<Rc<String>, Rc<TimerData>>,
pub timers_by_id: CopyHashMap<u64, Rc<TimerData>>,
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 client_matcher_ids: NumCell<u64>,
pub client_matchers:
CopyHashMap<ClientMatcher, Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>>,
pub client_matcher_cache: CriterionCache<ClientCriterionIpc, Rc<Client>>,
pub client_matcher_leafs: CopyHashMap<ClientMatcher, Rc<ClmLeafMatcher>>,
pub client_matcher_capabilities: CopyHashMap<
ClientMatcher,
(
Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>,
ClientCaps,
),
>,
pub client_matcher_bounding_capabilities: CopyHashMap<
ClientMatcher,
(
Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>,
ClientCaps,
),
>,
pub window_matcher_ids: NumCell<u64>,
pub window_matchers:
CopyHashMap<WindowMatcher, Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>>,
pub window_matcher_cache: CriterionCache<WindowCriterionIpc, ToplevelData>,
pub window_matcher_leafs: CopyHashMap<WindowMatcher, Rc<TlmLeafMatcher>>,
pub window_matcher_std_kinds: Rc<TlmUpstreamNode>,
pub window_matcher_properties: CopyHashMap<WindowMatcher, Rc<WindowMatcherProperties>>,
}
pub struct WindowMatcherProperties {
criterion: Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>,
auto_focus: Cell<Option<bool>>,
initial_tile_state: Cell<Option<TileState>>,
initial_floating_size: Cell<Option<(i32, i32)>>,
initial_floating_position: Cell<Option<(i32, i32)>>,
}
pub struct ConfigWorkspace {
id: u64,
name: String,
ty: Cell<WorkspaceType>,
initial_connector: Cell<Option<Connector>>,
}
pub struct Pollable {
write_trigger: Rc<AsyncEvent>,
_write_future: SpawnedFuture<()>,
read_trigger: Rc<AsyncEvent>,
_read_future: SpawnedFuture<()>,
}
pub(super) struct TimerData {
timer: TimerFd,
id: u64,
name: Rc<String>,
_handler: SpawnedFuture<()>,
}
pub type CriterionCache<K, T> = Rc<CopyHashMap<K, Weak<CachedCriterion<K, T>>>>;
pub struct CachedCriterion<K, T>
where
K: JayHash + Eq,
T: CritTarget,
{
crit: K,
cache: CriterionCache<K, T>,
upstream: Vec<Rc<CachedCriterion<K, T>>>,
node: Rc<dyn CritUpstreamNode<T>>,
}
impl<K, T> Drop for CachedCriterion<K, T>
where
K: JayHash + Eq,
T: CritTarget,
{
fn drop(&mut self) {
self.cache.remove(&self.crit);
}
}
impl<K, T> CachedCriterion<K, T>
where
K: JayHash + Eq,
T: CritTarget,
{
fn any(&self, v: &impl Fn(&K) -> bool) -> bool {
v(&self.crit) || self.upstream.iter().any(|u| u.any(v))
}
}
impl ConfigProxyHandler {
pub fn do_drop(&self) {
self.dropped.set(true);
self.timers_by_name.clear();
self.timers_by_id.clear();
self.pollables.clear();
self.client_matcher_leafs.clear();
self.client_matchers.clear();
self.window_matcher_leafs.clear();
self.window_matchers.clear();
}
pub fn send(&self, msg: &ServerMessage) {
let mut buf = self.bufs.pop().unwrap_or_default();
buf.clear();
serialize_server_message(&mut buf, msg);
unsafe {
(self.handle_msg)(self.client_data.get(), buf.as_ptr(), buf.len());
}
self.bufs.push(buf);
}
fn respond(&self, msg: Response) {
self.send(&ServerMessage::Response { response: msg });
}
fn id(&self) -> u64 {
self.next_id.fetch_add(1)
}
fn get_workspace_by_name(&self, name: &String, ty: WorkspaceType) -> Workspace {
let id = match self.workspaces_by_name.get(name) {
None => {
let id = self.workspace_ids.fetch_add(1);
let ws = Rc::new(ConfigWorkspace {
id,
name: name.to_string(),
ty: Cell::new(ty),
initial_connector: Default::default(),
});
self.workspaces_by_name.set(name.clone(), ws.clone());
self.workspaces_by_id.set(id, ws);
id
}
Some(ws) => {
ws.ty.set(ty);
ws.id
}
};
Workspace(id)
}
fn handle_log_request(
&self,
level: ConfigLogLevel,
msg: &str,
file: Option<&str>,
line: Option<u32>,
) {
let level = match level {
ConfigLogLevel::Error => Level::Error,
ConfigLogLevel::Warn => Level::Warn,
ConfigLogLevel::Info => Level::Info,
ConfigLogLevel::Debug => Level::Debug,
ConfigLogLevel::Trace => Level::Trace,
};
let debug = fmt::from_fn(|fmt| {
if let Some(file) = file {
write!(fmt, "{file}")?;
if let Some(line) = line {
write!(fmt, ":{line}")?;
}
write!(fmt, ": ")?;
}
write!(fmt, "{msg}")?;
Ok(())
});
log::log!(level, "{debug:?}");
}
fn handle_get_seat(&self, name: &str) {
for seat in self.state.globals.seats.lock().values() {
if seat.seat_name() == name {
self.respond(Response::GetSeat {
seat: Seat(seat.id().raw() as _),
});
return;
}
}
let seat = self.state.create_seat(name);
self.respond(Response::GetSeat {
seat: Seat(seat.id().raw() as _),
});
}
fn handle_parse_keymap(&self, keymap: &str) -> Result<(), CphError> {
let (keymap, res) = match self.state.kb_ctx.parse_keymap(keymap.as_bytes(), None) {
Ok(keymap) => {
let id = Keymap(self.id());
self.keymaps.set(id, keymap);
(id, Ok(()))
}
Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))),
};
self.respond(Response::ParseKeymap { keymap });
res
}
fn handle_keymap_from_names(
&self,
rules: Option<&str>,
model: Option<&str>,
groups: Option<Vec<Group<'_>>>,
options: Option<Vec<&str>>,
) -> Result<(), CphError> {
let kbvm_groups = groups.map(|groups| {
groups
.iter()
.map(|g| kbvm::xkb::rmlvo::Group {
layout: g.layout,
variant: g.variant,
})
.collect::<Vec<_>>()
});
let (keymap, res) = match self.state.kb_ctx.keymap_from_names(
rules,
model,
kbvm_groups.as_deref(),
options.as_deref(),
None,
) {
Ok(keymap) => {
let id = Keymap(self.id());
self.keymaps.set(id, keymap);
(id, Ok(()))
}
Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))),
};
self.respond(Response::KeymapFromNames { keymap });
res
}
fn handle_get_connectors(
&self,
dev: Option<DrmDevice>,
connected_only: bool,
) -> Result<(), CphError> {
let datas: Vec<_>;
if let Some(dev) = dev {
let dev = self.get_drm_device(dev)?;
datas = dev.connectors.lock().values().cloned().collect();
} else {
datas = self.state.connectors.lock().values().cloned().collect();
}
let connectors = datas
.iter()
.flat_map(|d| match (connected_only, d.connected.get()) {
(false, _) | (true, true) => Some(Connector(d.connector.id().raw() as _)),
_ => None,
})
.collect();
self.respond(Response::GetConnectors { connectors });
Ok(())
}
fn handle_get_drm_device_syspath(&self, dev: DrmDevice) -> Result<(), CphError> {
let dev = self.get_drm_device(dev)?;
let syspath = dev.syspath.clone().unwrap_or_default();
self.respond(Response::GetDrmDeviceSyspath { syspath });
Ok(())
}
fn handle_get_drm_device_devnode(&self, dev: DrmDevice) -> Result<(), CphError> {
let dev = self.get_drm_device(dev)?;
let devnode = dev.devnode.clone().unwrap_or_default();
self.respond(Response::GetDrmDeviceDevnode { devnode });
Ok(())
}
fn handle_get_drm_device_vendor(&self, dev: DrmDevice) -> Result<(), CphError> {
let dev = self.get_drm_device(dev)?;
let vendor = dev.vendor.clone().unwrap_or_default();
self.respond(Response::GetDrmDeviceVendor { vendor });
Ok(())
}
fn handle_get_drm_devices(&self) {
let devs = self.state.drm_devs.lock();
let mut res = vec![];
for dev in devs.values() {
res.push(DrmDevice(dev.dev.id().raw() as _));
}
self.respond(Response::GetDrmDevices { devices: res });
}
fn handle_make_render_device(&self, dev: DrmDevice) -> Result<(), CphError> {
let dev = self.get_drm_device(dev)?;
dev.make_render_device();
Ok(())
}
fn handle_get_drm_device_model(&self, dev: DrmDevice) -> Result<(), CphError> {
let dev = self.get_drm_device(dev)?;
let model = dev.model.clone().unwrap_or_default();
self.respond(Response::GetDrmDeviceModel { model });
Ok(())
}
fn handle_get_drm_device_pci_id(&self, dev: DrmDevice) -> Result<(), CphError> {
let dev = self.get_drm_device(dev)?;
let pci_id = dev.pci_id.unwrap_or_default();
self.respond(Response::GetDrmDevicePciId { pci_id });
Ok(())
}
fn handle_reload(&self) {
self.state.reload_config();
}
fn handle_get_seat_fullscreen(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
self.respond(Response::GetFullscreen {
fullscreen: seat.get_fullscreen(),
});
Ok(())
}
fn handle_set_seat_fullscreen(&self, seat: Seat, fullscreen: bool) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_fullscreen(fullscreen);
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[LiveTL].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, None);
Ok(())
}
fn handle_set_keymap(&self, seat: Seat, keymap: Keymap) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
let keymap = if keymap.is_invalid() {
self.state.default_keymap.clone()
} else {
self.get_keymap(keymap)?
};
seat.set_seat_keymap(&keymap);
Ok(())
}
fn handle_set_device_keymap(
&self,
device: InputDevice,
keymap: Keymap,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
let map = if keymap.is_invalid() {
None
} else {
Some(self.get_keymap(keymap)?)
};
dev.set_keymap(&self.state, map);
Ok(())
}
fn handle_set_forward(&self, seat: Seat, forward: bool) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_forward(forward);
Ok(())
}
fn handle_set_focus_follows_mouse_mode(
&self,
seat: Seat,
mode: FocusFollowsMouseMode,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
let focus_follows_mouse = match mode {
FocusFollowsMouseMode::True => true,
FocusFollowsMouseMode::False => false,
};
seat.set_focus_follows_mouse(focus_follows_mouse);
Ok(())
}
fn handle_set_fallback_output_mode(
&self,
seat: Seat,
mode: JcFallbackOutputMode,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_fallback_output_mode(mode.into());
Ok(())
}
fn handle_set_window_management_enabled(
&self,
seat: Seat,
enabled: bool,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_window_management_enabled(enabled);
Ok(())
}
fn handle_set_input_device_connector(
&self,
input_device: InputDevice,
connector: Connector,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(input_device)?;
let output = self.get_output_node(connector)?;
dev.set_output(&self.state, Some(&output.global));
Ok(())
}
fn handle_remove_input_mapping(&self, input_device: InputDevice) -> Result<(), CphError> {
let dev = self.get_device_handler_data(input_device)?;
dev.set_output(&self.state, None);
Ok(())
}
fn handle_set_status(&self, status: &str) {
self.state.set_status(status);
}
fn get_timer(&self, timer: JayTimer) -> Result<Rc<TimerData>, CphError> {
match self.timers_by_id.get(&timer.0) {
Some(t) => Ok(t),
_ => Err(CphError::TimerDoesNotExist(timer)),
}
}
fn handle_remove_timer(&self, timer: JayTimer) -> Result<(), CphError> {
let timer = self.get_timer(timer)?;
self.timers_by_id.remove(&timer.id);
self.timers_by_name.remove(&timer.name);
Ok(())
}
fn handle_set_env(&self, key: &str, val: &str) {
if let Some(f) = self.state.forker.get() {
f.setenv(key.as_bytes(), val.as_bytes());
}
}
fn handle_unset_env(&self, key: &str) {
if let Some(f) = self.state.forker.get() {
f.unsetenv(key.as_bytes());
}
}
fn handle_get_config_dir(&self) {
let dir = self.state.config_dir.unwrap_or_default();
self.respond(Response::GetConfigDir {
dir: dir.to_string(),
});
}
fn handle_get_workspaces(&self) {
let mut workspaces = vec![];
for ws in self.state.workspaces.lock().values() {
workspaces.push(self.get_workspace_by_name(&ws.name, ws.ty));
}
self.respond(Response::GetWorkspaces { workspaces });
}
fn handle_program_timer(
&self,
timer: JayTimer,
initial: Option<Duration>,
periodic: Option<Duration>,
) -> Result<(), CphError> {
let timer = self.get_timer(timer)?;
timer.timer.program(initial, periodic)?;
Ok(())
}
fn handle_get_timer(self: &Rc<Self>, name: &str) -> Result<(), CphError> {
let name = Rc::new(name.to_owned());
if let Some(t) = self.timers_by_name.get(&name) {
self.respond(Response::GetTimer {
timer: JayTimer(t.id),
});
return Ok(());
}
let id = self.timer_ids.fetch_add(1);
let timer = TimerFd::new(c::CLOCK_BOOTTIME)?;
let handler = {
let timer = timer.clone();
let slf = self.clone();
self.state.eng.spawn("config timer", async move {
loop {
match timer.expired(&slf.state.ring).await {
Ok(_) => slf.send(&ServerMessage::TimerExpired {
timer: JayTimer(id),
}),
Err(e) => {
log::error!("Could not wait for timer expiration: {}", ErrorFmt(e));
if let Some(timer) = slf.timers_by_id.remove(&id) {
slf.timers_by_name.remove(&timer.name);
}
return;
}
}
}
})
};
let td = Rc::new(TimerData {
timer,
id,
name: name.clone(),
_handler: handler,
});
self.timers_by_name.set(name.clone(), td.clone());
self.timers_by_id.set(id, td.clone());
self.respond(Response::GetTimer {
timer: JayTimer(id),
});
Ok(())
}
fn handle_seat_close(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.close();
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());
Ok(())
}
fn handle_seat_move(&self, seat: Seat, direction: Direction) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.move_focused(direction.into());
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();
self.respond(Response::GetRepeatRate { rate, delay });
Ok(())
}
fn handle_set_repeat_rate(&self, seat: Seat, rate: i32, delay: i32) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
if rate < 0 {
return Err(CphError::NegativeRepeatRate);
}
if delay < 0 {
return Err(CphError::NegativeRepeatDelay);
}
seat.set_rate(rate, delay);
Ok(())
}
fn get_workspace(&self, ws: Workspace) -> Result<Rc<ConfigWorkspace>, CphError> {
match self.workspaces_by_id.get(&ws.0) {
Some(ws) => Ok(ws),
_ => Err(CphError::WorkspaceDoesNotExist(ws)),
}
}
fn get_existing_workspace(&self, ws: Workspace) -> Result<Option<Rc<WorkspaceNode>>, CphError> {
self.get_workspace(ws)
.map(|ws| self.state.workspaces.get(&ws.name))
}
fn get_device_handler_data(
&self,
device: InputDevice,
) -> Result<Rc<DeviceHandlerData>, CphError> {
let data = self
.state
.input_device_handlers
.borrow_mut()
.get(&InputDeviceId::from_raw(device.0 as _))
.map(|d| d.data.clone());
match data {
Some(d) => Ok(d),
_ => Err(CphError::DeviceDoesNotExist(device)),
}
}
fn get_connector(&self, connector: Connector) -> Result<Rc<ConnectorData>, CphError> {
let data = self
.state
.connectors
.get(&ConnectorId::from_raw(connector.0 as _));
match data {
Some(d) => Ok(d),
_ => Err(CphError::ConnectorDoesNotExist(connector)),
}
}
fn get_output(&self, connector: Connector) -> Result<Rc<OutputData>, CphError> {
let data = self
.state
.outputs
.get(&ConnectorId::from_raw(connector.0 as _));
match data {
Some(d) => Ok(d),
_ => Err(CphError::OutputDoesNotExist(connector)),
}
}
fn get_output_node(&self, connector: Connector) -> Result<Rc<OutputNode>, CphError> {
let data = self.get_output(connector)?;
match data.node.clone() {
Some(d) => Ok(d),
_ => Err(CphError::OutputIsNotDesktop(connector)),
}
}
fn get_monitor_info(&self, connector: Connector) -> Result<Rc<MonitorInfo>, CphError> {
let data = self.get_output(connector)?;
Ok(data.monitor_info.clone())
}
fn get_monitor_persistent(
&self,
connector: Connector,
) -> Result<Rc<PersistentOutputState>, CphError> {
let data = self.get_output(connector)?;
let pos = self
.state
.ensure_persistent_output_state(&data.monitor_info.output_id);
Ok(pos)
}
fn get_output_node_or_persistent(
&self,
connector: Connector,
) -> Result<OutputNodeOrPersistent, CphError> {
let data = self.get_output(connector)?;
if let Some(node) = &data.node {
return Ok(OutputNodeOrPersistent::Node(node.clone()));
}
let pos = self
.state
.ensure_persistent_output_state(&data.monitor_info.output_id);
Ok(OutputNodeOrPersistent::Persistent(pos))
}
fn get_drm_device(&self, dev: DrmDevice) -> Result<Rc<DrmDevData>, CphError> {
match self.state.drm_devs.get(&DrmDeviceId::from_raw(dev.0 as _)) {
Some(dev) => Ok(dev),
_ => Err(CphError::DrmDeviceDoesNotExist(dev)),
}
}
fn get_seat(&self, seat: Seat) -> Result<Rc<WlSeatGlobal>, CphError> {
let seats = self.state.globals.seats.lock();
for seat_global in seats.values() {
if seat_global.id().raw() == seat.0 as u32 {
return Ok(seat_global.clone());
}
}
Err(CphError::SeatDoesNotExist(seat))
}
fn get_kb(&self, kb: InputDevice) -> Result<Rc<dyn backend::InputDevice>, CphError> {
let kbs = self.state.input_device_handlers.borrow_mut();
match kbs.get(&(InputDeviceId::from_raw(kb.0 as _))) {
None => Err(CphError::KeyboardDoesNotExist(kb)),
Some(kb) => Ok(kb.data.device.clone()),
}
}
fn get_keymap(&self, keymap: Keymap) -> Result<Rc<KbvmMap>, CphError> {
match self.keymaps.get(&keymap) {
Some(k) => Ok(k),
None => Err(CphError::KeymapDoesNotExist(keymap)),
}
}
fn handle_set_seat(&self, device: InputDevice, seat: Seat) -> Result<(), CphError> {
let seat = if seat.is_invalid() {
None
} else {
Some(self.get_seat(seat)?)
};
let dev = self.get_device_handler_data(device)?;
dev.set_seat(&self.state, seat);
Ok(())
}
fn handle_set_left_handed(
&self,
device: InputDevice,
left_handed: bool,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_left_handed(&self.state, left_handed);
Ok(())
}
fn handle_set_accel_profile(
&self,
device: InputDevice,
accel_profile: AccelProfile,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
let profile = match accel_profile {
ACCEL_PROFILE_FLAT => InputDeviceAccelProfile::Flat,
ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
_ => return Err(CphError::UnknownAccelProfile(accel_profile)),
};
dev.set_accel_profile(&self.state, profile);
Ok(())
}
fn handle_set_accel_speed(&self, device: InputDevice, speed: f64) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_accel_speed(&self.state, speed);
Ok(())
}
fn handle_set_px_per_wheel_scroll(&self, device: InputDevice, px: f64) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_px_per_scroll_wheel(&self.state, px);
Ok(())
}
fn handle_set_px_scroll_multiplier(
&self,
device: InputDevice,
mul: f64,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_px_scroll_multiplier(&self.state, mul);
Ok(())
}
fn handle_set_tap_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_tap_enabled(&self.state, enabled);
Ok(())
}
fn handle_set_drag_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_drag_enabled(&self.state, enabled);
Ok(())
}
fn handle_set_natural_scrolling_enabled(
&self,
device: InputDevice,
enabled: bool,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_natural_scrolling_enabled(&self.state, enabled);
Ok(())
}
fn handle_set_drag_lock_enabled(
&self,
device: InputDevice,
enabled: bool,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_drag_lock_enabled(&self.state, enabled);
Ok(())
}
fn handle_set_transform_matrix(
&self,
device: InputDevice,
matrix: [[f64; 2]; 2],
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_transform_matrix(&self.state, matrix);
Ok(())
}
fn handle_set_calibration_matrix(
&self,
device: InputDevice,
matrix: [[f32; 3]; 2],
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_calibration_matrix(&self.state, matrix);
Ok(())
}
fn handle_set_click_method(
&self,
device: InputDevice,
click_method: ClickMethod,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
let method = match click_method {
CLICK_METHOD_NONE => InputDeviceClickMethod::None,
CLICK_METHOD_BUTTON_AREAS => InputDeviceClickMethod::ButtonAreas,
CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
_ => return Err(CphError::UnknownClickMethod(click_method)),
};
dev.set_click_method(&self.state, method);
Ok(())
}
fn handle_set_scroll_method(
&self,
device: InputDevice,
scroll_method: ScrollMethod,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
let method = match scroll_method {
SCROLL_METHOD_NO_SCROLL => InputDeviceScrollMethod::NoScroll,
SCROLL_METHOD_TWO_FINGERS => InputDeviceScrollMethod::TwoFingers,
SCROLL_METHOD_EDGE => InputDeviceScrollMethod::Edge,
SCROLL_METHOD_ON_BUTTON_DOWN => InputDeviceScrollMethod::OnButtonDown,
_ => return Err(CphError::UnknownScrollMethod(scroll_method)),
};
dev.set_scroll_method(&self.state, method);
Ok(())
}
fn handle_set_scroll_button(
&self,
device: InputDevice,
button: ConfigInputEventCode,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
let button = if button.0 == 0 {
None
} else if let Some(button) = InputEventCode::from_raw(button.0) {
Some(button)
} else {
return Err(CphError::UnknownScrollButton(button));
};
dev.set_scroll_button(&self.state, button);
Ok(())
}
fn handle_set_scroll_button_lock(
&self,
device: InputDevice,
enabled: bool,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_scroll_button_lock(&self.state, enabled);
Ok(())
}
fn handle_set_middle_button_emulation_enabled(
&self,
device: InputDevice,
enabled: bool,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
dev.set_middle_button_emulation_enabled(&self.state, enabled);
Ok(())
}
fn handle_set_ei_socket_enabled(&self, enabled: bool) {
self.state.set_ei_socket_enabled(enabled);
}
fn handle_get_workspace(&self, name: &str) {
self.respond(Response::GetWorkspace {
workspace: self.get_workspace_by_name(&name.to_owned(), WorkspaceType::Normal),
});
}
fn handle_get_overlay(&self, name: &str) {
self.respond(Response::GetWorkspace {
workspace: self.get_workspace_by_name(&name.to_owned(), WorkspaceType::Overlay),
});
}
fn handle_hide_overlays(&self) {
let outputs: SmallVec<[Rc<OutputNode>; 8]> = self
.state
.root
.outputs
.lock()
.values()
.filter(|o| o.node_state[LiveTL].overlay.is_some())
.cloned()
.collect();
for output in outputs {
output.hide_overlay();
}
}
fn handle_hide_workspace(&self, workspace: Workspace) -> Result<(), CphError> {
let Some(ws) = self.get_existing_workspace(workspace)? else {
return Ok(());
};
let wns = &ws.node_state[LiveTL];
if ws.ty == WorkspaceType::Overlay && wns.output.id() != self.state.dummy_output_id {
wns.output.get().hide_overlay();
}
Ok(())
}
fn handle_get_workspace_kind(&self, workspace: Workspace) -> Result<(), CphError> {
let ws = self.get_workspace(workspace)?;
let kind = self
.state
.workspaces
.get(&ws.name)
.map(|ws| ws.ty)
.unwrap_or(ws.ty.get());
let kind = match kind {
WorkspaceType::Normal => JcWorkspaceKind::Normal,
WorkspaceType::Overlay => JcWorkspaceKind::Overlay,
};
self.respond(Response::GetWorkspaceKind { kind });
Ok(())
}
fn handle_get_workspace_capture(&self, workspace: Workspace) -> Result<(), CphError> {
let ws = self.get_existing_workspace(workspace)?;
let capture = match ws {
Some(ws) => ws.may_capture.get(),
None => self.state.default_workspace_capture.get(),
};
self.respond(Response::GetWorkspaceCapture { capture });
Ok(())
}
fn handle_set_workspace_capture(
&self,
workspace: Workspace,
capture: bool,
) -> Result<(), CphError> {
if let Some(ws) = self.get_existing_workspace(workspace)? {
ws.may_capture.set(capture);
ws.update_has_captures();
}
Ok(())
}
fn handle_set_gfx_api(&self, device: Option<DrmDevice>, api: JcGfxApi) -> Result<(), CphError> {
let api = api.into();
match device {
Some(dev) => self.get_drm_device(dev)?.dev.set_gfx_api(api),
_ => self.state.default_gfx_api.set(api),
}
Ok(())
}
fn handle_set_flip_margin(&self, device: DrmDevice, margin: Duration) -> Result<(), CphError> {
self.get_drm_device(device)?.set_flip_margin(
&self.state,
margin.as_nanos().try_into().unwrap_or(u64::MAX),
);
Ok(())
}
fn handle_set_x_scaling_mode(&self, mode: XScalingMode) -> Result<(), CphError> {
let use_wire_scale = match mode {
XScalingMode::DEFAULT => false,
XScalingMode::DOWNSCALED => true,
_ => return Err(CphError::UnknownXScalingMode(mode)),
};
self.state.set_xwayland_use_wire_scale(use_wire_scale);
Ok(())
}
fn handle_set_x_wayland_enabled(&self, enabled: bool) {
self.state.set_xwayland_enabled(enabled);
}
fn handle_set_ui_drag_enabled(&self, enabled: bool) {
self.state.set_ui_drag_enabled(enabled);
}
fn handle_set_ui_drag_threshold(&self, threshold: i32) {
self.state.set_ui_drag_threshold(threshold.max(1));
}
fn handle_set_direct_scanout_enabled(
&self,
device: Option<DrmDevice>,
enabled: bool,
) -> Result<(), CphError> {
match device {
Some(dev) => self
.get_drm_device(dev)?
.set_direct_scanout_enabled(&self.state, enabled),
_ => self.state.direct_scanout_enabled.set(enabled),
}
Ok(())
}
fn handle_set_plane_color_pipelines_enabled(
&self,
dev: DrmDevice,
enabled: bool,
) -> Result<(), CphError> {
self.get_drm_device(dev)?
.set_use_plane_color_pipelines(&self.state, enabled);
Ok(())
}
fn handle_get_plane_color_pipelines_enabled(&self, dev: DrmDevice) -> Result<(), CphError> {
self.respond(Response::GetPlaneColorPipelinesEnabled {
enabled: self.get_drm_device(dev)?.dev.use_plane_color_pipelines(),
});
Ok(())
}
fn handle_get_default_workspace_capture(&self) {
self.respond(Response::GetDefaultWorkspaceCapture {
capture: self.state.default_workspace_capture.get(),
});
}
fn handle_set_default_workspace_capture(&self, capture: bool) {
self.state.default_workspace_capture.set(capture);
}
fn handle_set_double_click_interval_usec(&self, usec: u64) {
self.state.double_click_interval_usec.set(usec);
}
fn handle_set_double_click_distance(&self, dist: i32) {
self.state.double_click_distance.set(dist);
}
fn handle_get_seat_cursor_workspace(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
let output = seat.get_cursor_output();
let mut workspace = Workspace(0);
if !output.is_dummy
&& let Some(ws) = output.workspace()
{
workspace = self.get_workspace_by_name(&ws.name, ws.ty);
}
self.respond(Response::GetSeatCursorWorkspace { workspace });
Ok(())
}
fn handle_get_seat_keyboard_workspace(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
let mut workspace = Workspace(0);
if let Some(ws) = seat.get_keyboard_workspace() {
workspace = self.get_workspace_by_name(&ws.name, ws.ty);
}
self.respond(Response::GetSeatKeyboardWorkspace { workspace });
Ok(())
}
fn handle_show_workspace(
&self,
seat: Seat,
ws: Workspace,
output: Option<Connector>,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
let ws = self.get_workspace(ws)?;
let mut output = output.map(|o| self.get_output_node(o)).transpose()?;
if output.is_none() {
output = ws
.initial_connector
.get()
.and_then(|o| self.get_output_node(o).ok());
}
self.state
.show_workspace(&seat, &ws.name, ws.ty.get(), output);
Ok(())
}
fn handle_show_workspace_3(&self, v1: WorkspaceShowOpV1) -> Result<(), CphError> {
self.handle_show_workspace_4(v1, Default::default())
}
fn handle_show_workspace_4(
&self,
v1: WorkspaceShowOpV1,
v2: WorkspaceShowOpV2,
) -> Result<(), CphError> {
let WorkspaceShowOpV1 {
workspace,
connector,
mut move_to_connector,
seat,
fallback_output_mode,
focus,
} = v1;
let WorkspaceShowOpV2 { toggle } = v2;
let toggle = toggle.unwrap_or(false);
struct Params {
move_: bool,
ws: Rc<WorkspaceNode>,
output: Rc<OutputNode>,
seat: Option<Rc<WlSeatGlobal>>,
}
let Params {
move_,
ws,
output,
seat,
} = 'params: {
let ws = self.get_workspace(workspace)?;
let get_output = || {
if let Some(c) = connector {
return self.get_output_node(c).map(Some);
}
if let Some(c) = ws.initial_connector.get()
&& let Ok(o) = self.get_output_node(c)
{
return Ok(Some(o));
}
Ok(None)
};
let mut seat_opt = None::<Option<_>>;
let get_seat = |seat_opt: &mut Option<Option<_>>| -> Result<_, CphError> {
Ok(match seat_opt {
Some(s) => s.clone(),
_ => seat_opt
.insert(seat.map(|s| self.get_seat(s)).transpose()?)
.clone(),
})
};
let get_output = |seat_opt| -> Result<_, CphError> {
let o = if let Some(o) = get_output()? {
o
} else if let Some(s) = get_seat(seat_opt)? {
let fom = fallback_output_mode.map(Into::into);
s.get_fallback_output2(fom)
} else {
return Ok(None);
};
Ok(Some(o))
};
let mut seat = None;
if focus {
seat = get_seat(&mut seat_opt)?;
}
if let Some(ws) = self.state.workspaces.get(&ws.name) {
let wns = &ws.node_state[LiveTL];
if ws.ty == WorkspaceType::Overlay {
if wns.output.id() == self.state.dummy_output_id {
move_to_connector = true;
} else if toggle {
wns.output.get().hide_overlay();
return Ok(());
}
}
let mut output = wns.output.get();
if move_to_connector && let Some(o) = get_output(&mut seat_opt)? {
output = o;
}
if output.id == self.state.dummy_output_id {
log::warn!("Could not determine output to show workspace on");
return Ok(());
}
break 'params Params {
move_: move_to_connector && wns.output.id() != output.id,
ws,
output,
seat,
};
}
let Some(output) = get_output(&mut seat_opt)? else {
log::warn!("Workspace does not exist and no target output could be determined");
return Ok(());
};
Params {
move_: false,
ws: match ws.ty.get() {
WorkspaceType::Normal => output.create_normal_workspace(&ws.name),
WorkspaceType::Overlay => self.state.create_overlay_workspace(&ws.name),
},
output,
seat,
}
};
let mut did_focus = false;
if move_ {
move_ws_to_output(
&ws,
&output,
WsMoveConfig {
make_visible_always: true,
make_visible_if_empty: true,
source_is_destroyed: false,
before: None,
},
);
if let Some(seat) = &seat {
did_focus = ws.do_focus(seat, crate::tree::Direction::Unspecified);
}
if !output.is_dummy {
ws.desired_output.set(output.global.output_id.clone());
}
self.state.tree_changed();
} else {
did_focus = self.state.show_workspace2(seat.as_ref(), &output, &ws);
}
if (did_focus || ws.ty == WorkspaceType::Normal)
&& let Some(seat) = &seat
{
seat.maybe_schedule_warp_mouse_to_focus();
}
Ok(())
}
fn handle_set_seat_workspace(&self, seat: Seat, ws: Workspace) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
let ws = self.get_workspace(ws)?;
let workspace = match self.state.workspaces.get(&ws.name) {
Some(ws) => ws,
_ => match ws.ty.get() {
WorkspaceType::Normal => {
let output = if let Some(connector) = ws.initial_connector.get()
&& let Ok(output) = self.get_output_node(connector)
{
output
} else {
seat.get_fallback_output()
};
output.create_normal_workspace(&ws.name)
}
WorkspaceType::Overlay => self.state.create_overlay_workspace(&ws.name),
},
};
seat.set_workspace(&workspace);
Ok(())
}
fn handle_set_window_workspace(&self, window: Window, ws: Workspace) -> Result<(), CphError> {
let window = self.get_window(window)?;
let ws = self.get_workspace(ws)?;
let workspace = match self.state.workspaces.get(&ws.name) {
Some(ws) => ws,
_ => match ws.ty.get() {
WorkspaceType::Normal => {
let o = if let Some(connector) = ws.initial_connector.get()
&& let Ok(output) = self.get_output_node(connector)
{
output
} else if let Some(output) = window.node_output() {
output
} else {
return Ok(());
};
o.create_normal_workspace(&ws.name)
}
WorkspaceType::Overlay => self.state.create_overlay_workspace(&ws.name),
},
};
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();
self.respond(Response::GetDeviceName {
name: name.to_string(),
});
Ok(())
}
fn handle_get_input_device_syspath(&self, device: InputDevice) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
self.respond(Response::GetInputDeviceSyspath {
syspath: dev.syspath.clone().unwrap_or_default(),
});
Ok(())
}
fn handle_get_input_device_devnode(&self, device: InputDevice) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
self.respond(Response::GetInputDeviceDevnode {
devnode: dev.devnode.clone().unwrap_or_default(),
});
Ok(())
}
fn handle_move_to_output(
&self,
workspace: WorkspaceSource,
connector: Connector,
) -> Result<(), CphError> {
let output = self.get_output_node(connector)?;
let ws = match workspace {
WorkspaceSource::Explicit(ws) => match self.get_existing_workspace(ws)? {
Some(ws) => ws,
_ => return Ok(()),
},
WorkspaceSource::Seat(s) => match self.get_seat(s)?.get_fallback_workspace() {
Some(ws) => ws,
_ => return Ok(()),
},
};
self.state.move_ws_to_output(&ws, &output);
Ok(())
}
fn handle_set_idle(&self, timeout: Duration) {
self.state.idle.set_timeout(&self.state, timeout);
}
fn handle_set_idle_grace_period(&self, period: Duration) {
self.state.idle.set_grace_period(&self.state, period);
}
fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
self.state.set_explicit_sync_enabled(enabled);
}
fn handle_set_color_management_enabled(&self, enabled: bool) {
self.state.set_color_management_enabled(enabled);
}
fn handle_set_session_management_enabled(&self, enabled: bool) {
self.state.set_session_management_enabled(enabled);
}
fn handle_get_socket_path(&self) {
match self.state.acceptor.get() {
Some(a) => {
self.respond(Response::GetSocketPath {
path: a.socket_name().to_string(),
});
}
_ => {
log::warn!("There is no acceptor");
}
}
}
fn handle_connector_connected(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_connector(connector)?;
self.respond(Response::ConnectorConnected {
connected: connector.connected.get(),
});
Ok(())
}
fn handle_connector_type(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_connector(connector)?;
self.respond(Response::ConnectorType {
ty: connector.connector.kernel_id().ty.to_config(),
});
Ok(())
}
fn handle_connector_mode(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output_node(connector)?;
let mode = connector.global.mode.get();
self.respond(Response::ConnectorMode {
width: mode.width,
height: mode.height,
refresh_millihz: mode.refresh_rate_millihz,
});
Ok(())
}
fn handle_connector_set_mode(
&self,
connector: Connector,
mode: WireMode,
) -> Result<(), CphError> {
let connector = self.get_connector(connector)?;
connector
.modify_state(&self.state, |s| {
s.mode = backend::Mode {
width: mode.width,
height: mode.height,
refresh_rate_millihz: mode.refresh_millihz,
};
})
.map_err(CphError::ModifyConnectorState)?;
Ok(())
}
fn handle_connector_modes(&self, connector: Connector) -> Result<(), CphError> {
let info = self.get_monitor_info(connector)?;
self.respond(Response::ConnectorModes {
modes: info
.modes
.iter()
.flatten()
.map(|m| WireMode {
width: m.width,
height: m.height,
refresh_millihz: m.refresh_rate_millihz,
})
.collect(),
});
Ok(())
}
fn handle_connector_supports_arbitrary_modes(
&self,
connector: Connector,
) -> Result<(), CphError> {
let info = self.get_monitor_info(connector)?;
self.respond(Response::ConnectorSupportsArbitraryModes {
supports_arbitrary_modes: info.modes.is_none(),
});
Ok(())
}
fn handle_connector_name(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_connector(connector)?;
self.respond(Response::GetConnectorName {
name: connector.name.deref().clone(),
});
Ok(())
}
fn handle_connector_model(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output(connector)?;
self.respond(Response::GetConnectorModel {
model: connector.monitor_info.output_id.model.clone(),
});
Ok(())
}
fn handle_connector_manufacturer(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output(connector)?;
self.respond(Response::GetConnectorManufacturer {
manufacturer: connector.monitor_info.output_id.manufacturer.clone(),
});
Ok(())
}
fn handle_connector_serial_number(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output(connector)?;
self.respond(Response::GetConnectorSerialNumber {
serial_number: connector.monitor_info.output_id.serial_number.clone(),
});
Ok(())
}
fn handle_set_cursor_size(&self, seat: Seat, size: i32) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
if size < 0 {
return Err(CphError::NegativeCursorSize);
}
seat.cursor_group().set_cursor_size(size as _);
Ok(())
}
fn handle_disable_pointer_constraint(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.disable_pointer_constraint();
Ok(())
}
fn handle_set_use_hardware_cursor(
&self,
seat: Seat,
use_hardware_cursor: bool,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.cursor_group().set_hardware_cursor(use_hardware_cursor);
self.state.refresh_hardware_cursors();
Ok(())
}
fn handle_connector_size(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output_node(connector)?;
let pos = connector.node_state[LiveTL].pos.get();
self.respond(Response::ConnectorSize {
width: pos.width(),
height: pos.height(),
});
Ok(())
}
fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> {
let pos = self.get_monitor_persistent(connector)?;
self.respond(Response::ConnectorGetScale {
scale: pos.scale.get().to_f64(),
});
Ok(())
}
fn handle_connector_set_scale(&self, connector: Connector, scale: f64) -> Result<(), CphError> {
if scale < 0.1 {
return Err(CphError::ScaleTooSmall(scale));
}
if scale > 1000.0 {
return Err(CphError::ScaleTooLarge(scale));
}
let scale = Scale::from_f64(scale);
let connector = self.get_output_node_or_persistent(connector)?;
connector.set_preferred_scale(scale);
Ok(())
}
fn handle_connector_set_scaling_filter(
&self,
connector: Connector,
scaling_filter: ConfigScalingFilter,
) -> Result<(), CphError> {
let Some(scaling_filter) = ScalingFilter::from_config(scaling_filter) else {
return Err(CphError::UnknownScalingFilter(scaling_filter));
};
let connector = self.get_output_node_or_persistent(connector)?;
connector.set_scaling_filter(scaling_filter);
Ok(())
}
fn handle_connector_set_format(
&self,
connector: Connector,
format: ConfigFormat,
) -> Result<(), CphError> {
let Some(&format) = config_formats().get(&format) else {
return Err(CphError::UnknownFormat(format));
};
let connector = self.get_connector(connector)?;
connector
.modify_state(&self.state, |s| s.format = format)
.map_err(CphError::ModifyConnectorState)?;
Ok(())
}
fn handle_connector_set_colors(
&self,
connector: Connector,
color_space: ColorSpace,
eotf: ConfigEotf,
) -> Result<(), CphError> {
let bcs = match color_space {
ColorSpace::DEFAULT => BackendColorSpace::Default,
ColorSpace::BT2020 => BackendColorSpace::Bt2020,
_ => return Err(CphError::UnknownColorSpace(color_space)),
};
let btf = match eotf {
ConfigEotf::DEFAULT => BackendEotfs::Default,
ConfigEotf::PQ => BackendEotfs::Pq,
_ => return Err(CphError::UnknownEotf(eotf)),
};
let connector = self.get_connector(connector)?;
connector
.modify_state(&self.state, |s| {
s.color_space = bcs;
s.eotf = btf;
})
.map_err(CphError::ModifyConnectorState)?;
Ok(())
}
fn handle_connector_set_blend_space(
&self,
connector: Connector,
blend_space: ConfigBlendSpace,
) -> Result<(), CphError> {
let blend_space = match blend_space {
ConfigBlendSpace::SRGB => BlendSpace::Srgb,
ConfigBlendSpace::LINEAR => BlendSpace::Linear,
_ => return Err(CphError::UnknownBlendSpace(blend_space)),
};
let connector = self.get_output_node_or_persistent(connector)?;
connector.set_blend_space(blend_space);
Ok(())
}
fn handle_connector_set_brightness(
&self,
connector: Connector,
brightness: Option<f64>,
) -> Result<(), CphError> {
let connector = self.get_output_node_or_persistent(connector)?;
connector.set_brightness(brightness);
Ok(())
}
fn handle_connector_set_use_native_gamut(
&self,
connector: Connector,
use_native_gamut: bool,
) -> Result<(), CphError> {
let connector = self.get_output_node_or_persistent(connector)?;
connector.set_use_native_gamut(use_native_gamut);
Ok(())
}
fn handle_set_float_above_fullscreen(&self, above: bool) {
self.state.set_float_above_fullscreen(above);
}
fn handle_get_float_above_fullscreen(&self) {
self.respond(Response::GetFloatAboveFullscreen {
above: self.state.float_above_fullscreen.get(),
});
}
fn handle_set_split_reuses_container(&self, reuse: bool) {
self.state.set_split_reuses_container(reuse);
}
fn handle_get_split_reuses_container(&self) {
self.respond(Response::GetSplitReusesContainer {
reuse: self.state.split_reuses_container.get(),
});
}
fn handle_set_show_bar(&self, show: bool) {
self.state.set_show_bar(show);
}
fn handle_get_show_bar(&self) {
self.respond(Response::GetShowBar {
show: self.state.show_bar.get(),
});
}
fn handle_set_show_titles(&self, show: bool) {
self.state.set_show_titles(show);
}
fn handle_get_show_titles(&self) {
self.respond(Response::GetShowTitles {
show: self.state.theme.show_titles.get(),
});
}
fn handle_set_bar_position(&self, position: JcBarPosition) {
self.state.set_bar_position(position.into());
}
fn handle_get_bar_position(&self) {
self.respond(Response::GetBarPosition {
position: self.state.theme.bar_position.get().into(),
});
}
fn handle_set_show_float_pin_icon(&self, show: bool) {
self.state.set_show_pin_icon(show);
}
fn handle_set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
self.state.set_workspace_display_order(order.into());
}
fn handle_get_seat_float_pinned(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
self.respond(Response::GetFloatPinned {
pinned: seat.pinned(),
});
Ok(())
}
fn handle_set_seat_float_pinned(&self, seat: Seat, pinned: bool) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_pinned(pinned);
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>,
mode: ConfigVrrMode,
) -> Result<(), CphError> {
let Some(mode) = VrrMode::from_config(mode) else {
return Err(CphError::UnknownVrrMode(mode));
};
match connector {
Some(c) => {
let connector = self.get_output_node_or_persistent(c)?;
connector.set_vrr_mode(mode);
}
_ => self.state.default_vrr_mode.set(*mode),
}
Ok(())
}
fn handle_set_vrr_cursor_hz(
&self,
connector: Option<Connector>,
hz: f64,
) -> Result<(), CphError> {
let Some((hz, _)) = map_cursor_hz(hz) else {
return Err(CphError::InvalidCursorHz(hz));
};
match connector {
Some(c) => {
let connector = self.get_output_node_or_persistent(c)?;
connector.set_cursor_hz(&self.state, hz);
}
_ => self.state.default_vrr_cursor_hz.set(hz),
}
Ok(())
}
fn handle_set_tearing_mode(
&self,
connector: Option<Connector>,
mode: ConfigTearingMode,
) -> Result<(), CphError> {
let Some(mode) = TearingMode::from_config(mode) else {
return Err(CphError::UnknownTearingMode(mode));
};
match connector {
Some(c) => {
let connector = self.get_output_node_or_persistent(c)?;
connector.set_tearing_mode(mode);
}
_ => self.state.default_tearing_mode.set(*mode),
}
Ok(())
}
fn handle_connector_set_transform(
&self,
connector: Connector,
transform: Transform,
) -> Result<(), CphError> {
let connector = self.get_output_node_or_persistent(connector)?;
connector.update_transform(transform.into());
Ok(())
}
fn handle_connector_set_position(
&self,
connector: Connector,
x: i32,
y: i32,
) -> Result<(), CphError> {
let connector = self.get_output_node_or_persistent(connector)?;
if x < 0 || y < 0 || x > MAX_EXTENTS || y > MAX_EXTENTS {
return Err(CphError::InvalidConnectorPosition(x, y));
}
connector.set_position(x, y);
Ok(())
}
fn handle_connector_get_position(&self, connector: Connector) -> Result<(), CphError> {
let pos = self.get_monitor_persistent(connector)?;
let (x, y) = pos.pos.get();
self.respond(Response::ConnectorGetPosition { x, y });
Ok(())
}
fn handle_connector_set_enabled(
&self,
connector: Connector,
enabled: bool,
) -> Result<(), CphError> {
let connector = self.get_connector(connector)?;
connector
.modify_state(&self.state, |s| {
s.enabled = enabled;
})
.map_err(CphError::ModifyConnectorState)?;
Ok(())
}
fn handle_get_connector(&self, ty: jay_config::video::connector_type::ConnectorType, idx: u32) {
let connectors = self.state.connectors.lock();
let connector = 'get_connector: {
for connector in connectors.values() {
let kid = connector.connector.kernel_id();
if ty == kid.ty.to_config() && idx == kid.idx {
break 'get_connector Connector(connector.connector.id().raw() as _);
}
}
Connector(0)
};
self.respond(Response::GetConnector { connector });
}
fn handle_get_connector_by_name(&self, name: &str) {
let connector = self
.state
.connectors
.lock()
.values()
.find(|c| *c.name == name)
.map(|c| c.connector.id().raw() as _)
.map(Connector)
.unwrap_or(Connector(0));
self.respond(Response::GetConnector { connector });
}
fn handle_create_virtual_output(&self, name: &str) {
self.state.virtual_outputs.get_or_create(&self.state, name);
}
fn handle_remove_virtual_output(&self, name: &str) {
self.state.virtual_outputs.remove_output(&self.state, name);
}
fn handle_get_connector_active_workspace(&self, connector: Connector) -> Result<(), CphError> {
let output = self.get_output_node(connector)?;
let workspace = output.node_state[LiveTL]
.workspace
.get()
.map(|ws| self.get_workspace_by_name(&ws.name, ws.ty))
.unwrap_or(Workspace(0));
self.respond(Response::GetConnectorActiveWorkspace { workspace });
Ok(())
}
fn handle_get_connector_workspaces(&self, connector: Connector) -> Result<(), CphError> {
let output = self.get_output_node(connector)?;
let workspaces = output
.workspaces
.iter_valid(LiveTL)
.map(|ws| self.get_workspace_by_name(&ws.name, ws.ty))
.collect::<Vec<_>>();
self.respond(Response::GetConnectorWorkspaces { workspaces });
Ok(())
}
fn handle_get_workspace_connector(&self, workspace: Workspace) -> Result<(), CphError> {
let connector = self
.get_existing_workspace(workspace)?
.map(|ws| ws.node_state[LiveTL].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_output_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;
let has_cap = 'has_cap: {
let cap = match cap {
CAP_KEYBOARD => InputDeviceCapability::Keyboard,
CAP_POINTER => InputDeviceCapability::Pointer,
CAP_TOUCH => InputDeviceCapability::Touch,
CAP_TABLET_TOOL => InputDeviceCapability::TabletTool,
CAP_TABLET_PAD => InputDeviceCapability::TabletPad,
CAP_GESTURE => InputDeviceCapability::Gesture,
CAP_SWITCH => InputDeviceCapability::Switch,
_ => {
is_unknown = true;
break 'has_cap false;
}
};
dev.device.has_capability(cap)
};
self.respond(Response::HasCapability { has: has_cap });
if is_unknown {
Err(CphError::UnknownCapability(cap))
} else {
Ok(())
}
}
fn handle_get_seat_mono(
&self,
seat: Seat,
target: Option<JcContainerTarget>,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let seat = self.get_seat(seat)?;
self.respond(Response::GetMono {
mono: seat.get_mono(target).unwrap_or(false),
});
Ok(())
}
fn handle_set_seat_mono(
&self,
seat: Seat,
target: Option<JcContainerTarget>,
mono: bool,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let seat = self.get_seat(seat)?;
seat.set_mono(target, mono);
Ok(())
}
fn handle_get_window_mono(
&self,
window: Window,
target: Option<JcContainerTarget>,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let window = self.get_window(window)?;
self.respond(Response::GetWindowMono {
mono: toplevel_target_container(&window, target)
.map(|c| c.node_state[LiveTL].mono_child.is_some())
.unwrap_or(false),
});
Ok(())
}
fn handle_set_window_mono(
&self,
window: Window,
target: Option<JcContainerTarget>,
mono: bool,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let window = self.get_window(window)?;
toplevel_set_target_mono(&window, target, mono);
Ok(())
}
fn handle_get_seat_split(
&self,
seat: Seat,
target: Option<JcContainerTarget>,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let seat = self.get_seat(seat)?;
self.respond(Response::GetSplit {
axis: seat
.get_split(target)
.unwrap_or(ContainerSplit::Horizontal)
.into(),
});
Ok(())
}
fn handle_set_seat_split(
&self,
seat: Seat,
target: Option<JcContainerTarget>,
axis: Axis,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let seat = self.get_seat(seat)?;
seat.set_split(target, axis.into());
Ok(())
}
fn handle_get_window_split(
&self,
window: Window,
target: Option<JcContainerTarget>,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let window = self.get_window(window)?;
self.respond(Response::GetWindowSplit {
axis: toplevel_target_container(&window, target)
.map(|c| c.node_state[LiveTL].split.get())
.unwrap_or(ContainerSplit::Horizontal)
.into(),
});
Ok(())
}
fn handle_set_window_split(
&self,
window: Window,
target: Option<JcContainerTarget>,
axis: Axis,
) -> Result<(), CphError> {
let target = map_container_target_opt(target);
let window = self.get_window(window)?;
if let Some(c) = toplevel_target_container(&window, target) {
c.set_split(axis.into());
}
Ok(())
}
fn handle_create_seat_split_relative(
&self,
seat: Seat,
axis: JcRelativeAxis,
) -> Result<(), CphError> {
let axis = map_relative_axis(axis);
let seat = self.get_seat(seat)?;
seat.create_split_relative(axis);
Ok(())
}
fn handle_create_window_split_relative(
&self,
window: Window,
axis: JcRelativeAxis,
) -> Result<(), CphError> {
let axis = map_relative_axis(axis);
let window = self.get_window(window)?;
let pos = window.node_absolute_position(LiveTL);
let split = ContainerSplit::from_relative_axis(axis, &pos);
toplevel_create_split(&self.state, window, split);
Ok(())
}
fn handle_set_seat_split_relative(
&self,
seat: Seat,
target: JcContainerTarget,
axis: JcRelativeAxis,
) -> Result<(), CphError> {
let target = map_container_target(target);
let axis = map_relative_axis(axis);
let seat = self.get_seat(seat)?;
seat.set_split_relative(target, axis);
Ok(())
}
fn handle_set_window_split_relative(
&self,
window: Window,
target: JcContainerTarget,
axis: JcRelativeAxis,
) -> Result<(), CphError> {
let target = map_container_target(target);
let axis = map_relative_axis(axis);
let window = self.get_window(window)?;
if let Some(c) = toplevel_target_container(&window, target) {
let pos = c.node_absolute_position(LiveTL);
c.set_split(ContainerSplit::from_relative_axis(axis, &pos));
}
Ok(())
}
fn handle_add_shortcut(
&self,
seat: Seat,
mod_mask: Modifiers,
mods: Modifiers,
sym: KeySym,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.add_shortcut(mod_mask, mods, sym);
Ok(())
}
fn handle_remove_shortcut(
&self,
seat: Seat,
mods: Modifiers,
sym: KeySym,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.remove_shortcut(mods, sym);
Ok(())
}
fn handle_set_shortcut_repeat(
&self,
seat: Seat,
mods: Modifiers,
sym: KeySym,
repeat: bool,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_shortcut_repeat(mods, sym, repeat);
Ok(())
}
fn handle_get_input_devices(&self, seat: Option<Seat>) {
let id = seat.map(|s| SeatId::from_raw(s.0 as _));
let matches = |dhd: &DeviceHandlerData| {
let Some(id) = id else { return true };
if let Some(seat) = dhd.seat.get() {
return seat.id() == id;
}
false
};
let mut res = vec![];
{
let devs = self.state.input_device_handlers.borrow_mut();
for dev in devs.values() {
if matches(&dev.data) {
res.push(InputDevice(dev.id.raw() as _));
}
}
}
self.respond(Response::GetInputDevices { devices: res });
}
fn handle_get_seats(&self) {
let seats = {
let seats = self.state.globals.seats.lock();
seats
.values()
.map(|seat| Seat::from_raw(seat.id().raw() as _))
.collect()
};
self.respond(Response::GetSeats { seats });
}
fn handle_run(
&self,
prog: &str,
args: Vec<String>,
mut env: Vec<(String, String)>,
fds: Vec<(i32, i32)>,
tag: Option<&str>,
) -> Result<(), CphError> {
if let Some(tag) = tag {
let display = self
.state
.tagged_acceptors
.get(&self.state, tag)
.map_err(CphError::CreateTaggedAcceptor)?;
env.push((WAYLAND_DISPLAY.name().to_string(), display.to_string()));
}
let fds: Vec<_> = fds
.into_iter()
.map(|(a, b)| (a, Rc::new(OwnedFd::new(b))))
.collect();
let Some(forker) = self.state.forker.get() else {
return Err(CphError::NoForker);
};
let env = env.into_iter().map(|(k, v)| (k, Some(v))).collect();
forker.spawn(prog.to_string(), args, env, fds);
Ok(())
}
fn handle_set_egui_fonts(&self, proportional: Option<Vec<&str>>, monospace: Option<Vec<&str>>) {
self.state.set_egui_fonts(proportional, monospace);
}
fn handle_open_control_center(&self) {
if let Err(e) = self.state.open_control_center() {
log::error!("Could not open control center: {}", ErrorFmt(e));
}
}
fn handle_set_log_level(&self, level: ConfigLogLevel) {
self.state.set_log_level(level.into());
}
fn handle_clean_logs_older_than(&self, time: SystemTime) {
self.state.clean_logs_older_than.set(Some(time));
}
fn handle_grab(&self, kb: InputDevice, grab: bool) -> Result<(), CphError> {
let kb = self.get_kb(kb)?;
kb.grab(grab);
Ok(())
}
fn handle_create_seat_split(&self, seat: Seat, axis: Axis) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.create_split(axis.into());
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();
Ok(())
}
fn handle_quit(&self) {
log::info!("Quitting");
self.state.ring.stop();
}
fn handle_switch_to(&self, vtnr: u32) {
self.state.backend.get().switch_to(vtnr);
}
fn handle_get_seat_floating(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
self.respond(Response::GetFloating {
floating: seat.get_floating().unwrap_or(false),
});
Ok(())
}
fn handle_set_seat_floating(&self, seat: Seat, floating: bool) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_floating(floating);
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().parent_is_float.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) {
let fd = match fcntl_dupfd_cloexec(fd, 0).to_os_error() {
Ok(fd) => Rc::new(fd),
Err(e) => {
let err = format!("Could not invoke F_DUPFD_CLOEXEC: {}", ErrorFmt(e));
log::error!("{err}");
self.respond(Response::AddPollable { id: Err(err) });
return;
}
};
let id = self.pollable_id.fetch_add(1);
let id = PollableId(id);
let create = |writable: bool, events: c::c_short| {
let event = Rc::new(AsyncEvent::default());
let slf = self.clone();
let trigger = event.clone();
let fd = fd.clone();
let future = self.state.eng.spawn("config fd poller", async move {
loop {
trigger.triggered().await;
let res = slf.state.ring.poll(&fd, events).await.merge();
if let Err(e) = &res {
log::warn!("Could not poll fd: {}", ErrorFmt(e));
}
let res = res.map_err(|e| ErrorFmt(e).to_string()).map(drop);
slf.send(&ServerMessage::InterestReady { id, writable, res });
}
});
(event, future)
};
let (read_trigger, _read_future) = create(false, c::POLLIN);
let (write_trigger, _write_future) = create(true, c::POLLOUT);
self.pollables.set(
id,
Rc::new(Pollable {
write_trigger,
_write_future,
read_trigger,
_read_future,
}),
);
self.respond(Response::AddPollable { id: Ok(id) });
}
fn handle_remove_pollable(self: &Rc<Self>, id: PollableId) {
self.pollables.remove(&id);
}
fn handle_add_interest(
self: &Rc<Self>,
id: PollableId,
writable: bool,
) -> Result<(), CphError> {
let Some(pollable) = self.pollables.get(&id) else {
return Err(CphError::PollableDoesNotExist);
};
let trigger = match writable {
true => &pollable.write_trigger,
false => &pollable.read_trigger,
};
trigger.trigger();
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 get_client_matcher(
&self,
matcher: ClientMatcher,
) -> Result<Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>, CphError> {
self.client_matchers
.get(&matcher)
.ok_or(CphError::ClientMatcherDoesNotExist(matcher))
}
fn sort_generic_matcher<T, K>(
&self,
generic: &mut GenericCriterionIpc<T>,
key: impl FnMut(&T) -> K,
) where
K: Ord,
{
match generic {
GenericCriterionIpc::List { list, .. } | GenericCriterionIpc::Exactly { list, .. } => {
list.sort_by_key(key);
}
GenericCriterionIpc::Matcher(_) | GenericCriterionIpc::Not(_) => {}
}
}
fn create_generic_matcher<Crit, Matcher, Mgr>(
&self,
mgr: &Mgr,
generic: &GenericCriterionIpc<Matcher>,
upstream: &mut Vec<Rc<CachedCriterion<Crit, Mgr::Target>>>,
get_matcher: impl Fn(&Matcher) -> Result<Rc<CachedCriterion<Crit, Mgr::Target>>, CphError>,
) -> Result<Rc<dyn CritUpstreamNode<Mgr::Target>>, CphError>
where
Crit: Clone + JayHash + Eq,
Mgr: CritMgrExt,
{
let mut get_upstream = |m: &Matcher| -> Result<_, CphError> {
let m = get_matcher(m)?;
let node = m.node.clone();
upstream.push(m);
Ok(node)
};
let node = match generic {
GenericCriterionIpc::Matcher(m) => get_matcher(m)?.node.clone(),
GenericCriterionIpc::Not(m) => mgr.not(&get_upstream(m)?),
GenericCriterionIpc::List { list, all } => {
let mut m = Vec::with_capacity(list.len());
for c in list {
m.push(get_upstream(c)?);
}
mgr.list(&m, *all)
}
GenericCriterionIpc::Exactly { list, num } => {
let mut m = Vec::with_capacity(list.len());
for c in list {
m.push(get_upstream(c)?);
}
mgr.exactly(&m, *num)
}
};
Ok(node)
}
fn handle_create_client_matcher(
&self,
mut criterion: ClientCriterionIpc,
) -> Result<(), CphError> {
if let ClientCriterionIpc::Generic(generic) = &mut criterion {
self.sort_generic_matcher(generic, |m| m.0);
}
let id = ClientMatcher(self.client_matcher_ids.fetch_add(1));
let cache = &self.client_matcher_cache;
if let Some(matcher) = cache.get(&criterion)
&& let Some(matcher) = matcher.upgrade()
{
self.client_matchers.set(id, matcher);
self.respond(Response::CreateClientMatcher { matcher: id });
return Ok(());
}
let mgr = &*self.state.cl_matcher_manager;
let mut upstream = vec![];
let matcher = match &criterion {
ClientCriterionIpc::Generic(m) => {
self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_client_matcher(*m))?
}
ClientCriterionIpc::String {
string,
field,
regex,
} => {
let needle = match *regex {
true => {
let regex = Regex::new(string).map_err(CphError::InvalidRegex)?;
CritLiteralOrRegex::Regex(regex)
}
false => CritLiteralOrRegex::Literal(string.to_string()),
};
match *field {
ClientCriterionStringField::SandboxEngine => mgr.sandbox_engine(needle),
ClientCriterionStringField::SandboxAppId => mgr.sandbox_app_id(needle),
ClientCriterionStringField::SandboxInstanceId => {
mgr.sandbox_instance_id(needle)
}
ClientCriterionStringField::Comm => mgr.comm(needle),
ClientCriterionStringField::Exe => mgr.exe(needle),
ClientCriterionStringField::Tag => mgr.tag(needle),
}
}
ClientCriterionIpc::Sandboxed => mgr.sandboxed(),
ClientCriterionIpc::Uid(p) => mgr.uid(*p),
ClientCriterionIpc::Pid(p) => mgr.pid(*p),
ClientCriterionIpc::IsXwayland => mgr.is_xwayland(),
};
let cached = Rc::new(CachedCriterion {
crit: criterion.clone(),
cache: cache.clone(),
upstream,
node: matcher.clone(),
});
cache.set(criterion, Rc::downgrade(&cached));
self.client_matchers.set(id, cached);
self.respond(Response::CreateClientMatcher { matcher: id });
Ok(())
}
fn handle_destroy_client_matcher(&self, matcher: ClientMatcher) {
self.client_matchers.remove(&matcher);
self.client_matcher_leafs.remove(&matcher);
self.client_matcher_capabilities.remove(&matcher);
self.client_matcher_bounding_capabilities.remove(&matcher);
}
fn handle_enable_client_matcher_events(
self: &Rc<Self>,
matcher: ClientMatcher,
) -> Result<(), CphError> {
if self.client_matcher_leafs.contains(&matcher) {
return Ok(());
}
let upstream = self.get_client_matcher(matcher)?;
let slf = self.clone();
let leaf = self
.state
.cl_matcher_manager
.leaf(&upstream.node, move |id| {
let client = ConfigClient(id.raw());
slf.send(&ServerMessage::ClientMatcherMatched { matcher, client });
let slf = slf.clone();
Box::new(move || {
slf.send(&ServerMessage::ClientMatcherUnmatched { matcher, client });
})
});
self.client_matcher_leafs.set(matcher, leaf);
self.state.cl_matcher_manager.rematch_all(&self.state);
Ok(())
}
fn get_window_matcher(
&self,
matcher: WindowMatcher,
) -> Result<Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>, CphError> {
self.window_matchers
.get(&matcher)
.ok_or(CphError::WindowMatcherDoesNotExist(matcher))
}
fn handle_create_window_matcher(
&self,
mut criterion: WindowCriterionIpc,
) -> Result<(), CphError> {
if let WindowCriterionIpc::Generic(generic) = &mut criterion {
self.sort_generic_matcher(generic, |m| m.0);
}
let id = WindowMatcher(self.window_matcher_ids.fetch_add(1));
let cache = &self.window_matcher_cache;
if let Some(matcher) = cache.get(&criterion)
&& let Some(matcher) = matcher.upgrade()
{
self.window_matchers.set(id, matcher);
self.respond(Response::CreateWindowMatcher { matcher: id });
return Ok(());
}
let mgr = &*self.state.tl_matcher_manager;
let mut upstream = vec![];
let matcher = match &criterion {
WindowCriterionIpc::Generic(m) => {
self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_window_matcher(*m))?
}
WindowCriterionIpc::String {
string,
field,
regex,
} => {
let needle = match *regex {
true => {
let regex = Regex::new(string).map_err(CphError::InvalidRegex)?;
CritLiteralOrRegex::Regex(regex)
}
false => CritLiteralOrRegex::Literal(string.to_string()),
};
match *field {
WindowCriterionStringField::Title => mgr.title(needle),
WindowCriterionStringField::AppId => mgr.app_id(needle),
WindowCriterionStringField::Tag => mgr.tag(needle),
WindowCriterionStringField::XClass => mgr.class(needle),
WindowCriterionStringField::XInstance => mgr.instance(needle),
WindowCriterionStringField::XRole => mgr.role(needle),
WindowCriterionStringField::Workspace => mgr.workspace(needle),
}
}
WindowCriterionIpc::Types(t) => mgr.kind(*t),
WindowCriterionIpc::Client(c) => {
self.state.cl_matcher_manager.rematch_all(&self.state);
mgr.client(&self.state, &self.get_client_matcher(*c)?.node)
}
WindowCriterionIpc::Floating => mgr.floating(),
WindowCriterionIpc::Visible => mgr.visible(),
WindowCriterionIpc::Urgent => mgr.urgent(),
WindowCriterionIpc::SeatFocus(seat) => mgr.seat_focus(&*self.get_seat(*seat)?),
WindowCriterionIpc::Fullscreen => mgr.fullscreen(),
WindowCriterionIpc::JustMapped => mgr.just_mapped(),
WindowCriterionIpc::IsWorkspaceContainer => mgr.is_workspace_container(),
WindowCriterionIpc::Workspace(w) => mgr.workspace(CritLiteralOrRegex::Literal(
self.get_workspace(*w)?.name.clone(),
)),
WindowCriterionIpc::ContentTypes(t) => mgr.content_type(*t),
};
let cached = Rc::new(CachedCriterion {
crit: criterion.clone(),
cache: cache.clone(),
upstream,
node: matcher.clone(),
});
cache.set(criterion, Rc::downgrade(&cached));
self.window_matchers.set(id, cached);
self.respond(Response::CreateWindowMatcher { matcher: id });
Ok(())
}
fn handle_destroy_window_matcher(&self, matcher: WindowMatcher) {
self.window_matchers.remove(&matcher);
self.window_matcher_leafs.remove(&matcher);
self.window_matcher_properties.remove(&matcher);
}
fn ensure_window_matcher_properties(
&self,
matcher: WindowMatcher,
) -> Result<Rc<WindowMatcherProperties>, CphError> {
if let Some(p) = self.window_matcher_properties.get(&matcher) {
return Ok(p);
}
let p = Rc::new(WindowMatcherProperties {
criterion: self.get_window_matcher(matcher)?,
auto_focus: Default::default(),
initial_tile_state: Default::default(),
initial_floating_size: Default::default(),
initial_floating_position: Default::default(),
});
self.window_matcher_properties.set(matcher, p.clone());
Ok(p)
}
fn handle_enable_window_matcher_events(
self: &Rc<Self>,
matcher: WindowMatcher,
) -> Result<(), CphError> {
if self.window_matcher_leafs.contains(&matcher) {
return Ok(());
}
let upstream = self.get_window_matcher(matcher)?;
let mut node = upstream.node.clone();
if !upstream.any(&|crit| matches!(crit, WindowCriterionIpc::Types(_))) {
let list = [self.window_matcher_std_kinds.clone(), node];
node = self.state.tl_matcher_manager.list(&list, true);
}
let slf = self.clone();
let leaf = self.state.tl_matcher_manager.leaf(&node, move |tl| {
let window = slf.tl_id_to_window(tl);
slf.send(&ServerMessage::WindowMatcherMatched { matcher, window });
let slf = slf.clone();
Box::new(move || {
slf.send(&ServerMessage::WindowMatcherUnmatched { matcher, window });
})
});
self.window_matcher_leafs.set(matcher, leaf);
self.state.tl_matcher_manager.rematch_all(&self.state);
Ok(())
}
fn handle_set_window_matcher_auto_focus(
&self,
matcher: WindowMatcher,
auto_focus: bool,
) -> Result<(), CphError> {
self.ensure_window_matcher_properties(matcher)?
.auto_focus
.set(Some(auto_focus));
Ok(())
}
fn handle_set_window_matcher_initial_tile_state(
&self,
matcher: WindowMatcher,
tile_state: JcTileState,
) -> Result<(), CphError> {
self.ensure_window_matcher_properties(matcher)?
.initial_tile_state
.set(Some(tile_state.into()));
Ok(())
}
fn handle_set_window_matcher_initial_floating_size(
&self,
matcher: WindowMatcher,
width: i32,
height: i32,
) -> Result<(), CphError> {
if width <= 0 || height <= 0 {
return Err(CphError::InvalidWindowSize(width, height));
}
self.ensure_window_matcher_properties(matcher)?
.initial_floating_size
.set(Some((width, height)));
Ok(())
}
fn handle_set_window_matcher_initial_floating_position(
&self,
matcher: WindowMatcher,
x: i32,
y: i32,
) -> Result<(), CphError> {
self.ensure_window_matcher_properties(matcher)?
.initial_floating_position
.set(Some((x, y)));
Ok(())
}
fn handle_set_pointer_revert_key(&self, seat: Seat, key: KeySym) -> Result<(), CphError> {
self.get_seat(seat)?.set_pointer_revert_key(key);
Ok(())
}
fn handle_seat_focus_history(&self, seat: Seat, timeline: Timeline) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
match timeline {
Timeline::Older => seat.focus_prev(),
Timeline::Newer => seat.focus_next(),
}
Ok(())
}
fn handle_seat_focus_history_set_only_visible(
&self,
seat: Seat,
visible: bool,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.focus_history_set_visible(visible);
Ok(())
}
fn handle_seat_focus_history_set_same_workspace(
&self,
seat: Seat,
same_workspace: bool,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.focus_history_set_same_workspace(same_workspace);
Ok(())
}
fn handle_seat_focus_layer_rel(
&self,
seat: Seat,
direction: LayerDirection,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
match direction {
LayerDirection::Below => seat.focus_layer_below(),
LayerDirection::Above => seat.focus_layer_above(),
}
Ok(())
}
fn handle_seat_focus_tiles(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.focus_tiles();
Ok(())
}
fn handle_set_middle_click_paste_enabled(&self, enabled: bool) {
self.state.set_primary_selection_enabled(enabled);
}
fn handle_seat_create_mark(&self, seat: Seat, kc: Option<u32>) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
if let Some(kc) = kc {
seat.create_mark(Keycode::from_evdev(kc));
} else {
seat.create_mark_interactive();
}
Ok(())
}
fn handle_seat_jump_to_mark(&self, seat: Seat, kc: Option<u32>) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
if let Some(kc) = kc {
seat.jump_to_mark(Keycode::from_evdev(kc));
} else {
seat.jump_to_mark_interactive();
}
Ok(())
}
fn handle_seat_copy_mark(&self, seat: Seat, src: u32, dst: u32) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.copy_mark(Keycode::from_evdev(src), Keycode::from_evdev(dst));
Ok(())
}
fn handle_seat_set_simple_im_enabled(&self, seat: Seat, enabled: bool) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_simple_im_enabled(enabled);
Ok(())
}
fn handle_seat_get_simple_im_enabled(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
self.respond(Response::SeatGetSimpleImEnabled {
enabled: seat.simple_im_enabled(),
});
Ok(())
}
fn handle_seat_reload_simple_im(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.reload_simple_im();
Ok(())
}
fn handle_seat_enable_unicode_input(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.enable_unicode_input();
Ok(())
}
fn handle_seat_warp_mouse_to_focus(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.schedule_warp_mouse_to_focus();
Ok(())
}
fn handle_seat_set_mouse_follows_focus(
&self,
seat: Seat,
enabled: bool,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_mouse_follows_focus(enabled);
Ok(())
}
fn get_sized(&self, sized: Resizable) -> Result<ThemeSized, CphError> {
use jay_config::theme::sized::*;
let sized = match sized {
TITLE_HEIGHT => ThemeSized::title_height,
BORDER_WIDTH => ThemeSized::border_width,
BAR_HEIGHT => ThemeSized::bar_height,
BAR_SEPARATOR_WIDTH => ThemeSized::bar_separator_width,
_ => return Err(CphError::UnknownSized(sized.0)),
};
Ok(sized)
}
fn handle_get_size(&self, sized: Resizable) -> Result<(), CphError> {
let sized = self.get_sized(sized)?;
let size = sized.field(&self.state.theme).val.get();
self.respond(Response::GetSize { size });
Ok(())
}
fn handle_set_size(&self, sized: Resizable, size: i32) -> Result<(), CphError> {
let sized = self.get_sized(sized)?;
if size < sized.min() {
return Err(CphError::InvalidSize(size, sized));
}
if size > sized.max() {
return Err(CphError::InvalidSize(size, sized));
}
self.state.set_size(sized, size);
Ok(())
}
fn handle_reset_colors(&self) {
self.state.reset_colors();
}
fn handle_reset_sizes(&self) {
self.state.reset_sizes();
}
fn handle_reset_font(&self) {
self.state.reset_fonts();
}
fn handle_set_font(&self, font: &str) {
self.state.set_font(font);
}
fn handle_set_bar_font(&self, font: &str) {
self.state.set_bar_font(Some(font));
}
fn handle_set_title_font(&self, font: &str) {
self.state.set_title_font(Some(font));
}
fn handle_get_font(&self) {
let font = self.state.theme.font.get().to_string();
self.respond(Response::GetFont { font });
}
fn get_color(&self, colorable: Colorable) -> Result<ThemeColored, CphError> {
use jay_config::theme::colors::*;
let colorable = match colorable {
UNFOCUSED_TITLE_BACKGROUND_COLOR => ThemeColored::unfocused_title_background,
FOCUSED_TITLE_BACKGROUND_COLOR => ThemeColored::focused_title_background,
CAPTURED_UNFOCUSED_TITLE_BACKGROUND_COLOR => {
ThemeColored::captured_unfocused_title_background
}
CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR => {
ThemeColored::captured_focused_title_background
}
FOCUSED_INACTIVE_TITLE_BACKGROUND_COLOR => {
ThemeColored::focused_inactive_title_background
}
BACKGROUND_COLOR => ThemeColored::background,
BAR_BACKGROUND_COLOR => ThemeColored::bar_background,
SEPARATOR_COLOR => ThemeColored::separator,
BORDER_COLOR => ThemeColored::border,
UNFOCUSED_TITLE_TEXT_COLOR => ThemeColored::unfocused_title_text,
FOCUSED_TITLE_TEXT_COLOR => ThemeColored::focused_title_text,
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => ThemeColored::focused_inactive_title_text,
BAR_STATUS_TEXT_COLOR => ThemeColored::bar_text,
ATTENTION_REQUESTED_BACKGROUND_COLOR => ThemeColored::attention_requested_background,
HIGHLIGHT_COLOR => ThemeColored::highlight,
FOCUSED_BORDER_COLOR => ThemeColored::focused_border,
_ => return Err(CphError::UnknownColor(colorable.0)),
};
Ok(colorable)
}
fn handle_get_color(&self, colorable: Colorable) -> Result<(), CphError> {
let color = self.get_color(colorable)?.field(&self.state.theme).get();
let [r, g, b, a] = color.to_array(Eotf::Gamma22);
let color = jay_config::theme::Color::new_f32_premultiplied(r, g, b, a);
self.respond(Response::GetColor { color });
Ok(())
}
fn handle_set_color(
&self,
colorable: Colorable,
color: jay_config::theme::Color,
) -> Result<(), CphError> {
self.state
.set_color(self.get_color(colorable)?, color.into());
Ok(())
}
fn handle_destroy_keymap(&self, keymap: Keymap) {
self.keymaps.remove(&keymap);
}
fn get_client(&self, client: ConfigClient) -> Result<Rc<Client>, CphError> {
self.state
.clients
.get(ClientId::from_raw(client.0))
.ok()
.ok_or(CphError::ClientDoesNotExist(client))
}
fn handle_get_clients(&self) {
let mut clients = vec![];
for client in self.state.clients.clients.borrow().values() {
clients.push(ConfigClient(client.data.id.raw()));
}
self.respond(Response::GetClients { clients });
}
fn handle_client_exists(&self, client: ConfigClient) {
self.respond(Response::ClientExists {
exists: self.get_client(client).is_ok(),
});
}
fn handle_client_is_xwayland(&self, client: ConfigClient) -> Result<(), CphError> {
self.respond(Response::ClientIsXwayland {
is_xwayland: self.get_client(client)?.is_xwayland,
});
Ok(())
}
fn handle_client_kill(&self, client: ConfigClient) {
if let Ok(client) = self.state.clients.get(ClientId::from_raw(client.0)) {
client.kill();
}
}
fn handle_get_workspace_window(&self, ws: Workspace) -> Result<(), CphError> {
let window = self
.get_existing_workspace(ws)?
.and_then(|ws| ws.node_state[LiveTL].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(LiveTL) {
return Err(CphError::WindowNotVisible(window_id));
}
seat.focus_toplevel(window);
seat.maybe_schedule_warp_mouse_to_focus();
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_get_content_type(&self, window: Window) -> Result<(), CphError> {
let kind = self
.get_window(window)?
.tl_data()
.content_type
.get()
.to_config();
self.respond(Response::GetContentType { kind });
Ok(())
}
fn handle_window_resize(
&self,
window: Window,
dx1: i32,
dy1: i32,
dx2: i32,
dy2: i32,
) -> Result<(), CphError> {
self.get_window(window)?.tl_resize(dx1, dy1, dx2, dy2);
Ok(())
}
fn handle_get_window_position(&self, window: Window) -> Result<(), CphError> {
let pos = self.get_window(window)?.node_absolute_position(LiveTL);
self.respond(Response::GetWindowPosition {
x: pos.x1(),
y: pos.y1(),
width: pos.width(),
height: pos.height(),
});
Ok(())
}
fn handle_get_workspace_position(&self, workspace: Workspace) -> Result<(), CphError> {
let pos = self
.get_existing_workspace(workspace)?
.map(|ws| ws.node_absolute_position(LiveTL))
.unwrap_or_default();
self.respond(Response::GetWorkspacePosition {
x: pos.x1(),
y: pos.y1(),
width: pos.width(),
height: pos.height(),
});
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(LiveTL),
});
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[LiveTL]
.get()
.map(|ws| self.get_workspace_by_name(&ws.name, ws.ty))
.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_valid(LiveTL) {
windows.push(self.tl_to_window(&*c.node));
}
}
self.respond(Response::GetWindowChildren { windows });
Ok(())
}
fn handle_set_client_matcher_capabilities(
&self,
matcher: ClientMatcher,
caps: ClientCapabilities,
) -> Result<(), CphError> {
let m = self.get_client_matcher(matcher)?;
self.client_matcher_capabilities
.set(matcher, (m, caps.to_client_caps()));
Ok(())
}
fn handle_set_client_matcher_bounding_capabilities(
&self,
matcher: ClientMatcher,
caps: ClientCapabilities,
) -> Result<(), CphError> {
let m = self.get_client_matcher(matcher)?;
self.client_matcher_bounding_capabilities
.set(matcher, (m, caps.to_client_caps()));
Ok(())
}
fn handle_get_seat_connector(
&self,
seat: Seat,
get: impl FnOnce(&WlSeatGlobal) -> Option<Rc<OutputNode>>,
) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
let mut connector = Connector(0);
if let Some(output) = get(&seat)
&& !output.is_dummy
{
connector = Connector(output.global.connector.id.raw() as u64);
}
self.respond(Response::GetSeatConnector { connector });
Ok(())
}
fn handle_get_seat_cursor_connector(&self, seat: Seat) -> Result<(), CphError> {
self.handle_get_seat_connector(seat, |s| Some(s.get_cursor_output()))
}
fn handle_get_seat_keyboard_connector(&self, seat: Seat) -> Result<(), CphError> {
self.handle_get_seat_connector(seat, |s| s.get_keyboard_output())
}
fn handle_connector_compositor_output(&self, connector: Connector) {
let compositor_output = self
.state
.root
.outputs
.contains(&ConnectorId::from_raw(connector.0 as _));
self.respond(Response::ConnectorCompositorOutput { compositor_output });
}
fn handle_parse_keymap_2(&self, v1: KeymapBuildParamsV1<'_>) -> Result<(), CphError> {
let Some(kind) = v1.kind else {
return Err(CphError::MissingKeymapKind);
};
let ctx = &self.state.kb_ctx;
let group = v1.shortcuts_group.map(GroupIndex);
let res = match kind {
KeymapBuildParamsV1Kind::Map(m) => ctx.parse_keymap(m.as_bytes(), group),
KeymapBuildParamsV1Kind::Names {
rules,
model,
groups,
options,
} => {
let kbvm_groups = groups.map(|groups| {
groups
.iter()
.map(|g| kbvm::xkb::rmlvo::Group {
layout: g.layout,
variant: g.variant,
})
.collect::<Vec<_>>()
});
ctx.keymap_from_names(
rules,
model,
kbvm_groups.as_deref(),
options.as_deref(),
group,
)
}
};
let (keymap, res) = match res {
Ok(keymap) => {
let id = Keymap(self.id());
self.keymaps.set(id, keymap);
(id, Ok(()))
}
Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))),
};
self.respond(Response::ParseKeymap2 { keymap });
res
}
fn handle_set_visualize_compositing(&self, visualize: bool) {
self.state.set_visualize_compositing(visualize);
}
fn handle_get_visualize_compositing(&self) {
self.respond(Response::GetVisualizeCompositing {
visualize: self.state.visualize_compositing.get(),
});
}
fn handle_set_transaction_timeout(&self, timeout: Duration) {
self.state
.set_transaction_timeout_ns(SaturatingCast::saturating_cast(timeout.as_nanos()));
}
fn handle_set_configure_timeout(&self, timeout: Duration) {
self.state
.set_configure_timeout_ns(SaturatingCast::saturating_cast(timeout.as_nanos()));
}
fn handle_set_container_borders(&self, borders: JcContainerBorders) {
self.state.set_container_borders(borders.into());
}
fn handle_get_container_borders(&self) {
self.respond(Response::GetContainerBorders {
borders: self.state.theme.container_borders.get().into(),
});
}
fn modify_window_theme(
&self,
window: Window,
kind: WindowThemeKind,
set: bool,
f: impl FnOnce(&ToplevelTheme),
) -> Result<(), CphError> {
let window = self.get_window(window)?;
let ty = map_window_theme_kind(kind);
let data = window.tl_data();
if !set && data.theme(ty).is_none() {
return Ok(());
}
data.modify_theme(ty, f);
Ok(())
}
fn read_window_theme<T>(
&self,
window: Window,
kind: WindowThemeKind,
f: impl FnOnce(&ToplevelTheme) -> Option<T>,
) -> Result<Option<T>, CphError> {
let window = self.get_window(window)?;
let ty = map_window_theme_kind(kind);
Ok(window.tl_data().theme(ty).and_then(f))
}
fn get_window_theme_colored(
&self,
colorable: Colorable,
) -> Result<ToplevelThemeColored, CphError> {
use jay_config::theme::colors::*;
let colorable = match colorable {
UNFOCUSED_TITLE_BACKGROUND_COLOR => ToplevelThemeColored::unfocused_title_background,
FOCUSED_TITLE_BACKGROUND_COLOR => ToplevelThemeColored::focused_title_background,
FOCUSED_INACTIVE_TITLE_BACKGROUND_COLOR => {
ToplevelThemeColored::focused_inactive_title_background
}
UNFOCUSED_TITLE_TEXT_COLOR => ToplevelThemeColored::unfocused_title_text,
FOCUSED_TITLE_TEXT_COLOR => ToplevelThemeColored::focused_title_text,
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => ToplevelThemeColored::focused_inactive_title_text,
SEPARATOR_COLOR => ToplevelThemeColored::separator,
BORDER_COLOR => ToplevelThemeColored::border,
FOCUSED_BORDER_COLOR => ToplevelThemeColored::focused_border,
ATTENTION_REQUESTED_BACKGROUND_COLOR => {
ToplevelThemeColored::attention_requested_background
}
_ => return Err(CphError::UnsupportedWindowThemeColor(colorable.0)),
};
Ok(colorable)
}
fn get_window_theme_sized(&self, sized: Resizable) -> Result<ToplevelThemeSized, CphError> {
use jay_config::theme::sized::*;
let sized = match sized {
TITLE_HEIGHT => ToplevelThemeSized::title_height,
BORDER_WIDTH => ToplevelThemeSized::border_width,
_ => return Err(CphError::UnsupportedWindowThemeSized(sized.0)),
};
Ok(sized)
}
fn handle_reset_window_theme(
&self,
window: Window,
kind: WindowThemeKind,
) -> Result<(), CphError> {
self.modify_window_theme(window, kind, false, |t| t.reset_immutable())
}
fn handle_set_window_theme_color(
&self,
window: Window,
kind: WindowThemeKind,
colorable: Colorable,
color: Option<jay_config::theme::Color>,
) -> Result<(), CphError> {
let tc = self.get_window_theme_colored(colorable)?;
self.modify_window_theme(window, kind, color.is_some(), |t| {
tc.field(t).set(color.map(|c| c.into()));
})
}
fn handle_get_window_theme_color(
&self,
window: Window,
kind: WindowThemeKind,
colorable: Colorable,
) -> Result<(), CphError> {
let tc = self.get_window_theme_colored(colorable)?;
let color = self.read_window_theme(window, kind, |t| tc.field(t).get())?;
let color = color.map(|c| {
let [r, g, b, a] = c.to_array(Eotf::Gamma22);
jay_config::theme::Color::new_f32_premultiplied(r, g, b, a)
});
self.respond(Response::GetWindowThemeColor { color });
Ok(())
}
fn handle_set_window_theme_size(
&self,
window: Window,
kind: WindowThemeKind,
sized: Resizable,
size: Option<i32>,
) -> Result<(), CphError> {
let ts = self.get_window_theme_sized(sized)?;
if let Some(size) = size
&& ts.not_admits(size)
{
return Err(CphError::InvalidSize(size, ts.theme()));
}
self.modify_window_theme(window, kind, size.is_some(), |t| ts.field(t).set(size))
}
fn handle_get_window_theme_size(
&self,
window: Window,
kind: WindowThemeKind,
sized: Resizable,
) -> Result<(), CphError> {
let ts = self.get_window_theme_sized(sized)?;
let size = self.read_window_theme(window, kind, |t| ts.field(t).get())?;
self.respond(Response::GetWindowThemeSize { size });
Ok(())
}
fn handle_set_window_theme_show_titles(
&self,
window: Window,
kind: WindowThemeKind,
show: Option<bool>,
) -> Result<(), CphError> {
self.modify_window_theme(window, kind, show.is_some(), |t| t.show_titles.set(show))
}
fn handle_get_window_theme_show_titles(
&self,
window: Window,
kind: WindowThemeKind,
) -> Result<(), CphError> {
let show = self.read_window_theme(window, kind, |t| t.show_titles.get())?;
self.respond(Response::GetWindowThemeShowTitles { show });
Ok(())
}
fn handle_set_window_theme_show_window_icons(
&self,
window: Window,
kind: WindowThemeKind,
show: Option<bool>,
) -> Result<(), CphError> {
self.modify_window_theme(window, kind, show.is_some(), |t| {
t.show_window_icons.set(show);
})
}
fn handle_set_window_theme_window_icons_grayscale(
&self,
window: Window,
kind: WindowThemeKind,
grayscale: Option<bool>,
) -> Result<(), CphError> {
self.modify_window_theme(window, kind, grayscale.is_some(), |t| {
t.window_icons_grayscale.set(grayscale);
})
}
fn handle_set_window_theme_title_font(
&self,
window: Window,
kind: WindowThemeKind,
font: Option<&str>,
) -> Result<(), CphError> {
self.modify_window_theme(window, kind, font.is_some(), |t| {
t.title_font.set(font.map(|f| Rc::new(f.into())));
})
}
fn handle_set_window_theme_container_borders(
&self,
window: Window,
kind: WindowThemeKind,
borders: Option<JcContainerBorders>,
) -> Result<(), CphError> {
self.modify_window_theme(window, kind, borders.is_some(), |t| {
t.container_borders.set(borders.map(Into::into));
})
}
fn handle_get_window_theme_container_borders(
&self,
window: Window,
kind: WindowThemeKind,
) -> Result<(), CphError> {
let borders = self.read_window_theme(window, kind, |t| t.container_borders.get())?;
self.respond(Response::GetWindowThemeContainerBorders {
borders: borders.map(|b| b.into()),
});
Ok(())
}
fn handle_set_workspace_initial_connector(
&self,
workspace: Workspace,
connector: Option<Connector>,
) -> Result<(), CphError> {
let ws = self.get_workspace(workspace)?;
ws.initial_connector.set(connector);
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));
}
}
fn handle_request_(self: &Rc<Self>, msg: &[u8]) -> Result<(), CphError> {
let request = match deserialize_client_message(msg) {
Ok(msg) => msg,
Err(e) => return Err(CphError::ParsingFailed(e)),
};
match request {
ClientMessage::Log {
level,
msg,
file,
line,
} => self.handle_log_request(level, msg, file, line),
ClientMessage::GetSeat { name } => self.handle_get_seat(name),
ClientMessage::ParseKeymap { keymap } => {
self.handle_parse_keymap(keymap).wrn("parse_keymap")?;
}
ClientMessage::SeatSetKeymap { seat, keymap } => {
self.handle_set_keymap(seat, keymap).wrn("set_keymap")?;
}
ClientMessage::SeatGetRepeatRate { seat } => {
self.handle_get_repeat_rate(seat).wrn("get_repeat_rate")?;
}
ClientMessage::SeatSetRepeatRate { seat, rate, delay } => self
.handle_set_repeat_rate(seat, rate, delay)
.wrn("set_repeat_rate")?,
ClientMessage::SetSeat { device, seat } => {
self.handle_set_seat(device, seat).wrn("set_seat")?;
}
ClientMessage::GetSeatMono { seat } => {
self.handle_get_seat_mono(seat, None).wrn("get_seat_mono")?;
}
ClientMessage::SetSeatMono { seat, mono } => self
.handle_set_seat_mono(seat, None, mono)
.wrn("set_seat_mono")?,
ClientMessage::GetSeatSplit { seat } => self
.handle_get_seat_split(seat, None)
.wrn("get_seat_split")?,
ClientMessage::SetSeatSplit { seat, axis } => self
.handle_set_seat_split(seat, None, axis)
.wrn("set_seat_split")?,
ClientMessage::AddShortcut { seat, mods, sym } => self
.handle_add_shortcut(seat, Modifiers(!0), mods, sym)
.wrn("add_shortcut")?,
ClientMessage::RemoveShortcut { seat, mods, sym } => self
.handle_remove_shortcut(seat, mods, sym)
.wrn("remove_shortcut")?,
ClientMessage::SeatFocus { seat, direction } => {
self.handle_seat_focus(seat, direction).wrn("seat_focus")?;
}
ClientMessage::SeatMove { seat, direction } => {
self.handle_seat_move(seat, direction).wrn("seat_move")?;
}
ClientMessage::GetInputDevices { seat } => self.handle_get_input_devices(seat),
ClientMessage::GetSeats => self.handle_get_seats(),
ClientMessage::RemoveSeat { .. } => {}
ClientMessage::Run { prog, args, env } => {
self.handle_run(prog, args, env, vec![], None).wrn("run")?;
}
ClientMessage::GrabKb { kb, grab } => self.handle_grab(kb, grab).wrn("grab")?,
ClientMessage::SetColor { colorable, color } => {
self.handle_set_color(colorable, color).wrn("set_color")?;
}
ClientMessage::GetColor { colorable } => {
self.handle_get_color(colorable).wrn("get_color")?;
}
ClientMessage::CreateSeatSplit { seat, axis } => self
.handle_create_seat_split(seat, axis)
.wrn("create_seat_split")?,
ClientMessage::FocusSeatParent { seat } => self
.handle_focus_seat_parent(seat)
.wrn("focus_seat_parent")?,
ClientMessage::GetSeatFloating { seat } => self
.handle_get_seat_floating(seat)
.wrn("get_seat_floating")?,
ClientMessage::SetSeatFloating { seat, floating } => self
.handle_set_seat_floating(seat, floating)
.wrn("set_seat_floating")?,
ClientMessage::Quit => self.handle_quit(),
ClientMessage::SwitchTo { vtnr } => self.handle_switch_to(vtnr),
ClientMessage::HasCapability { device, cap } => self
.handle_has_capability(device, cap)
.wrn("has_capability")?,
ClientMessage::SetLeftHanded {
device,
left_handed,
} => self
.handle_set_left_handed(device, left_handed)
.wrn("set_left_handed")?,
ClientMessage::SetAccelProfile { device, profile } => self
.handle_set_accel_profile(device, profile)
.wrn("set_accel_profile")?,
ClientMessage::SetAccelSpeed { device, speed } => self
.handle_set_accel_speed(device, speed)
.wrn("set_accel_speed")?,
ClientMessage::SetTransformMatrix { device, matrix } => self
.handle_set_transform_matrix(device, matrix)
.wrn("set_transform_matrix")?,
ClientMessage::GetDeviceName { device } => {
self.handle_get_device_name(device).wrn("get_device_name")?;
}
ClientMessage::GetWorkspace { name } => self.handle_get_workspace(name),
ClientMessage::ShowWorkspace { seat, workspace } => self
.handle_show_workspace(seat, workspace, None)
.wrn("show_workspace")?,
ClientMessage::SetSeatWorkspace { seat, workspace } => self
.handle_set_seat_workspace(seat, workspace)
.wrn("set_seat_workspace")?,
ClientMessage::GetConnector { ty, idx } => {
self.handle_get_connector(ty, idx);
}
ClientMessage::ConnectorConnected { connector } => self
.handle_connector_connected(connector)
.wrn("connector_connected")?,
ClientMessage::ConnectorType { connector } => self
.handle_connector_type(connector)
.wrn("connector_type")?,
ClientMessage::ConnectorMode { connector } => self
.handle_connector_mode(connector)
.wrn("connector_mode")?,
ClientMessage::ConnectorSetPosition { connector, x, y } => self
.handle_connector_set_position(connector, x, y)
.wrn("connector_set_position")?,
ClientMessage::ConnectorSetEnabled { connector, enabled } => self
.handle_connector_set_enabled(connector, enabled)
.wrn("connector_set_enabled")?,
ClientMessage::SeatClose { seat } => self.handle_seat_close(seat).wrn("seat_close")?,
ClientMessage::SetStatus { status } => self.handle_set_status(status),
ClientMessage::GetTimer { name } => self.handle_get_timer(name).wrn("get_timer")?,
ClientMessage::RemoveTimer { timer } => {
self.handle_remove_timer(timer).wrn("remove_timer")?;
}
ClientMessage::ProgramTimer {
timer,
initial,
periodic,
} => self
.handle_program_timer(timer, initial, periodic)
.wrn("program_timer")?,
ClientMessage::SetEnv { key, val } => self.handle_set_env(key, val),
ClientMessage::SetSeatFullscreen { seat, fullscreen } => self
.handle_set_seat_fullscreen(seat, fullscreen)
.wrn("set_seat_fullscreen")?,
ClientMessage::GetSeatFullscreen { seat } => self
.handle_get_seat_fullscreen(seat)
.wrn("get_seat_fullscreen")?,
ClientMessage::Reload => self.handle_reload(),
ClientMessage::GetDeviceConnectors { device } => self
.handle_get_connectors(Some(device), false)
.wrn("get_device_connectors")?,
ClientMessage::GetDrmDeviceSyspath { device } => self
.handle_get_drm_device_syspath(device)
.wrn("get_drm_device_syspath")?,
ClientMessage::GetDrmDeviceVendor { device } => self
.handle_get_drm_device_vendor(device)
.wrn("get_drm_device_vendor")?,
ClientMessage::GetDrmDeviceModel { device } => self
.handle_get_drm_device_model(device)
.wrn("get_drm_device_model")?,
ClientMessage::GetDrmDevices => self.handle_get_drm_devices(),
ClientMessage::GetDrmDevicePciId { device } => self
.handle_get_drm_device_pci_id(device)
.wrn("get_drm_device_pci_id")?,
ClientMessage::ResetColors => self.handle_reset_colors(),
ClientMessage::ResetSizes => self.handle_reset_sizes(),
ClientMessage::GetSize { sized } => self.handle_get_size(sized).wrn("get_size")?,
ClientMessage::SetSize { sized, size } => {
self.handle_set_size(sized, size).wrn("set_size")?;
}
ClientMessage::ResetFont => self.handle_reset_font(),
ClientMessage::GetFont => self.handle_get_font(),
ClientMessage::SetFont { font } => self.handle_set_font(font),
ClientMessage::SetPxPerWheelScroll { device, px } => self
.handle_set_px_per_wheel_scroll(device, px)
.wrn("set_px_per_wheel_scroll")?,
ClientMessage::SetPxScrollMultiplier { device, mul } => self
.handle_set_px_scroll_multiplier(device, mul)
.wrn("set_px_scroll_multiplier")?,
ClientMessage::ConnectorSetScale { connector, scale } => self
.handle_connector_set_scale(connector, scale)
.wrn("connector_set_scale")?,
ClientMessage::ConnectorGetScale { connector } => self
.handle_connector_get_scale(connector)
.wrn("connector_get_scale")?,
ClientMessage::ConnectorSize { connector } => self
.handle_connector_size(connector)
.wrn("connector_size")?,
ClientMessage::SetCursorSize { seat, size } => self
.handle_set_cursor_size(seat, size)
.wrn("set_cursor_size")?,
ClientMessage::SetTapEnabled { device, enabled } => self
.handle_set_tap_enabled(device, enabled)
.wrn("set_tap_enabled")?,
ClientMessage::SetDragEnabled { device, enabled } => self
.handle_set_drag_enabled(device, enabled)
.wrn("set_drag_enabled")?,
ClientMessage::SetDragLockEnabled { device, enabled } => self
.handle_set_drag_lock_enabled(device, enabled)
.wrn("set_drag_lock_enabled")?,
ClientMessage::SetUseHardwareCursor {
seat,
use_hardware_cursor,
} => self
.handle_set_use_hardware_cursor(seat, use_hardware_cursor)
.wrn("set_use_hardware_cursor")?,
ClientMessage::DisablePointerConstraint { seat } => self
.handle_disable_pointer_constraint(seat)
.wrn("disable_pointer_constraint")?,
ClientMessage::MakeRenderDevice { device } => self
.handle_make_render_device(device)
.wrn("make_render_device")?,
ClientMessage::GetSeatCursorWorkspace { seat } => self
.handle_get_seat_cursor_workspace(seat)
.wrn("get_seat_cursor_workspace")?,
ClientMessage::GetSeatKeyboardWorkspace { seat } => self
.handle_get_seat_keyboard_workspace(seat)
.wrn("get_seat_keyboard_workspace")?,
ClientMessage::SetDefaultWorkspaceCapture { capture } => {
self.handle_set_default_workspace_capture(capture);
}
ClientMessage::GetDefaultWorkspaceCapture => {
self.handle_get_default_workspace_capture();
}
ClientMessage::SetWorkspaceCapture { workspace, capture } => self
.handle_set_workspace_capture(workspace, capture)
.wrn("set_workspace_capture")?,
ClientMessage::GetWorkspaceCapture { workspace } => self
.handle_get_workspace_capture(workspace)
.wrn("get_workspace_capture")?,
ClientMessage::SetNaturalScrollingEnabled { device, enabled } => self
.handle_set_natural_scrolling_enabled(device, enabled)
.wrn("set_natural_scrolling_enabled")?,
ClientMessage::SetGfxApi { device, api } => {
self.handle_set_gfx_api(device, api).wrn("set_gfx_api")?;
}
ClientMessage::SetDirectScanoutEnabled { device, enabled } => self
.handle_set_direct_scanout_enabled(device, enabled)
.wrn("set_direct_scanout_enabled")?,
ClientMessage::ConnectorSetTransform {
connector,
transform,
} => self
.handle_connector_set_transform(connector, transform)
.wrn("connector_set_transform")?,
ClientMessage::SetDoubleClickIntervalUsec { usec } => {
self.handle_set_double_click_interval_usec(usec);
}
ClientMessage::SetDoubleClickDistance { dist } => {
self.handle_set_double_click_distance(dist);
}
ClientMessage::ConnectorModes { connector } => self
.handle_connector_modes(connector)
.wrn("connector_modes")?,
ClientMessage::ConnectorSetMode { connector, mode } => self
.handle_connector_set_mode(connector, mode)
.wrn("connector_set_mode")?,
ClientMessage::AddPollable { fd } => {
self.handle_add_pollable(fd);
}
ClientMessage::RemovePollable { id } => self.handle_remove_pollable(id),
ClientMessage::AddInterest { pollable, writable } => self
.handle_add_interest(pollable, writable)
.wrn("add_interest")?,
ClientMessage::Run2 {
prog,
args,
env,
fds,
} => self.handle_run(prog, args, env, fds, None).wrn("run")?,
ClientMessage::DisableDefaultSeat => self.state.create_default_seat.set(false),
ClientMessage::DestroyKeymap { keymap } => self.handle_destroy_keymap(keymap),
ClientMessage::GetConnectorName { connector } => self
.handle_connector_name(connector)
.wrn("connector_name")?,
ClientMessage::GetConnectorModel { connector } => self
.handle_connector_model(connector)
.wrn("connector_model")?,
ClientMessage::GetConnectorManufacturer { connector } => self
.handle_connector_manufacturer(connector)
.wrn("connector_manufacturer")?,
ClientMessage::GetConnectorSerialNumber { connector } => self
.handle_connector_serial_number(connector)
.wrn("connector_serial_number")?,
ClientMessage::GetConnectors {
device,
connected_only,
} => self
.handle_get_connectors(device, connected_only)
.wrn("get_connectors")?,
ClientMessage::ConnectorGetPosition { connector } => self
.handle_connector_get_position(connector)
.wrn("connector_get_position")?,
ClientMessage::GetConfigDir => self.handle_get_config_dir(),
ClientMessage::GetWorkspaces => self.handle_get_workspaces(),
ClientMessage::UnsetEnv { key } => self.handle_unset_env(key),
ClientMessage::SetLogLevel { level } => self.handle_set_log_level(level),
ClientMessage::GetDrmDeviceDevnode { device } => self
.handle_get_drm_device_devnode(device)
.wrn("get_drm_device_devnode")?,
ClientMessage::GetInputDeviceSyspath { device } => self
.handle_get_input_device_syspath(device)
.wrn("get_input_device_syspath")?,
ClientMessage::GetInputDeviceDevnode { device } => self
.handle_get_input_device_devnode(device)
.wrn("get_input_device_devnode")?,
ClientMessage::SetIdle { timeout } => self.handle_set_idle(timeout),
ClientMessage::MoveToOutput {
workspace,
connector,
} => self
.handle_move_to_output(workspace, connector)
.wrn("move_to_output")?,
ClientMessage::SetExplicitSyncEnabled { enabled } => {
self.handle_set_explicit_sync_enabled(enabled);
}
ClientMessage::GetSocketPath => self.handle_get_socket_path(),
ClientMessage::DeviceSetKeymap { device, keymap } => self
.handle_set_device_keymap(device, keymap)
.wrn("set_device_keymap")?,
ClientMessage::SetForward { seat, forward } => {
self.handle_set_forward(seat, forward).wrn("set_forward")?;
}
ClientMessage::AddShortcut2 {
seat,
mod_mask,
mods,
sym,
} => self
.handle_add_shortcut(seat, mod_mask, mods, sym)
.wrn("add_shortcut")?,
ClientMessage::SetFocusFollowsMouseMode { seat, mode } => self
.handle_set_focus_follows_mouse_mode(seat, mode)
.wrn("set_focus_follows_mouse_mode")?,
ClientMessage::SetInputDeviceConnector {
input_device,
connector,
} => self
.handle_set_input_device_connector(input_device, connector)
.wrn("set_input_device_connector")?,
ClientMessage::RemoveInputMapping { input_device } => self
.handle_remove_input_mapping(input_device)
.wrn("remove_input_mapping")?,
ClientMessage::SetWindowManagementEnabled { seat, enabled } => self
.handle_set_window_management_enabled(seat, enabled)
.wrn("set_window_management_enabled")?,
ClientMessage::SetVrrMode { connector, mode } => self
.handle_set_vrr_mode(connector, mode)
.wrn("set_vrr_mode")?,
ClientMessage::SetVrrCursorHz { connector, hz } => self
.handle_set_vrr_cursor_hz(connector, hz)
.wrn("set_vrr_cursor_hz")?,
ClientMessage::SetTearingMode { connector, mode } => self
.handle_set_tearing_mode(connector, mode)
.wrn("set_tearing_mode")?,
ClientMessage::SetCalibrationMatrix { device, matrix } => self
.handle_set_calibration_matrix(device, matrix)
.wrn("set_calibration_matrix")?,
ClientMessage::SetEiSocketEnabled { enabled } => {
self.handle_set_ei_socket_enabled(enabled);
}
ClientMessage::ConnectorSetFormat { connector, format } => self
.handle_connector_set_format(connector, format)
.wrn("connector_set_format")?,
ClientMessage::SetFlipMargin { device, margin } => self
.handle_set_flip_margin(device, margin)
.wrn("set_flip_margin")?,
ClientMessage::SetUiDragEnabled { enabled } => self.handle_set_ui_drag_enabled(enabled),
ClientMessage::SetUiDragThreshold { threshold } => {
self.handle_set_ui_drag_threshold(threshold);
}
ClientMessage::SetXScalingMode { mode } => self
.handle_set_x_scaling_mode(mode)
.wrn("set_x_scaling_mode")?,
ClientMessage::SetIdleGracePeriod { period } => {
self.handle_set_idle_grace_period(period);
}
ClientMessage::SetColorManagementEnabled { enabled } => {
self.handle_set_color_management_enabled(enabled);
}
ClientMessage::ConnectorSetColors {
connector,
color_space,
eotf,
} => self
.handle_connector_set_colors(connector, color_space, eotf)
.wrn("connector_set_colors")?,
ClientMessage::ConnectorSetBrightness {
connector,
brightness,
} => self
.handle_connector_set_brightness(connector, brightness)
.wrn("connector_set_brightness")?,
ClientMessage::SetFloatAboveFullscreen { above } => {
self.handle_set_float_above_fullscreen(above);
}
ClientMessage::GetFloatAboveFullscreen => self.handle_get_float_above_fullscreen(),
ClientMessage::GetSeatFloatPinned { seat } => self
.handle_get_seat_float_pinned(seat)
.wrn("get_seat_float_pinned")?,
ClientMessage::SetSeatFloatPinned { seat, pinned } => self
.handle_set_seat_float_pinned(seat, pinned)
.wrn("set_seat_float_pinned")?,
ClientMessage::SetShowFloatPinIcon { show } => {
self.handle_set_show_float_pin_icon(show);
}
ClientMessage::GetConnectorActiveWorkspace { connector } => self
.handle_get_connector_active_workspace(connector)
.wrn("get_connector_active_workspace")?,
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
.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, None)
.wrn("get_window_split")?,
ClientMessage::SetWindowSplit { window, axis } => self
.handle_set_window_split(window, None, axis)
.wrn("set_window_split")?,
ClientMessage::GetWindowMono { window } => self
.handle_get_window_mono(window, None)
.wrn("get_window_mono")?,
ClientMessage::SetWindowMono { window, mono } => self
.handle_set_window_mono(window, None, 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")?,
ClientMessage::CreateClientMatcher { criterion } => self
.handle_create_client_matcher(criterion)
.wrn("create_window_matcher")?,
ClientMessage::DestroyClientMatcher { matcher } => {
self.handle_destroy_client_matcher(matcher);
}
ClientMessage::EnableClientMatcherEvents { matcher } => self
.handle_enable_client_matcher_events(matcher)
.wrn("enable_window_matcher_events")?,
ClientMessage::CreateWindowMatcher { criterion } => self
.handle_create_window_matcher(criterion)
.wrn("create_window_matcher")?,
ClientMessage::DestroyWindowMatcher { matcher } => {
self.handle_destroy_window_matcher(matcher);
}
ClientMessage::EnableWindowMatcherEvents { matcher } => self
.handle_enable_window_matcher_events(matcher)
.wrn("enable_window_matcher_events")?,
ClientMessage::SetWindowMatcherAutoFocus {
matcher,
auto_focus,
} => self
.handle_set_window_matcher_auto_focus(matcher, auto_focus)
.wrn("set_window_matcher_auto_focus")?,
ClientMessage::SetWindowMatcherInitialTileState {
matcher,
tile_state,
} => self
.handle_set_window_matcher_initial_tile_state(matcher, tile_state)
.wrn("set_window_matcher_initial_tile_state")?,
ClientMessage::SetWindowMatcherInitialFloatingSize {
matcher,
width,
height,
} => self
.handle_set_window_matcher_initial_floating_size(matcher, width, height)
.wrn("set_window_matcher_initial_floating_size")?,
ClientMessage::SetWindowMatcherInitialFloatingPosition { matcher, x, y } => self
.handle_set_window_matcher_initial_floating_position(matcher, x, y)
.wrn("set_window_matcher_initial_floating_position")?,
ClientMessage::SetPointerRevertKey { seat, key } => self
.handle_set_pointer_revert_key(seat, key)
.wrn("set_pointer_revert_key")?,
ClientMessage::SetClickMethod { device, method } => self
.handle_set_click_method(device, method)
.wrn("set_click_method")?,
ClientMessage::SetMiddleButtonEmulationEnabled { device, enabled } => self
.handle_set_middle_button_emulation_enabled(device, enabled)
.wrn("set_middle_button_emulation_enabled")?,
ClientMessage::GetContentType { window } => self
.handle_get_content_type(window)
.wrn("get_content_type")?,
ClientMessage::SetSplitReusesContainer { reuse } => {
self.handle_set_split_reuses_container(reuse);
}
ClientMessage::GetSplitReusesContainer => self.handle_get_split_reuses_container(),
ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show),
ClientMessage::GetShowBar => self.handle_get_show_bar(),
ClientMessage::SetShowTitles { show } => self.handle_set_show_titles(show),
ClientMessage::GetShowTitles => self.handle_get_show_titles(),
ClientMessage::SetBarPosition { position } => self.handle_set_bar_position(position),
ClientMessage::GetBarPosition => self.handle_get_bar_position(),
ClientMessage::SeatFocusHistory { seat, timeline } => self
.handle_seat_focus_history(seat, timeline)
.wrn("seat_focus_history")?,
ClientMessage::SeatFocusHistorySetOnlyVisible { seat, only_visible } => self
.handle_seat_focus_history_set_only_visible(seat, only_visible)
.wrn("seat_focus_history_set_only_visible")?,
ClientMessage::SeatFocusHistorySetSameWorkspace {
seat,
same_workspace,
} => self
.handle_seat_focus_history_set_same_workspace(seat, same_workspace)
.wrn("seat_focus_history_set_same_workspace")?,
ClientMessage::SeatFocusLayerRel { seat, direction } => self
.handle_seat_focus_layer_rel(seat, direction)
.wrn("seat_focus_layer_rel")?,
ClientMessage::SeatFocusTiles { seat } => {
self.handle_seat_focus_tiles(seat).wrn("seat_focus_tiles")?;
}
ClientMessage::SetMiddleClickPasteEnabled { enabled } => {
self.handle_set_middle_click_paste_enabled(enabled);
}
ClientMessage::SetWorkspaceDisplayOrder { order } => {
self.handle_set_workspace_display_order(order);
}
ClientMessage::SeatCreateMark { seat, kc } => self
.handle_seat_create_mark(seat, kc)
.wrn("seat_create_mark")?,
ClientMessage::SeatJumpToMark { seat, kc } => self
.handle_seat_jump_to_mark(seat, kc)
.wrn("seat_jump_to_mark")?,
ClientMessage::SeatCopyMark { seat, src, dst } => self
.handle_seat_copy_mark(seat, src, dst)
.wrn("seat_copy_mark")?,
ClientMessage::ConnectorSetBlendSpace {
connector,
blend_space,
} => self
.handle_connector_set_blend_space(connector, blend_space)
.wrn("connector_set_blend_space")?,
ClientMessage::SetBarFont { font } => self.handle_set_bar_font(font),
ClientMessage::SetTitleFont { font } => self.handle_set_title_font(font),
ClientMessage::SetClientMatcherCapabilities { matcher, caps } => self
.handle_set_client_matcher_capabilities(matcher, caps)
.wrn("set_client_matcher_capabilities")?,
ClientMessage::SetClientMatcherBoundingCapabilities { matcher, caps } => self
.handle_set_client_matcher_bounding_capabilities(matcher, caps)
.wrn("set_client_matcher_bounding_capabilities")?,
ClientMessage::ShowWorkspaceOn {
seat,
workspace,
connector,
} => self
.handle_show_workspace(seat, workspace, Some(connector))
.wrn("show_workspace_on")?,
ClientMessage::SeatSetSimpleImEnabled { seat, enabled } => self
.handle_seat_set_simple_im_enabled(seat, enabled)
.wrn("seat_set_simple_im_enabled")?,
ClientMessage::SeatGetSimpleImEnabled { seat } => self
.handle_seat_get_simple_im_enabled(seat)
.wrn("seat_get_simple_im_enabled")?,
ClientMessage::SeatReloadSimpleIm { seat } => self
.handle_seat_reload_simple_im(seat)
.wrn("seat_reload_simple_im")?,
ClientMessage::SeatEnableUnicodeInput { seat } => self
.handle_seat_enable_unicode_input(seat)
.wrn("seat_enable_unicode_input")?,
ClientMessage::SeatWarpMouseToFocus { seat } => self
.handle_seat_warp_mouse_to_focus(seat)
.wrn("seat_warp_mouse_to_focus")?,
ClientMessage::SeatSetMouseFollowsFocus { seat, enabled } => self
.handle_seat_set_mouse_follows_focus(seat, enabled)
.wrn("seat_set_mouse_follows_focus")?,
ClientMessage::ConnectorSetUseNativeGamut {
connector,
use_native_gamut,
} => self
.handle_connector_set_use_native_gamut(connector, use_native_gamut)
.wrn("connector_set_use_native_gamut")?,
ClientMessage::KeymapFromNames {
rules,
model,
groups,
options,
} => self
.handle_keymap_from_names(rules, model, groups, options)
.wrn("keymap_from_names")?,
ClientMessage::SetFallbackOutputMode { seat, mode } => self
.handle_set_fallback_output_mode(seat, mode)
.wrn("set_fallback_output_mode")?,
ClientMessage::SetXWaylandEnabled { enabled } => {
self.handle_set_x_wayland_enabled(enabled);
}
ClientMessage::Run3 {
prog,
args,
env,
fds,
tag,
} => self.handle_run(prog, args, env, fds, tag).wrn("run")?,
ClientMessage::SetEguiFonts {
proportional,
monospace,
} => self.handle_set_egui_fonts(proportional, monospace),
ClientMessage::OpenControlCenter => self.handle_open_control_center(),
ClientMessage::ConnectorSupportsArbitraryModes { connector } => self
.handle_connector_supports_arbitrary_modes(connector)
.wrn("connector_supports_arbitrary_modes")?,
ClientMessage::GetConnectorByName { name } => self.handle_get_connector_by_name(name),
ClientMessage::CreateVirtualOutput { name } => self.handle_create_virtual_output(name),
ClientMessage::RemoveVirtualOutput { name } => self.handle_remove_virtual_output(name),
ClientMessage::CleanLogsOlderThan { time } => self.handle_clean_logs_older_than(time),
ClientMessage::WindowResize {
window,
dx1,
dy1,
dx2,
dy2,
} => self
.handle_window_resize(window, dx1, dy1, dx2, dy2)
.wrn("window_resize")?,
ClientMessage::GetWindowPosition { window } => self
.handle_get_window_position(window)
.wrn("get_window_position")?,
ClientMessage::GetWorkspacePosition { workspace } => self
.handle_get_workspace_position(workspace)
.wrn("get_workspace_position")?,
ClientMessage::SetSessionManagementEnabled { enabled } => {
self.handle_set_session_management_enabled(enabled);
}
ClientMessage::GetSeatCursorConnector { seat } => self
.handle_get_seat_cursor_connector(seat)
.wrn("get_seat_cursor_connector")?,
ClientMessage::GetSeatKeyboardConnector { seat } => self
.handle_get_seat_keyboard_connector(seat)
.wrn("get_seat_keyboard_connector")?,
ClientMessage::ConnectorCompositorOutput { connector } => {
self.handle_connector_compositor_output(connector);
}
ClientMessage::ShowWorkspace3 { v1 } => {
self.handle_show_workspace_3(v1).wrn("show_workspace_3")?;
}
ClientMessage::ShowWorkspace4 { v1, v2 } => self
.handle_show_workspace_4(v1, v2)
.wrn("show_workspace_4")?,
ClientMessage::GetOverlay { name } => self.handle_get_overlay(name),
ClientMessage::HideOverlays => self.handle_hide_overlays(),
ClientMessage::HideWorkspace { workspace } => self
.handle_hide_workspace(workspace)
.wrn("hide_workspace")?,
ClientMessage::GetWorkspaceKind { workspace } => self
.handle_get_workspace_kind(workspace)
.wrn("get_workspace_kind")?,
ClientMessage::ParseKeymap2 { v1 } => {
self.handle_parse_keymap_2(v1).wrn("parse_keymap_2")?;
}
ClientMessage::SetShowWindowIcons { show } => self.state.set_show_window_icons(show),
ClientMessage::SetWindowIconsGrayscale { grayscale } => {
self.state.set_window_icons_grayscale(grayscale);
}
ClientMessage::SetVisualizeCompositing { visualize } => {
self.handle_set_visualize_compositing(visualize);
}
ClientMessage::GetVisualizeCompositing => self.handle_get_visualize_compositing(),
ClientMessage::SetTransactionTimeout { timeout } => {
self.handle_set_transaction_timeout(timeout);
}
ClientMessage::SetConfigureTimeout { timeout } => {
self.handle_set_configure_timeout(timeout);
}
ClientMessage::SetContainerBorders { borders } => {
self.handle_set_container_borders(borders);
}
ClientMessage::GetContainerBorders => self.handle_get_container_borders(),
ClientMessage::SetWorkspaceInitialConnector {
workspace,
connector,
} => self
.handle_set_workspace_initial_connector(workspace, connector)
.wrn("set_workspace_initial_connector")?,
ClientMessage::SetScrollMethod { device, method } => self
.handle_set_scroll_method(device, method)
.wrn("set_scroll_method")?,
ClientMessage::SetScrollButton { device, button } => self
.handle_set_scroll_button(device, button)
.wrn("set_scroll_button")?,
ClientMessage::SetScrollButtonLock { device, enabled } => self
.handle_set_scroll_button_lock(device, enabled)
.wrn("set_scroll_button_lock")?,
ClientMessage::SetRepeatShortcut {
seat,
mods,
sym,
repeat,
} => self
.handle_set_shortcut_repeat(seat, mods, sym, repeat)
.wrn("set_shortcut_repeat")?,
ClientMessage::EnableLockedShortcuts => self.state.config_locked_shortcuts.set(true),
ClientMessage::ConnectorSetScalingFilter {
connector,
scaling_filter,
} => self
.handle_connector_set_scaling_filter(connector, scaling_filter)
.wrn("set_scaling_filter")?,
ClientMessage::SetPlaneColorPipelinesEnabled { device, enabled } => self
.handle_set_plane_color_pipelines_enabled(device, enabled)
.wrn("set_plane_color_pipelines_enabled")?,
ClientMessage::GetPlaneColorPipelinesEnabled { device } => self
.handle_get_plane_color_pipelines_enabled(device)
.wrn("get_plane_color_pipelines_enabled")?,
ClientMessage::GetSeatContainerMono { seat, target } => self
.handle_get_seat_mono(seat, Some(target))
.wrn("get_seat_container_mono")?,
ClientMessage::SetSeatContainerMono { seat, target, mono } => self
.handle_set_seat_mono(seat, Some(target), mono)
.wrn("set_seat_container_mono")?,
ClientMessage::GetSeatContainerSplit { seat, target } => self
.handle_get_seat_split(seat, Some(target))
.wrn("get_seat_container_split")?,
ClientMessage::SetSeatContainerSplit { seat, target, axis } => self
.handle_set_seat_split(seat, Some(target), axis)
.wrn("set_seat_container_split")?,
ClientMessage::GetWindowContainerMono { window, target } => self
.handle_get_window_mono(window, Some(target))
.wrn("get_window_container_mono")?,
ClientMessage::SetWindowContainerMono {
window,
target,
mono,
} => self
.handle_set_window_mono(window, Some(target), mono)
.wrn("set_window_container_mono")?,
ClientMessage::GetWindowContainerSplit { window, target } => self
.handle_get_window_split(window, Some(target))
.wrn("get_window_container_split")?,
ClientMessage::SetWindowContainerSplit {
window,
target,
axis,
} => self
.handle_set_window_split(window, Some(target), axis)
.wrn("set_window_container_split")?,
ClientMessage::CreateSeatSplitRelative { seat, axis } => self
.handle_create_seat_split_relative(seat, axis)
.wrn("create_seat_split_relative")?,
ClientMessage::CreateWindowSplitRelative { window, axis } => self
.handle_create_window_split_relative(window, axis)
.wrn("create_window_split_relative")?,
ClientMessage::SetSeatContainerSplitRelative { seat, target, axis } => self
.handle_set_seat_split_relative(seat, target, axis)
.wrn("set_seat_container_split_relative")?,
ClientMessage::SetWindowContainerSplitRelative {
window,
target,
axis,
} => self
.handle_set_window_split_relative(window, target, axis)
.wrn("set_window_container_split_relative")?,
ClientMessage::ResetWindowTheme { window, kind } => self
.handle_reset_window_theme(window, kind)
.wrn("reset_window_theme")?,
ClientMessage::SetWindowThemeColor {
window,
kind,
colorable,
color,
} => self
.handle_set_window_theme_color(window, kind, colorable, color)
.wrn("set_window_theme_color")?,
ClientMessage::GetWindowThemeColor {
window,
kind,
colorable,
} => self
.handle_get_window_theme_color(window, kind, colorable)
.wrn("get_window_theme_color")?,
ClientMessage::SetWindowThemeSize {
window,
kind,
sized,
size,
} => self
.handle_set_window_theme_size(window, kind, sized, size)
.wrn("set_window_theme_size")?,
ClientMessage::GetWindowThemeSize {
window,
kind,
sized,
} => self
.handle_get_window_theme_size(window, kind, sized)
.wrn("get_window_theme_size")?,
ClientMessage::SetWindowThemeShowTitles { window, kind, show } => self
.handle_set_window_theme_show_titles(window, kind, show)
.wrn("set_window_theme_show_titles")?,
ClientMessage::GetWindowThemeShowTitles { window, kind } => self
.handle_get_window_theme_show_titles(window, kind)
.wrn("get_window_theme_show_titles")?,
ClientMessage::SetWindowThemeShowWindowIcons { window, kind, show } => self
.handle_set_window_theme_show_window_icons(window, kind, show)
.wrn("set_window_theme_show_window_icons")?,
ClientMessage::SetWindowThemeWindowIconsGrayscale {
window,
kind,
grayscale,
} => self
.handle_set_window_theme_window_icons_grayscale(window, kind, grayscale)
.wrn("set_window_theme_window_icons_grayscale")?,
ClientMessage::SetWindowThemeTitleFont { window, kind, font } => self
.handle_set_window_theme_title_font(window, kind, font)
.wrn("set_window_theme_title_font")?,
ClientMessage::SetWindowThemeContainerBorders {
window,
kind,
borders,
} => self
.handle_set_window_theme_container_borders(window, kind, borders)
.wrn("set_window_theme_container_borders")?,
ClientMessage::GetWindowThemeContainerBorders { window, kind } => self
.handle_get_window_theme_container_borders(window, kind)
.wrn("get_window_theme_container_borders")?,
}
Ok(())
}
fn window_property<T>(
&self,
data: &ToplevelData,
get: impl Fn(&WindowMatcherProperties) -> Option<T>,
) -> Option<T> {
for properties in self.window_matcher_properties.lock().values() {
if let Some(value) = get(properties)
&& properties.criterion.node.pull(data)
{
return Some(value);
}
}
None
}
pub fn auto_focus(&self, data: &ToplevelData) -> bool {
self.window_property(data, |p| p.auto_focus.get().filter(|&af| !af))
.unwrap_or(true)
}
pub fn initial_tile_state(&self, data: &ToplevelData) -> Option<TileState> {
self.window_property(data, |p| p.initial_tile_state.get())
}
pub fn initial_floating_size(&self, data: &ToplevelData) -> Option<(i32, i32)> {
self.window_property(data, |p| p.initial_floating_size.get())
}
pub fn initial_floating_position(&self, data: &ToplevelData) -> Option<(i32, i32)> {
self.window_property(data, |p| p.initial_floating_position.get())
}
pub fn initial_output_for_workspace(&self, name: &str) -> Option<Option<Rc<OutputNode>>> {
let ws = self.workspaces_by_name.get(name)?;
let connector = ws.initial_connector.get()?;
Some(self.get_output_node(connector).ok())
}
pub fn update_capabilities(
&self,
data: &Rc<Client>,
bounding_caps: ClientCaps,
set_bounding_caps: bool,
) {
let mut have_caps = false;
let mut have_bounding_caps = false;
let mut caps = ClientCaps::none();
let mut new_bounding_caps = ClientCaps::none();
for (matcher, state) in self.client_matcher_capabilities.lock().values() {
if matcher.node.pull(data) {
have_caps = true;
caps |= *state;
}
}
for (matcher, state) in self.client_matcher_bounding_capabilities.lock().values() {
if matcher.node.pull(data) {
have_bounding_caps = true;
new_bounding_caps |= *state;
}
}
if have_caps {
caps &= bounding_caps;
data.effective_caps.set(caps);
}
if !have_bounding_caps && set_bounding_caps {
have_bounding_caps = true;
new_bounding_caps = data.effective_caps.get();
}
if have_bounding_caps {
new_bounding_caps &= bounding_caps;
data.bounding_caps_for_children.set(new_bounding_caps);
}
}
}
#[derive(Debug, Error)]
enum CphError {
#[error("Tried to set an unknown accel profile: {}", (.0).0)]
UnknownAccelProfile(AccelProfile),
#[error("Queried unknown capability: {}", (.0).0)]
UnknownCapability(Capability),
#[error("Tried to set an unknown click method: {}", (.0).0)]
UnknownClickMethod(ClickMethod),
#[error("The sized {} is outside the valid range [{}, {}] for component {}", .0, .1.min(), .1.max(), .1.name())]
InvalidSize(i32, ThemeSized),
#[error("The ol' forker is not available")]
NoForker,
#[error("Repeat rate is negative")]
NegativeRepeatRate,
#[error("Repeat delay is negative")]
NegativeRepeatDelay,
#[error("Parsing failed")]
ParseKeymapError(#[from] KbvmError),
#[error("Device {0:?} does not exist")]
DeviceDoesNotExist(InputDevice),
#[error("Connector {0:?} does not exist")]
ConnectorDoesNotExist(Connector),
#[error("Timer {0:?} does not exist")]
TimerDoesNotExist(JayTimer),
#[error("Connector {0:?} does not exist or is not connected")]
OutputDoesNotExist(Connector),
#[error("Output {0:?} is not a desktop output")]
OutputIsNotDesktop(Connector),
#[error("{0}x{1} is not a valid connector position")]
InvalidConnectorPosition(i32, i32),
#[error("{0}x{1} is not a valid window size")]
InvalidWindowSize(i32, i32),
#[error("Keymap {0:?} does not exist")]
KeymapDoesNotExist(Keymap),
#[error("Seat {0:?} does not exist")]
SeatDoesNotExist(Seat),
#[error("DRM device {0:?} does not exist")]
DrmDeviceDoesNotExist(DrmDevice),
#[error("Workspace {0:?} does not exist")]
WorkspaceDoesNotExist(Workspace),
#[error("Keyboard {0:?} does not exist")]
KeyboardDoesNotExist(InputDevice),
#[error("Colorable element {0} is not known")]
UnknownColor(u32),
#[error("Sized element {0} is not known")]
UnknownSized(u32),
#[error("Could not parse the message")]
ParsingFailed(#[source] bincode::Error),
#[error("Could not process a `{0}` request")]
FailedRequest(&'static str, #[source] Box<Self>),
#[error(transparent)]
TimerError(#[from] TimerError),
#[error("The requested monitor scale {0} is too small")]
ScaleTooSmall(f64),
#[error("The requested monitor scale {0} is too large")]
ScaleTooLarge(f64),
#[error("Tried to set a negative cursor size")]
NegativeCursorSize,
#[error("Config referred to a pollable that does not exist")]
PollableDoesNotExist,
#[error("Unknown VRR mode {0:?}")]
UnknownVrrMode(ConfigVrrMode),
#[error("Invalid cursor hz {0}")]
InvalidCursorHz(f64),
#[error("Unknown tearing mode {0:?}")]
UnknownTearingMode(ConfigTearingMode),
#[error("The format {0:?} is unknown")]
UnknownFormat(ConfigFormat),
#[error("Unknown x scaling mode {0:?}")]
UnknownXScalingMode(XScalingMode),
#[error("Unknown color space {0:?}")]
UnknownColorSpace(ColorSpace),
#[error("Unknown EOTF {0:?}")]
UnknownEotf(ConfigEotf),
#[error("Client {0:?} does not exist")]
ClientDoesNotExist(ConfigClient),
#[error("Window {0:?} does not exist")]
WindowDoesNotExist(Window),
#[error("Window {0:?} is not visible")]
WindowNotVisible(Window),
#[error("Client matcher {0:?} does not exist")]
ClientMatcherDoesNotExist(ClientMatcher),
#[error("Could not parse regex")]
InvalidRegex(#[source] regex::Error),
#[error("Window matcher {0:?} does not exist")]
WindowMatcherDoesNotExist(WindowMatcher),
#[error("Could not modify the connector state")]
ModifyConnectorState(#[source] BackendConnectorTransactionError),
#[error("Unknown blend space {0:?}")]
UnknownBlendSpace(ConfigBlendSpace),
#[error("Could not create a tagged acceptor")]
CreateTaggedAcceptor(#[source] TaggedAcceptorError),
#[error("Keymap must be defined through a map or rmlvo names")]
MissingKeymapKind,
#[error("Tried to set an unknown scroll method: {}", (.0).0)]
UnknownScrollMethod(ScrollMethod),
#[error("Tried to set an unknown scroll button: {}", (.0).0)]
UnknownScrollButton(ConfigInputEventCode),
#[error("Tried to set an unknown scaling filter: {}", (.0).0)]
UnknownScalingFilter(ConfigScalingFilter),
#[error("Colorable element {0} is not supported in window themes")]
UnsupportedWindowThemeColor(u32),
#[error("Sized element {0} is not supported in window themes")]
UnsupportedWindowThemeSized(u32),
}
trait WithRequestName {
fn wrn(self, request: &'static str) -> Result<(), CphError>;
}
impl WithRequestName for Result<(), CphError> {
fn wrn(self, request: &'static str) -> Result<(), CphError> {
self.map_err(move |e| CphError::FailedRequest(request, Box::new(e)))
}
}
trait ClientCapabilitiesExt {
fn to_client_caps(self) -> ClientCaps;
}
impl ClientCapabilitiesExt for ClientCapabilities {
fn to_client_caps(self) -> ClientCaps {
ClientCaps(self.0 as u32) & !CAP_JAY_COMPOSITOR & ClientCaps::all()
}
}
fn map_container_target_opt(target: Option<JcContainerTarget>) -> ContainerTarget {
let Some(target) = target else {
return ContainerTarget::Parent;
};
map_container_target(target)
}
fn map_container_target(target: JcContainerTarget) -> ContainerTarget {
match target {
JcContainerTarget::Parent => ContainerTarget::Parent,
JcContainerTarget::Itself => ContainerTarget::Itself,
JcContainerTarget::Auto => ContainerTarget::Auto,
}
}
fn map_relative_axis(axis: JcRelativeAxis) -> RelativeAxis {
match axis {
JcRelativeAxis::Major => RelativeAxis::Major,
JcRelativeAxis::Minor => RelativeAxis::Minor,
}
}
fn map_window_theme_kind(kind: WindowThemeKind) -> ToplevelThemeType {
match kind {
WindowThemeKind::ParentTheme => ToplevelThemeType::ParentTheme,
WindowThemeKind::SelfTheme => ToplevelThemeType::SelfTheme,
}
}