Merge branch 'master_jammy' into applibrary-laucher_jammy
This commit is contained in:
commit
fa25b9f1a4
17 changed files with 1624 additions and 763 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1311,7 +1311,7 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/pop-os/smithay?branch=main#78cfeb604a0563c2357a2ecd1ee048b71f96e1a4"
|
source = "git+https://github.com/pop-os/smithay?branch=main#9a2273f4c8fed28d20952a5493627edf9d3cdcfc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,10 @@
|
||||||
(modifiers: [Logo], key: "j"): Focus(Down),
|
(modifiers: [Logo], key: "j"): Focus(Down),
|
||||||
(modifiers: [Logo], key: "k"): Focus(Up),
|
(modifiers: [Logo], key: "k"): Focus(Up),
|
||||||
(modifiers: [Logo], key: "l"): Focus(Right),
|
(modifiers: [Logo], key: "l"): Focus(Right),
|
||||||
//TODO: automatic orientation with Logo+o toggling
|
//TODO: automatic orientation with Logo+o toggling
|
||||||
(modifiers: [Logo], key: "v"): Orientation(Vertical),
|
(modifiers: [Logo], key: "v"): Orientation(Vertical),
|
||||||
(modifiers: [Logo], key: "o"): Orientation(Horizontal),
|
(modifiers: [Logo], key: "o"): Orientation(Horizontal),
|
||||||
|
(modifiers: [Logo, Shift], key: "f"): Fullscreen,
|
||||||
//TODO: ability to select default web browser
|
//TODO: ability to select default web browser
|
||||||
(modifiers: [Logo], key: "b"): Spawn("firefox"),
|
(modifiers: [Logo], key: "b"): Spawn("firefox"),
|
||||||
//TODO: ability to select default file browser
|
//TODO: ability to select default file browser
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use smithay::{
|
||||||
allocator::{gbm::GbmDevice, Format},
|
allocator::{gbm::GbmDevice, Format},
|
||||||
drm::{DrmDevice, DrmEvent, DrmEventTime, DrmNode, GbmBufferedSurface, NodeType},
|
drm::{DrmDevice, DrmEvent, DrmEventTime, DrmNode, GbmBufferedSurface, NodeType},
|
||||||
egl::{EGLContext, EGLDevice, EGLDisplay},
|
egl::{EGLContext, EGLDevice, EGLDisplay},
|
||||||
|
input::InputEvent,
|
||||||
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
||||||
renderer::{
|
renderer::{
|
||||||
multigpu::{egl::EglGlesBackend, GpuManager},
|
multigpu::{egl::EglGlesBackend, GpuManager},
|
||||||
|
|
@ -34,7 +35,7 @@ use smithay::{
|
||||||
nix::{fcntl::OFlag, sys::stat::dev_t},
|
nix::{fcntl::OFlag, sys::stat::dev_t},
|
||||||
wayland_server::protocol::wl_output,
|
wayland_server::protocol::wl_output,
|
||||||
},
|
},
|
||||||
utils::signaling::{Linkable, Signaler},
|
utils::signaling::{Linkable, Signaler, SignalToken},
|
||||||
wayland::output::{Mode as OutputMode, Output, PhysicalProperties},
|
wayland::output::{Mode as OutputMode, Output, PhysicalProperties},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -58,7 +59,8 @@ pub struct KmsState {
|
||||||
primary: DrmNode,
|
primary: DrmNode,
|
||||||
session: AutoSession,
|
session: AutoSession,
|
||||||
signaler: Signaler<Signal>,
|
signaler: Signaler<Signal>,
|
||||||
tokens: Vec<RegistrationToken>,
|
_restart_token: SignalToken,
|
||||||
|
_tokens: Vec<RegistrationToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
|
|
@ -86,7 +88,7 @@ pub struct Surface {
|
||||||
fps: Fps,
|
fps: Fps,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
|
pub fn init_backend(event_loop: &mut EventLoop<'static, State>, state: &mut State) -> Result<()> {
|
||||||
let (session, notifier) = AutoSession::new(None).context("Failed to acquire session")?;
|
let (session, notifier) = AutoSession::new(None).context("Failed to acquire session")?;
|
||||||
let signaler = notifier.signaler();
|
let signaler = notifier.signaler();
|
||||||
|
|
||||||
|
|
@ -101,8 +103,11 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
|
|
||||||
let libinput_event_source = event_loop
|
let libinput_event_source = event_loop
|
||||||
.handle()
|
.handle()
|
||||||
.insert_source(libinput_backend, move |event, _, state| {
|
.insert_source(libinput_backend, move |mut event, _, state| {
|
||||||
state.common.process_input_event(event);
|
if let &mut InputEvent::DeviceAdded { ref mut device } = &mut event {
|
||||||
|
state.common.config.read_device(device);
|
||||||
|
}
|
||||||
|
state.process_input_event(event);
|
||||||
for output in state.common.shell.outputs() {
|
for output in state.common.shell.outputs() {
|
||||||
state.backend.kms().schedule_render(output);
|
state.backend.kms().schedule_render(output);
|
||||||
}
|
}
|
||||||
|
|
@ -143,51 +148,99 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
};
|
};
|
||||||
slog_scope::info!("Using {} as primary gpu for rendering", primary);
|
slog_scope::info!("Using {} as primary gpu for rendering", primary);
|
||||||
|
|
||||||
|
let udev_dispatcher = Dispatcher::new(udev_backend, move |event, _, state: &mut State| {
|
||||||
|
match match event {
|
||||||
|
UdevEvent::Added { device_id, path } => state
|
||||||
|
.device_added(device_id, path)
|
||||||
|
.with_context(|| format!("Failed to add drm device: {}", device_id)),
|
||||||
|
UdevEvent::Changed { device_id } => state
|
||||||
|
.device_changed(device_id)
|
||||||
|
.with_context(|| format!("Failed to update drm device: {}", device_id)),
|
||||||
|
UdevEvent::Removed { device_id } => state
|
||||||
|
.device_removed(device_id)
|
||||||
|
.with_context(|| format!("Failed to remove drm device: {}", device_id)),
|
||||||
|
} {
|
||||||
|
Ok(()) => {
|
||||||
|
slog_scope::debug!("Successfully handled udev event")
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::error!("Error while handling udev event: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let udev_event_source = event_loop
|
||||||
|
.handle()
|
||||||
|
.register_dispatcher(udev_dispatcher.clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let handle = event_loop.handle();
|
||||||
|
let loop_signal = state.common.event_loop_signal.clone();
|
||||||
|
let dispatcher = udev_dispatcher.clone();
|
||||||
|
let _restart_token = signaler.register(move |signal| {
|
||||||
|
if let Signal::ActivateSession = signal {
|
||||||
|
let dispatcher = dispatcher.clone();
|
||||||
|
handle.insert_idle(move |state| {
|
||||||
|
for (dev, path) in dispatcher.as_source_ref().device_list() {
|
||||||
|
let drm_node = match DrmNode::from_dev_id(dev) {
|
||||||
|
Ok(node) => node,
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::error!("Failed to read drm device {}: {}", path.display(), err);
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if state.backend.kms().devices.contains_key(&drm_node) {
|
||||||
|
if let Err(err) = state.device_changed(dev) {
|
||||||
|
slog_scope::error!("Failed to update drm device {}: {}", path.display(), err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Err(err) = state.device_added(dev, path.into()) {
|
||||||
|
slog_scope::error!("Failed to add drm device {}: {}", path.display(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.common
|
||||||
|
.output_conf
|
||||||
|
.update(&mut *state.common.display.borrow_mut());
|
||||||
|
|
||||||
|
state.common.config.read_outputs(state.common.output_conf.outputs(), &mut state.backend, &mut state.common.shell);
|
||||||
|
state.common.shell.refresh_outputs();
|
||||||
|
state.common.config.write_outputs(state.common.output_conf.outputs());
|
||||||
|
|
||||||
|
for surface in state.backend.kms().devices.values_mut().flat_map(|d| d.surfaces.values_mut()) {
|
||||||
|
surface.pending = false;
|
||||||
|
}
|
||||||
|
for output in state.common.shell.outputs() {
|
||||||
|
state.backend.kms().schedule_render(output);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loop_signal.wakeup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
state.backend = BackendData::Kms(KmsState {
|
state.backend = BackendData::Kms(KmsState {
|
||||||
api,
|
api,
|
||||||
tokens: vec![libinput_event_source, session_event_source],
|
_tokens: vec![libinput_event_source, session_event_source, udev_event_source],
|
||||||
primary,
|
primary,
|
||||||
session,
|
session,
|
||||||
signaler,
|
signaler,
|
||||||
|
_restart_token,
|
||||||
devices: HashMap::new(),
|
devices: HashMap::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
for (dev, path) in udev_backend.device_list() {
|
for (dev, path) in udev_dispatcher.as_source_ref().device_list() {
|
||||||
state
|
state
|
||||||
.device_added(dev, path.into())
|
.device_added(dev, path.into())
|
||||||
.with_context(|| format!("Failed to add drm device: {}", path.display()))?;
|
.with_context(|| format!("Failed to add drm device: {}", path.display()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let udev_event_source = event_loop
|
|
||||||
.handle()
|
|
||||||
.insert_source(udev_backend, move |event, _, state| {
|
|
||||||
match match event {
|
|
||||||
UdevEvent::Added { device_id, path } => state
|
|
||||||
.device_added(device_id, path)
|
|
||||||
.with_context(|| format!("Failed to add drm device: {}", device_id)),
|
|
||||||
UdevEvent::Changed { device_id } => state
|
|
||||||
.device_changed(device_id)
|
|
||||||
.with_context(|| format!("Failed to update drm device: {}", device_id)),
|
|
||||||
UdevEvent::Removed { device_id } => state
|
|
||||||
.device_removed(device_id)
|
|
||||||
.with_context(|| format!("Failed to remove drm device: {}", device_id)),
|
|
||||||
} {
|
|
||||||
Ok(()) => {
|
|
||||||
slog_scope::debug!("Successfully handled udev event")
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
slog_scope::error!("Error while handling udev event: {}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
state.backend.kms().tokens.push(udev_event_source);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn device_added(&mut self, dev: dev_t, path: PathBuf) -> Result<()> {
|
fn device_added(&mut self, dev: dev_t, path: PathBuf) -> Result<()> {
|
||||||
|
if !self.backend.kms().session.is_active() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let fd = SessionFd::new(
|
let fd = SessionFd::new(
|
||||||
self.backend
|
self.backend
|
||||||
.kms()
|
.kms()
|
||||||
|
|
@ -331,6 +384,10 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_changed(&mut self, dev: dev_t) -> Result<()> {
|
fn device_changed(&mut self, dev: dev_t) -> Result<()> {
|
||||||
|
if !self.backend.kms().session.is_active() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let drm_node = DrmNode::from_dev_id(dev)?;
|
let drm_node = DrmNode::from_dev_id(dev)?;
|
||||||
let mut outputs_removed = Vec::new();
|
let mut outputs_removed = Vec::new();
|
||||||
let mut outputs_added = Vec::new();
|
let mut outputs_added = Vec::new();
|
||||||
|
|
@ -400,9 +457,12 @@ impl State {
|
||||||
self.common
|
self.common
|
||||||
.output_conf
|
.output_conf
|
||||||
.update(&mut *self.common.display.borrow_mut());
|
.update(&mut *self.common.display.borrow_mut());
|
||||||
self.common.config.read_outputs(self.common.output_conf.outputs(), &mut self.backend, &mut self.common.shell);
|
|
||||||
self.common.shell.refresh_outputs();
|
if self.backend.kms().session.is_active() {
|
||||||
self.common.config.write_outputs(self.common.output_conf.outputs());
|
self.common.config.read_outputs(self.common.output_conf.outputs(), &mut self.backend, &mut self.common.shell);
|
||||||
|
self.common.shell.refresh_outputs();
|
||||||
|
self.common.config.write_outputs(self.common.output_conf.outputs());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -557,11 +617,23 @@ impl Surface {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let nodes = state
|
if render::needs_buffer_reset(&self.output, state) {
|
||||||
|
self.surface.as_mut().unwrap().reset_buffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
let workspace = state
|
||||||
.shell
|
.shell
|
||||||
.active_space(&self.output)
|
.active_space(&self.output);
|
||||||
.space
|
let nodes = workspace
|
||||||
.windows()
|
.get_fullscreen(&self.output)
|
||||||
|
.map(|w| vec![w])
|
||||||
|
.unwrap_or_else(||
|
||||||
|
workspace
|
||||||
|
.space
|
||||||
|
.windows()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
.flat_map(|w| {
|
.flat_map(|w| {
|
||||||
w.toplevel()
|
w.toplevel()
|
||||||
.get_surface()?
|
.get_surface()?
|
||||||
|
|
@ -624,6 +696,13 @@ impl Surface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KmsState {
|
impl KmsState {
|
||||||
|
pub fn switch_vt(
|
||||||
|
&mut self,
|
||||||
|
num: i32,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
self.session.change_vt(num).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_config_for_output(
|
pub fn apply_config_for_output(
|
||||||
&mut self,
|
&mut self,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
|
|
@ -711,6 +790,7 @@ impl KmsState {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shell.refresh_outputs();
|
||||||
if recreated {
|
if recreated {
|
||||||
self.schedule_render(output);
|
self.schedule_render(output);
|
||||||
}
|
}
|
||||||
|
|
@ -729,6 +809,7 @@ impl KmsState {
|
||||||
}
|
}
|
||||||
if !surface.pending {
|
if !surface.pending {
|
||||||
surface.pending = true;
|
surface.pending = true;
|
||||||
|
/*
|
||||||
let duration = surface
|
let duration = surface
|
||||||
.last_submit
|
.last_submit
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -739,12 +820,13 @@ impl KmsState {
|
||||||
DrmEventTime::Realtime(time) => time.duration_since(SystemTime::now()).ok(),
|
DrmEventTime::Realtime(time) => time.duration_since(SystemTime::now()).ok(),
|
||||||
})
|
})
|
||||||
.unwrap_or(Duration::ZERO); // + Duration::from_secs_f64((1.0 / surface.refresh_rate as f64) - 20.0);
|
.unwrap_or(Duration::ZERO); // + Duration::from_secs_f64((1.0 / surface.refresh_rate as f64) - 20.0);
|
||||||
|
*/
|
||||||
let data = (*device, *crtc);
|
let data = (*device, *crtc);
|
||||||
if surface.vrr {
|
//if surface.vrr {
|
||||||
surface.render_timer.add_timeout(Duration::ZERO, data);
|
surface.render_timer.add_timeout(Duration::ZERO, data);
|
||||||
} else {
|
//} else {
|
||||||
surface.render_timer.add_timeout(duration, data);
|
// surface.render_timer.add_timeout(duration, data);
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub mod x11;
|
||||||
// TODO
|
// TODO
|
||||||
// pub mod wayland; // tbd in smithay
|
// pub mod wayland; // tbd in smithay
|
||||||
|
|
||||||
pub fn init_backend_auto(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
|
pub fn init_backend_auto(event_loop: &mut EventLoop<'static, State>, state: &mut State) -> Result<()> {
|
||||||
match std::env::var("COSMIC_BACKEND") {
|
match std::env::var("COSMIC_BACKEND") {
|
||||||
Ok(x) if x == "x11" => x11::init_backend(event_loop, state),
|
Ok(x) if x == "x11" => x11::init_backend(event_loop, state),
|
||||||
Ok(x) if x == "winit" => winit::init_backend(event_loop, state),
|
Ok(x) if x == "winit" => winit::init_backend(event_loop, state),
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,18 @@ use smithay::{
|
||||||
renderer::{
|
renderer::{
|
||||||
gles2::{Gles2Renderbuffer, Gles2Renderer, Gles2Texture},
|
gles2::{Gles2Renderbuffer, Gles2Renderer, Gles2Texture},
|
||||||
multigpu::{egl::EglGlesBackend, Error as MultiError, MultiFrame, MultiRenderer},
|
multigpu::{egl::EglGlesBackend, Error as MultiError, MultiFrame, MultiRenderer},
|
||||||
ImportAll, Renderer,
|
ImportAll, Renderer, Frame, TextureFilter,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
desktop::space::{RenderElement, RenderError, SpaceOutputTuple, SurfaceTree},
|
desktop::{
|
||||||
utils::{Logical, Point, Rectangle},
|
space::{RenderElement, RenderError, SpaceOutputTuple, SurfaceTree},
|
||||||
wayland::output::Output,
|
draw_window, draw_layer_surface, Window, layer_map_for_output, utils::damage_from_surface_tree,
|
||||||
|
},
|
||||||
|
utils::{Logical, Point, Rectangle, Transform},
|
||||||
|
wayland::{
|
||||||
|
shell::wlr_layer::Layer as WlrLayer,
|
||||||
|
output::Output,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod cursor;
|
mod cursor;
|
||||||
|
|
@ -29,6 +35,8 @@ pub type GlMultiRenderer<'a> =
|
||||||
MultiRenderer<'a, 'a, EglGlesBackend, EglGlesBackend, Gles2Renderbuffer>;
|
MultiRenderer<'a, 'a, EglGlesBackend, EglGlesBackend, Gles2Renderbuffer>;
|
||||||
pub type GlMultiFrame = MultiFrame<EglGlesBackend, EglGlesBackend>;
|
pub type GlMultiFrame = MultiFrame<EglGlesBackend, EglGlesBackend>;
|
||||||
|
|
||||||
|
static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
|
||||||
|
|
||||||
smithay::custom_elements! {
|
smithay::custom_elements! {
|
||||||
pub CustomElem<=Gles2Renderer>;
|
pub CustomElem<=Gles2Renderer>;
|
||||||
SurfaceTree=SurfaceTree,
|
SurfaceTree=SurfaceTree,
|
||||||
|
|
@ -96,7 +104,72 @@ impl AsGles2Renderer for GlMultiRenderer<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn needs_buffer_reset(output: &Output, state: &Common) -> bool {
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
struct DidCustomRendering(AtomicBool);
|
||||||
|
|
||||||
|
let will_render_custom = {
|
||||||
|
let workspace = state.shell.active_space(output);
|
||||||
|
workspace.get_fullscreen(output).is_some()
|
||||||
|
};
|
||||||
|
|
||||||
|
let userdata = output.user_data();
|
||||||
|
userdata.insert_if_missing(|| DidCustomRendering(AtomicBool::new(false)));
|
||||||
|
userdata.get::<DidCustomRendering>().unwrap().0.swap(will_render_custom, Ordering::AcqRel) != will_render_custom
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_output<R>(
|
pub fn render_output<R>(
|
||||||
|
gpu: Option<&DrmNode>,
|
||||||
|
renderer: &mut R,
|
||||||
|
age: u8,
|
||||||
|
state: &mut Common,
|
||||||
|
output: &Output,
|
||||||
|
hardware_cursor: bool,
|
||||||
|
#[cfg(feature = "debug")] fps: &mut Fps,
|
||||||
|
) -> Result<Option<Vec<Rectangle<i32, Logical>>>, RenderError<R>>
|
||||||
|
where
|
||||||
|
R: Renderer + ImportAll + AsGles2Renderer,
|
||||||
|
<R as Renderer>::TextureId: Clone + 'static,
|
||||||
|
CustomElem: RenderElement<R>,
|
||||||
|
{
|
||||||
|
renderer.downscale_filter(TextureFilter::Linear).map_err(RenderError::Rendering)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
{
|
||||||
|
fps.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
let workspace = state.shell.active_space(output);
|
||||||
|
let maybe_fullscreen_window = workspace.get_fullscreen(output).cloned();
|
||||||
|
let res = if let Some(window) = maybe_fullscreen_window {
|
||||||
|
#[cfg(not(feature = "debug"))]
|
||||||
|
{
|
||||||
|
render_fullscreen(gpu, renderer, window, state, output, hardware_cursor)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
{
|
||||||
|
render_fullscreen(gpu, renderer, window, state, output, hardware_cursor, fps)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#[cfg(not(feature = "debug"))]
|
||||||
|
{
|
||||||
|
render_desktop(gpu, renderer, age, state, output, hardware_cursor)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
{
|
||||||
|
render_desktop(gpu, renderer, age, state, output, hardware_cursor, fps)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
{
|
||||||
|
fps.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_desktop<R>(
|
||||||
_gpu: Option<&DrmNode>,
|
_gpu: Option<&DrmNode>,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
age: u8,
|
age: u8,
|
||||||
|
|
@ -110,12 +183,6 @@ where
|
||||||
<R as Renderer>::TextureId: Clone + 'static,
|
<R as Renderer>::TextureId: Clone + 'static,
|
||||||
CustomElem: RenderElement<R>,
|
CustomElem: RenderElement<R>,
|
||||||
{
|
{
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
{
|
|
||||||
fps.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut custom_elements = Vec::<CustomElem>::new();
|
let mut custom_elements = Vec::<CustomElem>::new();
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
|
|
@ -125,7 +192,7 @@ where
|
||||||
.space
|
.space
|
||||||
.output_geometry(output)
|
.output_geometry(output)
|
||||||
.unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0)));
|
.unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0)));
|
||||||
let scale = workspace.space.output_scale(output).unwrap();
|
let scale = output.current_scale().fractional_scale();
|
||||||
|
|
||||||
let fps_overlay = fps_ui(_gpu, state, fps, output_geo, scale);
|
let fps_overlay = fps_ui(_gpu, state, fps, output_geo, scale);
|
||||||
custom_elements.push(fps_overlay.into());
|
custom_elements.push(fps_overlay.into());
|
||||||
|
|
@ -160,18 +227,115 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = state.shell.active_space_mut(output).space.render_output(
|
state.shell.active_space_mut(output).space.render_output(
|
||||||
renderer,
|
renderer,
|
||||||
&output,
|
&output,
|
||||||
age as usize,
|
age as usize,
|
||||||
[0.153, 0.161, 0.165, 1.0],
|
CLEAR_COLOR,
|
||||||
&*custom_elements,
|
&*custom_elements,
|
||||||
);
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_fullscreen<R>(
|
||||||
|
_gpu: Option<&DrmNode>,
|
||||||
|
renderer: &mut R,
|
||||||
|
window: Window,
|
||||||
|
state: &mut Common,
|
||||||
|
output: &Output,
|
||||||
|
hardware_cursor: bool,
|
||||||
|
#[cfg(feature = "debug")] fps: &mut Fps,
|
||||||
|
) -> Result<Option<Vec<Rectangle<i32, Logical>>>, RenderError<R>>
|
||||||
|
where
|
||||||
|
R: Renderer + ImportAll + AsGles2Renderer,
|
||||||
|
<R as Renderer>::TextureId: Clone + 'static,
|
||||||
|
CustomElem: RenderElement<R>,
|
||||||
|
{
|
||||||
|
let transform = Transform::from(output.current_transform());
|
||||||
|
let mode = output.current_mode().unwrap();
|
||||||
|
let scale = output.current_scale().fractional_scale();
|
||||||
|
let output_geo = state.shell.output_geometry(output);
|
||||||
|
|
||||||
|
let mut custom_elements = Vec::<CustomElem>::new();
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
{
|
{
|
||||||
fps.end();
|
let fps_overlay = fps_ui(_gpu, state, fps, Rectangle::from_loc_and_size((0, 0), output_geo.size), scale);
|
||||||
|
custom_elements.push(fps_overlay.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
for seat in &state.seats {
|
||||||
|
let pointer = match seat.get_pointer() {
|
||||||
|
Some(ptr) => ptr,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let location = state
|
||||||
|
.shell
|
||||||
|
.space_relative_output_geometry(pointer.current_location().to_i32_round(), output);
|
||||||
|
|
||||||
|
if let Some(cursor) = cursor::draw_cursor(
|
||||||
|
renderer.as_gles2(),
|
||||||
|
seat,
|
||||||
|
location,
|
||||||
|
&state.start_time,
|
||||||
|
!hardware_cursor,
|
||||||
|
) {
|
||||||
|
custom_elements.push(cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer
|
||||||
|
.render(mode.size, transform, |renderer, frame| {
|
||||||
|
let mut damage = window.accumulated_damage(None);
|
||||||
|
frame.clear(
|
||||||
|
CLEAR_COLOR,
|
||||||
|
&[Rectangle::from_loc_and_size((0, 0), mode.size).to_f64()],
|
||||||
|
)?;
|
||||||
|
draw_window(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
&window,
|
||||||
|
scale,
|
||||||
|
(0, 0),
|
||||||
|
&[Rectangle::from_loc_and_size(
|
||||||
|
(0, 0),
|
||||||
|
mode.size.to_f64().to_logical(scale).to_i32_round(),
|
||||||
|
)],
|
||||||
|
&slog_scope::logger(),
|
||||||
|
)?;
|
||||||
|
let layer_map = layer_map_for_output(output);
|
||||||
|
for layer_surface in layer_map.layers_on(WlrLayer::Overlay) {
|
||||||
|
let geo = layer_map.layer_geometry(&layer_surface).unwrap();
|
||||||
|
draw_layer_surface(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
layer_surface,
|
||||||
|
scale,
|
||||||
|
geo.loc,
|
||||||
|
&[Rectangle::from_loc_and_size((0, 0), geo.size)],
|
||||||
|
&slog_scope::logger(),
|
||||||
|
)?;
|
||||||
|
if let Some(surface) = layer_surface.get_surface() {
|
||||||
|
damage.extend(damage_from_surface_tree(surface, geo.loc, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for elem in custom_elements {
|
||||||
|
let geo = elem.geometry();
|
||||||
|
let elem_damage = elem.accumulated_damage(None);
|
||||||
|
elem.draw(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
scale,
|
||||||
|
geo.loc,
|
||||||
|
&[Rectangle::from_loc_and_size((0, 0), geo.size)],
|
||||||
|
&slog_scope::logger(),
|
||||||
|
)?;
|
||||||
|
damage.extend(elem_damage.into_iter().map(|mut rect| {
|
||||||
|
rect.loc += geo.loc;
|
||||||
|
rect
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
Ok(Some(damage))
|
||||||
|
})
|
||||||
|
.and_then(std::convert::identity)
|
||||||
|
.map_err(RenderError::<R>::Rendering)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,15 +35,25 @@ pub struct WinitState {
|
||||||
backend: Rc<RefCell<WinitGraphicsBackend>>,
|
backend: Rc<RefCell<WinitGraphicsBackend>>,
|
||||||
//_global: GlobalDrop<WlOutput>,
|
//_global: GlobalDrop<WlOutput>,
|
||||||
output: Output,
|
output: Output,
|
||||||
|
age_reset: u8,
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
fps: Fps,
|
fps: Fps,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WinitState {
|
impl WinitState {
|
||||||
pub fn render_output(&mut self, state: &mut Common) -> Result<()> {
|
pub fn render_output(&mut self, state: &mut Common) -> Result<()> {
|
||||||
|
if render::needs_buffer_reset(&self.output, state) {
|
||||||
|
self.reset_buffers();
|
||||||
|
}
|
||||||
|
|
||||||
let backend = &mut *self.backend.borrow_mut();
|
let backend = &mut *self.backend.borrow_mut();
|
||||||
backend.bind().with_context(|| "Failed to bind buffer")?;
|
backend.bind().with_context(|| "Failed to bind buffer")?;
|
||||||
let age = backend.buffer_age().unwrap_or(0);
|
let age = if self.age_reset > 0 {
|
||||||
|
self.age_reset -= 1;
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
backend.buffer_age().unwrap_or(0)
|
||||||
|
};
|
||||||
|
|
||||||
match render::render_output(
|
match render::render_output(
|
||||||
None,
|
None,
|
||||||
|
|
@ -98,6 +108,10 @@ impl WinitState {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_buffers(&mut self) {
|
||||||
|
self.age_reset = 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
|
pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
|
||||||
|
|
@ -186,6 +200,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
output: output.clone(),
|
output: output.clone(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
fps: Fps::default(),
|
fps: Fps::default(),
|
||||||
|
age_reset: 0,
|
||||||
});
|
});
|
||||||
state.common.output_conf.add_heads(std::iter::once(&output));
|
state.common.output_conf.add_heads(std::iter::once(&output));
|
||||||
state
|
state
|
||||||
|
|
@ -276,7 +291,7 @@ impl State {
|
||||||
render_ping.ping();
|
render_ping.ping();
|
||||||
}
|
}
|
||||||
WinitEvent::Refresh => render_ping.ping(),
|
WinitEvent::Refresh => render_ping.ping(),
|
||||||
WinitEvent::Input(event) => self.common.process_input_event(event),
|
WinitEvent::Input(event) => self.process_input_event(event),
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,10 @@ impl Surface {
|
||||||
renderer: &mut Gles2Renderer,
|
renderer: &mut Gles2Renderer,
|
||||||
state: &mut Common,
|
state: &mut Common,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
if render::needs_buffer_reset(&self.output, state) {
|
||||||
|
self.surface.reset_buffers();
|
||||||
|
}
|
||||||
|
|
||||||
let (buffer, age) = self
|
let (buffer, age) = self
|
||||||
.surface
|
.surface
|
||||||
.buffer()
|
.buffer()
|
||||||
|
|
@ -410,7 +414,7 @@ impl State {
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.common.process_input_event(event);
|
self.process_input_event(event);
|
||||||
// TODO actually figure out the output
|
// TODO actually figure out the output
|
||||||
for output in self.common.shell.outputs() {
|
for output in self.common.shell.outputs() {
|
||||||
self.backend.schedule_render(output);
|
self.backend.schedule_render(output);
|
||||||
|
|
|
||||||
475
src/config.rs
475
src/config.rs
|
|
@ -1,475 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
shell::{
|
|
||||||
Shell,
|
|
||||||
layout::FocusDirection,
|
|
||||||
},
|
|
||||||
state::BackendData,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
pub use smithay::{
|
|
||||||
backend::input::KeyState,
|
|
||||||
utils::{Logical, Physical, Point, Size, Transform},
|
|
||||||
wayland::{
|
|
||||||
output::{Mode, Output},
|
|
||||||
seat::{keysyms as KeySyms, Keysym, ModifiersState as KeyModifiers},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf};
|
|
||||||
use xkbcommon::xkb;
|
|
||||||
|
|
||||||
pub struct Config {
|
|
||||||
pub static_conf: StaticConfig,
|
|
||||||
pub dynamic_conf: DynamicConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct StaticConfig {
|
|
||||||
pub key_bindings: HashMap<KeyPattern, Action>,
|
|
||||||
pub workspace_mode: crate::shell::Mode,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DynamicConfig {
|
|
||||||
outputs: (Option<PathBuf>, OutputsConfig),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct OutputsConfig {
|
|
||||||
pub config: HashMap<Vec<OutputInfo>, Vec<OutputConfig>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct OutputInfo {
|
|
||||||
pub connector: String,
|
|
||||||
pub make: String,
|
|
||||||
pub model: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Output> for OutputInfo {
|
|
||||||
fn from(o: Output) -> OutputInfo {
|
|
||||||
let physical = o.physical_properties();
|
|
||||||
OutputInfo {
|
|
||||||
connector: o.name(),
|
|
||||||
make: physical.make,
|
|
||||||
model: physical.model,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_enabled() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
|
||||||
pub struct OutputConfig {
|
|
||||||
pub mode: ((i32, i32), Option<u32>),
|
|
||||||
pub vrr: bool,
|
|
||||||
pub scale: f64,
|
|
||||||
#[serde(with = "TransformDef")]
|
|
||||||
pub transform: Transform,
|
|
||||||
pub position: (i32, i32),
|
|
||||||
#[serde(default = "default_enabled")]
|
|
||||||
pub enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for OutputConfig {
|
|
||||||
fn default() -> OutputConfig {
|
|
||||||
OutputConfig {
|
|
||||||
mode: ((0, 0), None),
|
|
||||||
vrr: false,
|
|
||||||
scale: 1.0,
|
|
||||||
transform: Transform::Normal,
|
|
||||||
position: (0, 0),
|
|
||||||
enabled: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputConfig {
|
|
||||||
pub fn mode_size(&self) -> Size<i32, Physical> {
|
|
||||||
self.mode.0.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mode_refresh(&self) -> u32 {
|
|
||||||
self.mode.1.unwrap_or(60_000)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn output_mode(&self) -> Mode {
|
|
||||||
Mode {
|
|
||||||
size: self.mode_size(),
|
|
||||||
refresh: self.mode_refresh() as i32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[serde(remote = "Transform")]
|
|
||||||
enum TransformDef {
|
|
||||||
Normal,
|
|
||||||
_90,
|
|
||||||
_180,
|
|
||||||
_270,
|
|
||||||
Flipped,
|
|
||||||
Flipped90,
|
|
||||||
Flipped180,
|
|
||||||
Flipped270,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn load() -> Config {
|
|
||||||
let xdg = xdg::BaseDirectories::new().ok();
|
|
||||||
Config {
|
|
||||||
static_conf: Self::load_static(xdg.as_ref()),
|
|
||||||
dynamic_conf: Self::load_dynamic(xdg.as_ref()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_static(xdg: Option<&xdg::BaseDirectories>) -> StaticConfig {
|
|
||||||
let mut locations = if let Some(base) = xdg {
|
|
||||||
vec![
|
|
||||||
base.get_config_file("cosmic-comp.ron"),
|
|
||||||
base.get_config_file("cosmic-comp/config.ron"),
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
Vec::with_capacity(3)
|
|
||||||
};
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
if let Ok(mut cwd) = std::env::current_dir() {
|
|
||||||
cwd.push("config.ron");
|
|
||||||
locations.push(cwd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
locations.push(PathBuf::from("/etc/cosmic-comp/config.ron"));
|
|
||||||
locations.push(PathBuf::from("/etc/cosmic-comp.ron"));
|
|
||||||
|
|
||||||
for path in locations {
|
|
||||||
slog_scope::debug!("Trying config location: {}", path.display());
|
|
||||||
if path.exists() {
|
|
||||||
slog_scope::info!("Using config at {}", path.display());
|
|
||||||
return ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap())
|
|
||||||
.expect("Malformed config file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StaticConfig {
|
|
||||||
key_bindings: HashMap::new(),
|
|
||||||
workspace_mode: crate::shell::Mode::global(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_dynamic(xdg: Option<&xdg::BaseDirectories>) -> DynamicConfig {
|
|
||||||
let output_path =
|
|
||||||
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
|
||||||
let outputs = Self::load_outputs(&output_path);
|
|
||||||
|
|
||||||
DynamicConfig {
|
|
||||||
outputs: (output_path, outputs),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_outputs(path: &Option<PathBuf>) -> OutputsConfig {
|
|
||||||
if let Some(path) = path.as_ref() {
|
|
||||||
if path.exists() {
|
|
||||||
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
|
||||||
Ok(config) => return config,
|
|
||||||
Err(err) => {
|
|
||||||
slog_scope::warn!("Failed to read output_config ({}), resetting..", err);
|
|
||||||
if let Err(err) = std::fs::remove_file(path) {
|
|
||||||
slog_scope::error!("Failed to remove output_config {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputsConfig {
|
|
||||||
config: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_outputs<'a>(
|
|
||||||
&mut self,
|
|
||||||
outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>,
|
|
||||||
backend: &mut BackendData,
|
|
||||||
shell: &mut Shell,
|
|
||||||
) {
|
|
||||||
let outputs = outputs.map(|x| x.borrow().clone()).collect::<Vec<_>>();
|
|
||||||
let mut infos = outputs
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(Into::<crate::config::OutputInfo>::into)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
infos.sort();
|
|
||||||
if let Some(configs) = self.dynamic_conf.outputs().config.get(&infos).cloned() {
|
|
||||||
let mut reset = false;
|
|
||||||
let known_good_configs = outputs
|
|
||||||
.iter()
|
|
||||||
.map(|output| {
|
|
||||||
output
|
|
||||||
.user_data()
|
|
||||||
.get::<RefCell<OutputConfig>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow()
|
|
||||||
.clone()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
|
|
||||||
{
|
|
||||||
let output = outputs
|
|
||||||
.iter()
|
|
||||||
.find(|o| &o.name() == name)
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
*output
|
|
||||||
.user_data()
|
|
||||||
.get::<RefCell<OutputConfig>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow_mut() = output_config;
|
|
||||||
if let Err(err) = backend.apply_config_for_output(&output, false, shell) {
|
|
||||||
slog_scope::warn!(
|
|
||||||
"Failed to set new config for output {}: {}",
|
|
||||||
output.name(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
reset = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if reset {
|
|
||||||
for (output, output_config) in outputs
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.zip(known_good_configs.into_iter())
|
|
||||||
{
|
|
||||||
*output
|
|
||||||
.user_data()
|
|
||||||
.get::<RefCell<OutputConfig>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow_mut() = output_config;
|
|
||||||
if let Err(err) = backend.apply_config_for_output(&output, false, shell)
|
|
||||||
{
|
|
||||||
slog_scope::error!(
|
|
||||||
"Failed to reset config for output {}: {}",
|
|
||||||
output.name(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_outputs<'a>(&mut self, outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>) {
|
|
||||||
let mut infos = outputs
|
|
||||||
.map(|o| {
|
|
||||||
let o = o.borrow();
|
|
||||||
(
|
|
||||||
Into::<crate::config::OutputInfo>::into(o.clone()),
|
|
||||||
o.user_data()
|
|
||||||
.get::<RefCell<OutputConfig>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow()
|
|
||||||
.clone(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<(OutputInfo, OutputConfig)>>();
|
|
||||||
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
|
||||||
let (infos, configs) = infos.into_iter().unzip();
|
|
||||||
self
|
|
||||||
.dynamic_conf
|
|
||||||
.outputs_mut()
|
|
||||||
.config
|
|
||||||
.insert(infos, configs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PersistenceGuard<'a, T: Serialize>(Option<PathBuf>, &'a mut T);
|
|
||||||
|
|
||||||
impl<'a, T: Serialize> std::ops::Deref for PersistenceGuard<'a, T> {
|
|
||||||
type Target = T;
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
&self.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Serialize> std::ops::DerefMut for PersistenceGuard<'a, T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Serialize> Drop for PersistenceGuard<'a, T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(path) = self.0.as_ref() {
|
|
||||||
let writer = match OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.write(true)
|
|
||||||
.open(path)
|
|
||||||
{
|
|
||||||
Ok(writer) => writer,
|
|
||||||
Err(err) => {
|
|
||||||
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(err) = ron::ser::to_writer_pretty(writer, &self.1, Default::default()) {
|
|
||||||
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynamicConfig {
|
|
||||||
pub fn outputs(&self) -> &OutputsConfig {
|
|
||||||
&self.outputs.1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn outputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, OutputsConfig> {
|
|
||||||
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
|
||||||
pub enum KeyModifier {
|
|
||||||
Ctrl,
|
|
||||||
Alt,
|
|
||||||
Shift,
|
|
||||||
Logo,
|
|
||||||
CapsLock,
|
|
||||||
NumLock,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::AddAssign<KeyModifier> for KeyModifiers {
|
|
||||||
fn add_assign(&mut self, rhs: KeyModifier) {
|
|
||||||
match rhs {
|
|
||||||
KeyModifier::Ctrl => self.ctrl = true,
|
|
||||||
KeyModifier::Alt => self.alt = true,
|
|
||||||
KeyModifier::Shift => self.shift = true,
|
|
||||||
KeyModifier::Logo => self.logo = true,
|
|
||||||
KeyModifier::CapsLock => self.caps_lock = true,
|
|
||||||
KeyModifier::NumLock => self.num_lock = true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::BitOr for KeyModifier {
|
|
||||||
type Output = KeyModifiers;
|
|
||||||
|
|
||||||
fn bitor(self, rhs: KeyModifier) -> Self::Output {
|
|
||||||
let mut modifiers = self.into();
|
|
||||||
modifiers += rhs;
|
|
||||||
modifiers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<KeyModifiers> for KeyModifier {
|
|
||||||
fn into(self) -> KeyModifiers {
|
|
||||||
let mut modifiers = KeyModifiers {
|
|
||||||
ctrl: false,
|
|
||||||
alt: false,
|
|
||||||
shift: false,
|
|
||||||
caps_lock: false,
|
|
||||||
logo: false,
|
|
||||||
num_lock: false,
|
|
||||||
};
|
|
||||||
modifiers += self;
|
|
||||||
modifiers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(transparent)]
|
|
||||||
struct KeyModifiersDef(Vec<KeyModifier>);
|
|
||||||
|
|
||||||
impl From<KeyModifiersDef> for KeyModifiers {
|
|
||||||
fn from(src: KeyModifiersDef) -> Self {
|
|
||||||
src.0.into_iter().fold(
|
|
||||||
KeyModifiers {
|
|
||||||
ctrl: false,
|
|
||||||
alt: false,
|
|
||||||
shift: false,
|
|
||||||
caps_lock: false,
|
|
||||||
logo: false,
|
|
||||||
num_lock: false,
|
|
||||||
},
|
|
||||||
|mut modis, modi| {
|
|
||||||
modis += modi;
|
|
||||||
modis
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn deserialize_KeyModifiers<'de, D>(deserializer: D) -> Result<KeyModifiers, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
KeyModifiersDef::deserialize(deserializer).map(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn deserialize_Keysym<'de, D>(deserializer: D) -> Result<Keysym, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
use serde::de::{Error, Unexpected};
|
|
||||||
|
|
||||||
let name = String::deserialize(deserializer)?;
|
|
||||||
//let name = format!("KEY_{}", code);
|
|
||||||
match xkb::keysym_from_name(&name, xkb::KEYSYM_NO_FLAGS) {
|
|
||||||
KeySyms::KEY_NoSymbol => match xkb::keysym_from_name(&name, xkb::KEYSYM_CASE_INSENSITIVE) {
|
|
||||||
KeySyms::KEY_NoSymbol => Err(<D::Error as Error>::invalid_value(
|
|
||||||
Unexpected::Str(&name),
|
|
||||||
&"One of the keysym names of xkbcommon.h without the 'KEY_' prefix",
|
|
||||||
)),
|
|
||||||
x => {
|
|
||||||
slog_scope::warn!(
|
|
||||||
"Key-Binding '{}' only matched case insensitive for {:?}",
|
|
||||||
name,
|
|
||||||
xkb::keysym_get_name(x)
|
|
||||||
);
|
|
||||||
Ok(x)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
x => Ok(x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describtion of a key combination that might be
|
|
||||||
/// handled by the compositor.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct KeyPattern {
|
|
||||||
/// What modifiers are expected to be pressed alongside the key
|
|
||||||
#[serde(deserialize_with = "deserialize_KeyModifiers")]
|
|
||||||
pub modifiers: KeyModifiers,
|
|
||||||
/// The actual key, that was pressed
|
|
||||||
#[serde(deserialize_with = "deserialize_Keysym")]
|
|
||||||
pub key: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyPattern {
|
|
||||||
pub fn new(modifiers: impl Into<KeyModifiers>, key: u32) -> KeyPattern {
|
|
||||||
KeyPattern {
|
|
||||||
modifiers: modifiers.into(),
|
|
||||||
key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Action {
|
|
||||||
Terminate,
|
|
||||||
Debug,
|
|
||||||
Close,
|
|
||||||
Workspace(u8),
|
|
||||||
MoveToWorkspace(u8),
|
|
||||||
Focus(FocusDirection),
|
|
||||||
Orientation(crate::shell::layout::Orientation),
|
|
||||||
Spawn(String),
|
|
||||||
}
|
|
||||||
650
src/config/mod.rs
Normal file
650
src/config/mod.rs
Normal file
|
|
@ -0,0 +1,650 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
shell::{
|
||||||
|
Shell,
|
||||||
|
layout::FocusDirection,
|
||||||
|
},
|
||||||
|
state::BackendData,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
pub use smithay::{
|
||||||
|
backend::input::KeyState,
|
||||||
|
reexports::input::{AccelProfile, ClickMethod, ScrollMethod, SendEventsMode, TapButtonMap, Device as InputDevice},
|
||||||
|
utils::{Logical, Physical, Point, Size, Transform},
|
||||||
|
wayland::{
|
||||||
|
output::{Mode, Output},
|
||||||
|
seat::{keysyms as KeySyms, Keysym, ModifiersState as KeyModifiers},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf};
|
||||||
|
|
||||||
|
mod types;
|
||||||
|
pub use self::types::*;
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub static_conf: StaticConfig,
|
||||||
|
pub dynamic_conf: DynamicConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct StaticConfig {
|
||||||
|
pub key_bindings: HashMap<KeyPattern, Action>,
|
||||||
|
pub workspace_mode: crate::shell::Mode,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DynamicConfig {
|
||||||
|
outputs: (Option<PathBuf>, OutputsConfig),
|
||||||
|
inputs: (Option<PathBuf>, InputsConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct OutputsConfig {
|
||||||
|
pub config: HashMap<Vec<OutputInfo>, Vec<OutputConfig>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct OutputInfo {
|
||||||
|
pub connector: String,
|
||||||
|
pub make: String,
|
||||||
|
pub model: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Output> for OutputInfo {
|
||||||
|
fn from(o: Output) -> OutputInfo {
|
||||||
|
let physical = o.physical_properties();
|
||||||
|
OutputInfo {
|
||||||
|
connector: o.name(),
|
||||||
|
make: physical.make,
|
||||||
|
model: physical.model,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_enabled() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
|
pub struct OutputConfig {
|
||||||
|
pub mode: ((i32, i32), Option<u32>),
|
||||||
|
pub vrr: bool,
|
||||||
|
pub scale: f64,
|
||||||
|
#[serde(with = "TransformDef")]
|
||||||
|
pub transform: Transform,
|
||||||
|
pub position: (i32, i32),
|
||||||
|
#[serde(default = "default_enabled")]
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OutputConfig {
|
||||||
|
fn default() -> OutputConfig {
|
||||||
|
OutputConfig {
|
||||||
|
mode: ((0, 0), None),
|
||||||
|
vrr: false,
|
||||||
|
scale: 1.0,
|
||||||
|
transform: Transform::Normal,
|
||||||
|
position: (0, 0),
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputConfig {
|
||||||
|
pub fn mode_size(&self) -> Size<i32, Physical> {
|
||||||
|
self.mode.0.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mode_refresh(&self) -> u32 {
|
||||||
|
self.mode.1.unwrap_or(60_000)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn output_mode(&self) -> Mode {
|
||||||
|
Mode {
|
||||||
|
size: self.mode_size(),
|
||||||
|
refresh: self.mode_refresh() as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct InputsConfig {
|
||||||
|
xkb: XkbConfig,
|
||||||
|
devices: HashMap<String, InputConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct InputConfig {
|
||||||
|
state: DeviceState,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
acceleration: Option<AccelConfig>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
calibration: Option<[f32; 6]>,
|
||||||
|
#[serde(with = "ClickMethodDef")]
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
click_method: Option<ClickMethod>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
disable_while_typing: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
left_handed: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
middle_button_emulation: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
rotation_angle: Option<u32>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
scroll_config: Option<ScrollConfig>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||||
|
tap_config: Option<TapConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct AccelConfig {
|
||||||
|
#[serde(with = "AccelProfileDef")]
|
||||||
|
profile: Option<AccelProfile>,
|
||||||
|
speed: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct ScrollConfig {
|
||||||
|
#[serde(with = "ScrollMethodDef")]
|
||||||
|
method: Option<ScrollMethod>,
|
||||||
|
natural_scroll: Option<bool>,
|
||||||
|
scroll_button: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum DeviceState {
|
||||||
|
Enabled,
|
||||||
|
Disabled,
|
||||||
|
DisabledOnExternalMouse,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct TapConfig {
|
||||||
|
enabled: bool,
|
||||||
|
#[serde(with = "TapButtonMapDef")]
|
||||||
|
button_map: Option<TapButtonMap>,
|
||||||
|
drag: bool,
|
||||||
|
drag_lock: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load() -> Config {
|
||||||
|
let xdg = xdg::BaseDirectories::new().ok();
|
||||||
|
Config {
|
||||||
|
static_conf: Self::load_static(xdg.as_ref()),
|
||||||
|
dynamic_conf: Self::load_dynamic(xdg.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_static(xdg: Option<&xdg::BaseDirectories>) -> StaticConfig {
|
||||||
|
let mut locations = if let Some(base) = xdg {
|
||||||
|
vec![
|
||||||
|
base.get_config_file("cosmic-comp.ron"),
|
||||||
|
base.get_config_file("cosmic-comp/config.ron"),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
Vec::with_capacity(3)
|
||||||
|
};
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
if let Ok(mut cwd) = std::env::current_dir() {
|
||||||
|
cwd.push("config.ron");
|
||||||
|
locations.push(cwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
locations.push(PathBuf::from("/etc/cosmic-comp/config.ron"));
|
||||||
|
locations.push(PathBuf::from("/etc/cosmic-comp.ron"));
|
||||||
|
|
||||||
|
for path in locations {
|
||||||
|
slog_scope::debug!("Trying config location: {}", path.display());
|
||||||
|
if path.exists() {
|
||||||
|
slog_scope::info!("Using config at {}", path.display());
|
||||||
|
return ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap())
|
||||||
|
.expect("Malformed config file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticConfig {
|
||||||
|
key_bindings: HashMap::new(),
|
||||||
|
workspace_mode: crate::shell::Mode::global(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_dynamic(xdg: Option<&xdg::BaseDirectories>) -> DynamicConfig {
|
||||||
|
let output_path =
|
||||||
|
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
||||||
|
let outputs = Self::load_outputs(&output_path);
|
||||||
|
|
||||||
|
let input_path =
|
||||||
|
xdg.and_then(|base| base.place_state_file("cosmic-comp/inputs.ron").ok());
|
||||||
|
let inputs = Self::load_inputs(&input_path);
|
||||||
|
|
||||||
|
DynamicConfig {
|
||||||
|
outputs: (output_path, outputs),
|
||||||
|
inputs: (input_path, inputs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_outputs(path: &Option<PathBuf>) -> OutputsConfig {
|
||||||
|
if let Some(path) = path.as_ref() {
|
||||||
|
if path.exists() {
|
||||||
|
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
||||||
|
Ok(config) => return config,
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::warn!("Failed to read output_config ({}), resetting..", err);
|
||||||
|
if let Err(err) = std::fs::remove_file(path) {
|
||||||
|
slog_scope::error!("Failed to remove output_config {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputsConfig {
|
||||||
|
config: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_inputs(path: &Option<PathBuf>) -> InputsConfig {
|
||||||
|
if let Some(path) = path.as_ref() {
|
||||||
|
if path.exists() {
|
||||||
|
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
||||||
|
Ok(config) => return config,
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::warn!("Failed to read input_config ({}), resetting..", err);
|
||||||
|
if let Err(err) = std::fs::remove_file(path) {
|
||||||
|
slog_scope::error!("Failed to remove input_config {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputsConfig {
|
||||||
|
xkb: XkbConfig::default(),
|
||||||
|
devices: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_outputs(
|
||||||
|
&mut self,
|
||||||
|
outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>,
|
||||||
|
backend: &mut BackendData,
|
||||||
|
shell: &mut Shell,
|
||||||
|
) {
|
||||||
|
let outputs = outputs.map(|x| x.borrow().clone()).collect::<Vec<_>>();
|
||||||
|
let mut infos = outputs
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(Into::<crate::config::OutputInfo>::into)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
infos.sort();
|
||||||
|
if let Some(configs) = self.dynamic_conf.outputs().config.get(&infos).cloned() {
|
||||||
|
let mut reset = false;
|
||||||
|
let known_good_configs = outputs
|
||||||
|
.iter()
|
||||||
|
.map(|output| {
|
||||||
|
output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow()
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
|
||||||
|
{
|
||||||
|
let output = outputs
|
||||||
|
.iter()
|
||||||
|
.find(|o| &o.name() == name)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
*output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut() = output_config;
|
||||||
|
if let Err(err) = backend.apply_config_for_output(&output, false, shell) {
|
||||||
|
slog_scope::warn!(
|
||||||
|
"Failed to set new config for output {}: {}",
|
||||||
|
output.name(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
reset = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reset {
|
||||||
|
for (output, output_config) in outputs
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.zip(known_good_configs.into_iter())
|
||||||
|
{
|
||||||
|
*output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut() = output_config;
|
||||||
|
if let Err(err) = backend.apply_config_for_output(&output, false, shell)
|
||||||
|
{
|
||||||
|
slog_scope::error!(
|
||||||
|
"Failed to reset config for output {}: {}",
|
||||||
|
output.name(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_outputs(&mut self, outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>) {
|
||||||
|
let mut infos = outputs
|
||||||
|
.map(|o| {
|
||||||
|
let o = o.borrow();
|
||||||
|
(
|
||||||
|
Into::<crate::config::OutputInfo>::into(o.clone()),
|
||||||
|
o.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow()
|
||||||
|
.clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(OutputInfo, OutputConfig)>>();
|
||||||
|
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
||||||
|
let (infos, configs) = infos.into_iter().unzip();
|
||||||
|
self
|
||||||
|
.dynamic_conf
|
||||||
|
.outputs_mut()
|
||||||
|
.config
|
||||||
|
.insert(infos, configs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_device(&mut self, device: &mut InputDevice) {
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
|
let mut inputs = self.dynamic_conf.inputs_mut();
|
||||||
|
match inputs.devices.entry(device.name().into()) {
|
||||||
|
Entry::Occupied(entry) => {
|
||||||
|
let config = entry.get();
|
||||||
|
if let Err(err) = match config.state {
|
||||||
|
DeviceState::Enabled => device.config_send_events_set_mode(SendEventsMode::ENABLED),
|
||||||
|
DeviceState::Disabled => device.config_send_events_set_mode(SendEventsMode::DISABLED),
|
||||||
|
DeviceState::DisabledOnExternalMouse => device.config_send_events_set_mode(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE),
|
||||||
|
} {
|
||||||
|
slog_scope::warn!("Failed to apply mode {:?} for device {:?}: {:?}", config.state, device.name(), err);
|
||||||
|
}
|
||||||
|
if let Some(accel) = config.acceleration.as_ref() {
|
||||||
|
if let Some(profile) = accel.profile {
|
||||||
|
if let Err(err) = device.config_accel_set_profile(profile) {
|
||||||
|
slog_scope::warn!("Failed to apply acceleration profile {:?} for device {:?}: {:?}", profile, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(err) = device.config_accel_set_speed(accel.speed) {
|
||||||
|
slog_scope::warn!("Failed to apply acceleration speed {:?} for device {:?}: {:?}", accel.speed, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(matrix) = config.calibration {
|
||||||
|
if let Err(err) = device.config_calibration_set_matrix(matrix) {
|
||||||
|
slog_scope::warn!("Failed to apply calibration matrix {:?} for device {:?}: {:?}", matrix, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(dwt) = config.disable_while_typing {
|
||||||
|
if let Err(err) = device.config_dwt_set_enabled(dwt) {
|
||||||
|
slog_scope::warn!("Failed to apply disable-while-typing {:?} for device {:?}: {:?}", dwt, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(left) = config.left_handed {
|
||||||
|
if let Err(err) = device.config_left_handed_set(left) {
|
||||||
|
slog_scope::warn!("Failed to apply left-handed {:?} for device {:?}: {:?}", left, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(middle) = config.middle_button_emulation {
|
||||||
|
if let Err(err) = device.config_middle_emulation_set_enabled(middle) {
|
||||||
|
slog_scope::warn!("Failed to apply middle-button-emulation {:?} for device {:?}: {:?}", middle, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(angle) = config.rotation_angle {
|
||||||
|
if let Err(err) = device.config_rotation_set_angle(angle) {
|
||||||
|
slog_scope::warn!("Failed to apply rotation-angle {:?} for device {:?}: {:?}", angle, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(scroll) = config.scroll_config.as_ref() {
|
||||||
|
if let Some(method) = scroll.method {
|
||||||
|
if let Err(err) = device.config_scroll_set_method(method) {
|
||||||
|
slog_scope::warn!("Failed to apply scroll method {:?} for device {:?}: {:?}", method, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(natural) = scroll.natural_scroll {
|
||||||
|
if let Err(err) = device.config_scroll_set_natural_scroll_enabled(natural) {
|
||||||
|
slog_scope::warn!("Failed to apply natural scrolling {:?} for device {:?}: {:?}", natural, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(button) = scroll.scroll_button {
|
||||||
|
if let Err(err) = device.config_scroll_set_button(button) {
|
||||||
|
slog_scope::warn!("Failed to apply scroll button {:?} for device {:?}: {:?}", button, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(tap) = config.tap_config.as_ref() {
|
||||||
|
if let Err(err) = device.config_tap_set_enabled(tap.enabled) {
|
||||||
|
slog_scope::warn!("Failed to apply tap-to-click {:?} for device {:?}: {:?}", tap.enabled, device.name(), err);
|
||||||
|
}
|
||||||
|
if let Some(button_map) = tap.button_map {
|
||||||
|
if let Err(err) = device.config_tap_set_button_map(button_map) {
|
||||||
|
slog_scope::warn!("Failed to apply button map {:?} for device {:?}: {:?}", button_map, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(err) = device.config_tap_set_drag_enabled(tap.drag) {
|
||||||
|
slog_scope::warn!("Failed to apply tap-drag {:?} for device {:?}: {:?}", tap.drag, device.name(), err);
|
||||||
|
}
|
||||||
|
if let Err(err) = device.config_tap_set_drag_lock_enabled(tap.drag_lock) {
|
||||||
|
slog_scope::warn!("Failed to apply tap-drag-lock {:?} for device {:?}: {:?}", tap.drag_lock, device.name(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(InputConfig {
|
||||||
|
state: match device.config_send_events_mode() {
|
||||||
|
x if x.contains(SendEventsMode::ENABLED) => DeviceState::Enabled,
|
||||||
|
x if x.contains(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE) => DeviceState::DisabledOnExternalMouse,
|
||||||
|
x if x.contains(SendEventsMode::DISABLED) => DeviceState::Disabled,
|
||||||
|
_ => DeviceState::Disabled,
|
||||||
|
},
|
||||||
|
acceleration: if device.config_accel_is_available() {
|
||||||
|
Some(AccelConfig {
|
||||||
|
profile: device.config_accel_profile(),
|
||||||
|
speed: device.config_accel_speed(),
|
||||||
|
})
|
||||||
|
} else { None },
|
||||||
|
calibration: device.config_calibration_matrix(),
|
||||||
|
click_method: device.config_click_method(),
|
||||||
|
disable_while_typing: if device.config_dwt_is_available() {
|
||||||
|
Some(device.config_dwt_enabled())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
left_handed: if device.config_left_handed_is_available() {
|
||||||
|
Some(device.config_left_handed())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
middle_button_emulation: if device.config_middle_emulation_is_available() {
|
||||||
|
Some(device.config_middle_emulation_enabled())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
rotation_angle: if device.config_rotation_is_available() {
|
||||||
|
Some(device.config_rotation_angle())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
scroll_config: if device.config_scroll_methods().iter().any(|x| *x != ScrollMethod::NoScroll) {
|
||||||
|
Some(ScrollConfig {
|
||||||
|
method: device.config_scroll_method(),
|
||||||
|
natural_scroll: if device.config_scroll_has_natural_scroll() {
|
||||||
|
Some(device.config_scroll_natural_scroll_enabled())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
scroll_button: if device.config_scroll_method() == Some(ScrollMethod::OnButtonDown) {
|
||||||
|
Some(device.config_scroll_button())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else { None },
|
||||||
|
tap_config: if device.config_tap_finger_count() > 0 {
|
||||||
|
Some(TapConfig {
|
||||||
|
enabled: device.config_tap_enabled(),
|
||||||
|
button_map: device.config_tap_button_map(),
|
||||||
|
drag: device.config_tap_drag_enabled(),
|
||||||
|
drag_lock: device.config_tap_drag_lock_enabled(),
|
||||||
|
})
|
||||||
|
} else { None },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PersistenceGuard<'a, T: Serialize>(Option<PathBuf>, &'a mut T);
|
||||||
|
|
||||||
|
impl<'a, T: Serialize> std::ops::Deref for PersistenceGuard<'a, T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Serialize> std::ops::DerefMut for PersistenceGuard<'a, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Serialize> Drop for PersistenceGuard<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(path) = self.0.as_ref() {
|
||||||
|
let writer = match OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
|
Ok(writer) => writer,
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(err) = ron::ser::to_writer_pretty(writer, &self.1, Default::default()) {
|
||||||
|
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynamicConfig {
|
||||||
|
pub fn outputs(&self) -> &OutputsConfig {
|
||||||
|
&self.outputs.1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn outputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, OutputsConfig> {
|
||||||
|
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inputs(&self) -> &InputsConfig {
|
||||||
|
&self.inputs.1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, InputsConfig> {
|
||||||
|
PersistenceGuard(self.inputs.0.clone(), &mut self.inputs.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||||
|
pub enum KeyModifier {
|
||||||
|
Ctrl,
|
||||||
|
Alt,
|
||||||
|
Shift,
|
||||||
|
Logo,
|
||||||
|
CapsLock,
|
||||||
|
NumLock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign<KeyModifier> for KeyModifiers {
|
||||||
|
fn add_assign(&mut self, rhs: KeyModifier) {
|
||||||
|
match rhs {
|
||||||
|
KeyModifier::Ctrl => self.ctrl = true,
|
||||||
|
KeyModifier::Alt => self.alt = true,
|
||||||
|
KeyModifier::Shift => self.shift = true,
|
||||||
|
KeyModifier::Logo => self.logo = true,
|
||||||
|
KeyModifier::CapsLock => self.caps_lock = true,
|
||||||
|
KeyModifier::NumLock => self.num_lock = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::BitOr for KeyModifier {
|
||||||
|
type Output = KeyModifiers;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: KeyModifier) -> Self::Output {
|
||||||
|
let mut modifiers = self.into();
|
||||||
|
modifiers += rhs;
|
||||||
|
modifiers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<KeyModifiers> for KeyModifier {
|
||||||
|
fn into(self) -> KeyModifiers {
|
||||||
|
let mut modifiers = KeyModifiers {
|
||||||
|
ctrl: false,
|
||||||
|
alt: false,
|
||||||
|
shift: false,
|
||||||
|
caps_lock: false,
|
||||||
|
logo: false,
|
||||||
|
num_lock: false,
|
||||||
|
};
|
||||||
|
modifiers += self;
|
||||||
|
modifiers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describtion of a key combination that might be
|
||||||
|
/// handled by the compositor.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct KeyPattern {
|
||||||
|
/// What modifiers are expected to be pressed alongside the key
|
||||||
|
#[serde(deserialize_with = "deserialize_KeyModifiers")]
|
||||||
|
pub modifiers: KeyModifiers,
|
||||||
|
/// The actual key, that was pressed
|
||||||
|
#[serde(deserialize_with = "deserialize_Keysym")]
|
||||||
|
pub key: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyPattern {
|
||||||
|
pub fn new(modifiers: impl Into<KeyModifiers>, key: u32) -> KeyPattern {
|
||||||
|
KeyPattern {
|
||||||
|
modifiers: modifiers.into(),
|
||||||
|
key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Action {
|
||||||
|
Terminate,
|
||||||
|
Debug,
|
||||||
|
Close,
|
||||||
|
Workspace(u8),
|
||||||
|
MoveToWorkspace(u8),
|
||||||
|
Focus(FocusDirection),
|
||||||
|
Orientation(crate::shell::layout::Orientation),
|
||||||
|
Fullscreen,
|
||||||
|
Spawn(String),
|
||||||
|
}
|
||||||
251
src/config/types.rs
Normal file
251
src/config/types.rs
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use super::KeyModifier;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
pub use smithay::{
|
||||||
|
backend::input::KeyState,
|
||||||
|
reexports::input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap},
|
||||||
|
utils::{Logical, Physical, Point, Size, Transform},
|
||||||
|
wayland::{
|
||||||
|
output::{Mode, Output},
|
||||||
|
seat::{keysyms as KeySyms, Keysym, ModifiersState as KeyModifiers},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct XkbConfig {
|
||||||
|
pub rules: String,
|
||||||
|
pub model: String,
|
||||||
|
pub layout: String,
|
||||||
|
pub variant: String,
|
||||||
|
pub options: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for XkbConfig {
|
||||||
|
fn default() -> XkbConfig {
|
||||||
|
XkbConfig {
|
||||||
|
rules: String::new(),
|
||||||
|
model: String::new(),
|
||||||
|
layout: String::new(),
|
||||||
|
variant: String::new(),
|
||||||
|
options: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod ClickMethodDef {
|
||||||
|
use smithay::reexports::input::ClickMethod as ClickMethodOrig;
|
||||||
|
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum ClickMethod {
|
||||||
|
ButtonAreas,
|
||||||
|
Clickfinger,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ClickMethodOrig>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let o = Option::deserialize(deserializer)?;
|
||||||
|
Ok(o.map(|x| match x {
|
||||||
|
ClickMethod::ButtonAreas => ClickMethodOrig::ButtonAreas,
|
||||||
|
ClickMethod::Clickfinger => ClickMethodOrig::Clickfinger,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(arg: &Option<ClickMethodOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer
|
||||||
|
{
|
||||||
|
let arg = match arg {
|
||||||
|
Some(ClickMethodOrig::ButtonAreas) => Some(ClickMethod::ButtonAreas),
|
||||||
|
Some(ClickMethodOrig::Clickfinger) => Some(ClickMethod::Clickfinger),
|
||||||
|
Some(_) | None => None,
|
||||||
|
};
|
||||||
|
Option::serialize(&arg, ser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod AccelProfileDef {
|
||||||
|
use smithay::reexports::input::AccelProfile as AccelProfileOrig;
|
||||||
|
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
enum AccelProfile {
|
||||||
|
Flat,
|
||||||
|
Adaptive,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<AccelProfileOrig>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let o = Option::deserialize(deserializer)?;
|
||||||
|
Ok(o.map(|x| match x {
|
||||||
|
AccelProfile::Flat => AccelProfileOrig::Flat,
|
||||||
|
AccelProfile::Adaptive => AccelProfileOrig::Adaptive,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(arg: &Option<AccelProfileOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer
|
||||||
|
{
|
||||||
|
let arg = match arg {
|
||||||
|
Some(AccelProfileOrig::Flat) => Some(AccelProfile::Flat),
|
||||||
|
Some(AccelProfileOrig::Adaptive) => Some(AccelProfile::Adaptive),
|
||||||
|
Some(_) | None => None,
|
||||||
|
};
|
||||||
|
Option::serialize(&arg, ser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod ScrollMethodDef {
|
||||||
|
use smithay::reexports::input::ScrollMethod as ScrollMethodOrig;
|
||||||
|
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum ScrollMethod {
|
||||||
|
NoScroll,
|
||||||
|
TwoFinger,
|
||||||
|
Edge,
|
||||||
|
OnButtonDown,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ScrollMethodOrig>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let o = Option::deserialize(deserializer)?;
|
||||||
|
Ok(o.map(|x| match x {
|
||||||
|
ScrollMethod::NoScroll => ScrollMethodOrig::NoScroll,
|
||||||
|
ScrollMethod::TwoFinger => ScrollMethodOrig::TwoFinger,
|
||||||
|
ScrollMethod::Edge => ScrollMethodOrig::Edge,
|
||||||
|
ScrollMethod::OnButtonDown => ScrollMethodOrig::OnButtonDown,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(arg: &Option<ScrollMethodOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer
|
||||||
|
{
|
||||||
|
let arg = match arg {
|
||||||
|
Some(ScrollMethodOrig::NoScroll) => Some(ScrollMethod::NoScroll),
|
||||||
|
Some(ScrollMethodOrig::TwoFinger) => Some(ScrollMethod::TwoFinger),
|
||||||
|
Some(ScrollMethodOrig::Edge) => Some(ScrollMethod::Edge),
|
||||||
|
Some(ScrollMethodOrig::OnButtonDown) => Some(ScrollMethod::OnButtonDown),
|
||||||
|
Some(_) | None => None,
|
||||||
|
};
|
||||||
|
Option::serialize(&arg, ser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(remote = "Transform")]
|
||||||
|
pub enum TransformDef {
|
||||||
|
Normal,
|
||||||
|
_90,
|
||||||
|
_180,
|
||||||
|
_270,
|
||||||
|
Flipped,
|
||||||
|
Flipped90,
|
||||||
|
Flipped180,
|
||||||
|
Flipped270,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod TapButtonMapDef {
|
||||||
|
use smithay::reexports::input::TapButtonMap as TapButtonMapOrig;
|
||||||
|
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum TapButtonMap {
|
||||||
|
LeftRightMiddle,
|
||||||
|
LeftMiddleRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<TapButtonMapOrig>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let o = Option::deserialize(deserializer)?;
|
||||||
|
Ok(o.map(|x| match x {
|
||||||
|
TapButtonMap::LeftRightMiddle => TapButtonMapOrig::LeftRightMiddle,
|
||||||
|
TapButtonMap::LeftMiddleRight => TapButtonMapOrig::LeftMiddleRight,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(arg: &Option<TapButtonMapOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer
|
||||||
|
{
|
||||||
|
let arg = match arg {
|
||||||
|
Some(TapButtonMapOrig::LeftRightMiddle) => Some(TapButtonMap::LeftRightMiddle),
|
||||||
|
Some(TapButtonMapOrig::LeftMiddleRight) => Some(TapButtonMap::LeftMiddleRight),
|
||||||
|
Some(_) | None => None,
|
||||||
|
};
|
||||||
|
Option::serialize(&arg, ser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct KeyModifiersDef(Vec<KeyModifier>);
|
||||||
|
|
||||||
|
impl From<KeyModifiersDef> for KeyModifiers {
|
||||||
|
fn from(src: KeyModifiersDef) -> Self {
|
||||||
|
src.0.into_iter().fold(
|
||||||
|
KeyModifiers {
|
||||||
|
ctrl: false,
|
||||||
|
alt: false,
|
||||||
|
shift: false,
|
||||||
|
caps_lock: false,
|
||||||
|
logo: false,
|
||||||
|
num_lock: false,
|
||||||
|
},
|
||||||
|
|mut modis, modi: KeyModifier| {
|
||||||
|
modis += modi;
|
||||||
|
modis
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn deserialize_KeyModifiers<'de, D>(deserializer: D) -> Result<KeyModifiers, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
KeyModifiersDef::deserialize(deserializer).map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn deserialize_Keysym<'de, D>(deserializer: D) -> Result<Keysym, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::de::{Error, Unexpected};
|
||||||
|
|
||||||
|
let name = String::deserialize(deserializer)?;
|
||||||
|
//let name = format!("KEY_{}", code);
|
||||||
|
match xkb::keysym_from_name(&name, xkb::KEYSYM_NO_FLAGS) {
|
||||||
|
KeySyms::KEY_NoSymbol => match xkb::keysym_from_name(&name, xkb::KEYSYM_CASE_INSENSITIVE) {
|
||||||
|
KeySyms::KEY_NoSymbol => Err(<D::Error as Error>::invalid_value(
|
||||||
|
Unexpected::Str(&name),
|
||||||
|
&"One of the keysym names of xkbcommon.h without the 'KEY_' prefix",
|
||||||
|
)),
|
||||||
|
x => {
|
||||||
|
slog_scope::warn!(
|
||||||
|
"Key-Binding '{}' only matched case insensitive for {:?}",
|
||||||
|
name,
|
||||||
|
xkb::keysym_get_name(x)
|
||||||
|
);
|
||||||
|
Ok(x)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x => Ok(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
362
src/input/mod.rs
362
src/input/mod.rs
|
|
@ -1,19 +1,20 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::{config::Action, state::Common};
|
use crate::{config::Action, state::{Common, State}, shell::Workspace};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState},
|
backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState},
|
||||||
desktop::{layer_map_for_output, Kind, Space, WindowSurfaceType},
|
desktop::{layer_map_for_output, Kind, Space, WindowSurfaceType},
|
||||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Display},
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||||
utils::{Logical, Point},
|
utils::{Logical, Point, Rectangle},
|
||||||
wayland::{
|
wayland::{
|
||||||
data_device::set_data_device_focus,
|
data_device::set_data_device_focus,
|
||||||
output::Output,
|
output::Output,
|
||||||
seat::{CursorImageStatus, FilterResult, KeysymHandle, Seat, XkbConfig},
|
seat::{CursorImageStatus, FilterResult, KeysymHandle, Seat, XkbConfig, keysyms},
|
||||||
shell::wlr_layer::Layer as WlrLayer,
|
shell::wlr_layer::Layer as WlrLayer,
|
||||||
SERIAL_COUNTER,
|
SERIAL_COUNTER,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use xkbcommon::xkb::KEY_XF86Switch_VT_12;
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
pub struct ActiveOutput(pub RefCell<Output>);
|
pub struct ActiveOutput(pub RefCell<Output>);
|
||||||
|
|
@ -114,13 +115,13 @@ pub fn set_active_output(seat: &Seat, output: &Output) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Common {
|
impl State {
|
||||||
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
|
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
|
||||||
use smithay::backend::input::Event;
|
use smithay::backend::input::Event;
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
InputEvent::DeviceAdded { device } => {
|
InputEvent::DeviceAdded { device } => {
|
||||||
let seat = &mut self.last_active_seat;
|
let seat = &mut self.common.last_active_seat;
|
||||||
let userdata = seat.user_data();
|
let userdata = seat.user_data();
|
||||||
let devices = userdata.get::<Devices>().unwrap();
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
for cap in devices.add_device(&device) {
|
for cap in devices.add_device(&device) {
|
||||||
|
|
@ -136,6 +137,7 @@ impl Common {
|
||||||
}
|
}
|
||||||
DeviceCapability::Pointer => {
|
DeviceCapability::Pointer => {
|
||||||
let output = self
|
let output = self
|
||||||
|
.common
|
||||||
.shell
|
.shell
|
||||||
.outputs()
|
.outputs()
|
||||||
.next()
|
.next()
|
||||||
|
|
@ -157,12 +159,12 @@ impl Common {
|
||||||
}
|
}
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
{
|
{
|
||||||
self.egui.debug_state.handle_device_added(&device);
|
self.common.egui.debug_state.handle_device_added(&device);
|
||||||
self.egui.log_state.handle_device_added(&device);
|
self.common.egui.log_state.handle_device_added(&device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputEvent::DeviceRemoved { device } => {
|
InputEvent::DeviceRemoved { device } => {
|
||||||
for seat in &mut self.seats {
|
for seat in &mut self.common.seats {
|
||||||
let userdata = seat.user_data();
|
let userdata = seat.user_data();
|
||||||
let devices = userdata.get::<Devices>().unwrap();
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
if devices.has_device(&device) {
|
if devices.has_device(&device) {
|
||||||
|
|
@ -182,15 +184,15 @@ impl Common {
|
||||||
}
|
}
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
{
|
{
|
||||||
self.egui.debug_state.handle_device_added(&device);
|
self.common.egui.debug_state.handle_device_added(&device);
|
||||||
self.egui.log_state.handle_device_added(&device);
|
self.common.egui.log_state.handle_device_added(&device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputEvent::Keyboard { event, .. } => {
|
InputEvent::Keyboard { event, .. } => {
|
||||||
use smithay::backend::input::KeyboardKeyEvent;
|
use smithay::backend::input::KeyboardKeyEvent;
|
||||||
|
|
||||||
let device = event.device();
|
let device = event.device();
|
||||||
for seat in self.seats.clone().iter() {
|
for seat in self.common.seats.clone().iter() {
|
||||||
let userdata = seat.user_data();
|
let userdata = seat.user_data();
|
||||||
let devices = userdata.get::<Devices>().unwrap();
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
if devices.has_device(&device) {
|
if devices.has_device(&device) {
|
||||||
|
|
@ -212,11 +214,11 @@ impl Common {
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
{
|
{
|
||||||
if self.seats.iter().position(|x| x == seat).unwrap() == 0
|
if self.common.seats.iter().position(|x| x == seat).unwrap() == 0
|
||||||
&& self.egui.active
|
&& self.common.egui.active
|
||||||
{
|
{
|
||||||
if self.egui.debug_state.wants_keyboard() {
|
if self.common.egui.debug_state.wants_keyboard() {
|
||||||
self.egui.debug_state.handle_keyboard(
|
self.common.egui.debug_state.handle_keyboard(
|
||||||
&handle,
|
&handle,
|
||||||
state == KeyState::Pressed,
|
state == KeyState::Pressed,
|
||||||
modifiers.clone(),
|
modifiers.clone(),
|
||||||
|
|
@ -224,8 +226,8 @@ impl Common {
|
||||||
userdata.get::<SupressedKeys>().unwrap().add(&handle);
|
userdata.get::<SupressedKeys>().unwrap().add(&handle);
|
||||||
return FilterResult::Intercept(None);
|
return FilterResult::Intercept(None);
|
||||||
}
|
}
|
||||||
if self.egui.log_state.wants_keyboard() {
|
if self.common.egui.log_state.wants_keyboard() {
|
||||||
self.egui.log_state.handle_keyboard(
|
self.common.egui.log_state.handle_keyboard(
|
||||||
&handle,
|
&handle,
|
||||||
state == KeyState::Pressed,
|
state == KeyState::Pressed,
|
||||||
modifiers.clone(),
|
modifiers.clone(),
|
||||||
|
|
@ -236,8 +238,16 @@ impl Common {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state == KeyState::Pressed && (keysyms::KEY_XF86Switch_VT_1..=KEY_XF86Switch_VT_12).contains(&handle.modified_sym()) {
|
||||||
|
if let Err(err) = self.backend.kms().switch_vt((handle.modified_sym() - keysyms::KEY_XF86Switch_VT_1 + 1) as i32) {
|
||||||
|
slog_scope::error!("Failed switching virtual terminal: {}", err);
|
||||||
|
}
|
||||||
|
userdata.get::<SupressedKeys>().unwrap().add(&handle);
|
||||||
|
return FilterResult::Intercept(None);
|
||||||
|
}
|
||||||
|
|
||||||
// here we can handle global shortcuts and the like
|
// here we can handle global shortcuts and the like
|
||||||
for (binding, action) in self.config.static_conf.key_bindings.iter()
|
for (binding, action) in self.common.config.static_conf.key_bindings.iter()
|
||||||
{
|
{
|
||||||
if state == KeyState::Pressed
|
if state == KeyState::Pressed
|
||||||
&& binding.modifiers == *modifiers
|
&& binding.modifiers == *modifiers
|
||||||
|
|
@ -254,19 +264,19 @@ impl Common {
|
||||||
{
|
{
|
||||||
match action {
|
match action {
|
||||||
Action::Terminate => {
|
Action::Terminate => {
|
||||||
self.should_stop = true;
|
self.common.should_stop = true;
|
||||||
}
|
}
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
Action::Debug => {
|
Action::Debug => {
|
||||||
self.egui.active = !self.egui.active;
|
self.common.egui.active = !self.common.egui.active;
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "debug"))]
|
#[cfg(not(feature = "debug"))]
|
||||||
Action::Debug => {
|
Action::Debug => {
|
||||||
slog_scope::info!("Debug overlay not included in this version")
|
slog_scope::info!("Debug overlay not included in this version")
|
||||||
}
|
}
|
||||||
Action::Close => {
|
Action::Close => {
|
||||||
let current_output = active_output(seat, &self);
|
let current_output = active_output(seat, &self.common);
|
||||||
let workspace = self.shell.active_space_mut(¤t_output);
|
let workspace = self.common.shell.active_space_mut(¤t_output);
|
||||||
if let Some(window) = workspace.focus_stack(seat).last() {
|
if let Some(window) = workspace.focus_stack(seat).last() {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
if let Kind::Xdg(xdg) = &window.toplevel() {
|
if let Kind::Xdg(xdg) = &window.toplevel() {
|
||||||
|
|
@ -275,44 +285,52 @@ impl Common {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::Workspace(key_num) => {
|
Action::Workspace(key_num) => {
|
||||||
let current_output = active_output(seat, &self);
|
let current_output = active_output(seat, &self.common);
|
||||||
let workspace = match key_num {
|
let workspace = match key_num {
|
||||||
0 => 9,
|
0 => 9,
|
||||||
x => x - 1,
|
x => x - 1,
|
||||||
};
|
};
|
||||||
self.shell
|
self.common.shell
|
||||||
.activate(seat, ¤t_output, workspace as usize);
|
.activate(seat, ¤t_output, workspace as usize);
|
||||||
}
|
}
|
||||||
Action::MoveToWorkspace(key_num) => {
|
Action::MoveToWorkspace(key_num) => {
|
||||||
let current_output = active_output(seat, &self);
|
let current_output = active_output(seat, &self.common);
|
||||||
let workspace = match key_num {
|
let workspace = match key_num {
|
||||||
0 => 9,
|
0 => 9,
|
||||||
x => x - 1,
|
x => x - 1,
|
||||||
};
|
};
|
||||||
self.shell.move_current_window(
|
self.common.shell.move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
¤t_output,
|
||||||
workspace as usize,
|
workspace as usize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Action::Focus(focus) => {
|
Action::Focus(focus) => {
|
||||||
let current_output = active_output(seat, &self);
|
let current_output = active_output(seat, &self.common);
|
||||||
self.shell.move_focus(
|
self.common.shell.move_focus(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
¤t_output,
|
||||||
*focus,
|
*focus,
|
||||||
self.seats.iter(),
|
self.common.seats.iter(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Action::Fullscreen => {
|
||||||
|
let current_output = active_output(seat, &self.common);
|
||||||
|
let workspace = self.common.shell.active_space_mut(¤t_output);
|
||||||
|
let focused_window = workspace.focus_stack(seat).last();
|
||||||
|
if let Some(window) = focused_window {
|
||||||
|
workspace.fullscreen_toggle(&window, ¤t_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
Action::Orientation(orientation) => {
|
Action::Orientation(orientation) => {
|
||||||
let output = active_output(seat, &self);
|
let output = active_output(seat, &self.common);
|
||||||
self.shell.set_orientation(&seat, &output, *orientation);
|
self.common.shell.set_orientation(&seat, &output, *orientation);
|
||||||
}
|
}
|
||||||
Action::Spawn(command) => {
|
Action::Spawn(command) => {
|
||||||
if let Err(err) = std::process::Command::new("/bin/sh")
|
if let Err(err) = std::process::Command::new("/bin/sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(command)
|
.arg(command)
|
||||||
.env("WAYLAND_DISPLAY", &self.socket)
|
.env("WAYLAND_DISPLAY", &self.common.socket)
|
||||||
.spawn()
|
.spawn()
|
||||||
{
|
{
|
||||||
slog_scope::warn!("Failed to spawn: {}", err);
|
slog_scope::warn!("Failed to spawn: {}", err);
|
||||||
|
|
@ -328,20 +346,22 @@ impl Common {
|
||||||
use smithay::backend::input::PointerMotionEvent;
|
use smithay::backend::input::PointerMotionEvent;
|
||||||
|
|
||||||
let device = event.device();
|
let device = event.device();
|
||||||
for seat in self.seats.clone().iter() {
|
for seat in self.common.seats.clone().iter() {
|
||||||
let userdata = seat.user_data();
|
let userdata = seat.user_data();
|
||||||
let devices = userdata.get::<Devices>().unwrap();
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
if devices.has_device(&device) {
|
if devices.has_device(&device) {
|
||||||
let current_output = active_output(seat, &self);
|
let current_output = active_output(seat, &self.common);
|
||||||
|
|
||||||
let mut position = seat.get_pointer().unwrap().current_location();
|
let mut position = seat.get_pointer().unwrap().current_location();
|
||||||
position += event.delta();
|
position += event.delta();
|
||||||
|
|
||||||
let output = self
|
let output = self
|
||||||
|
.common
|
||||||
.shell
|
.shell
|
||||||
.outputs()
|
.outputs()
|
||||||
.find(|output| {
|
.find(|output| {
|
||||||
self.shell
|
self.common
|
||||||
|
.shell
|
||||||
.output_geometry(output)
|
.output_geometry(output)
|
||||||
.to_f64()
|
.to_f64()
|
||||||
.contains(position)
|
.contains(position)
|
||||||
|
|
@ -351,7 +371,7 @@ impl Common {
|
||||||
if output != current_output {
|
if output != current_output {
|
||||||
set_active_output(seat, &output);
|
set_active_output(seat, &output);
|
||||||
}
|
}
|
||||||
let output_geometry = self.shell.output_geometry(&output);
|
let output_geometry = self.common.shell.output_geometry(&output);
|
||||||
|
|
||||||
position.x = 0.0f64
|
position.x = 0.0f64
|
||||||
.max(position.x)
|
.max(position.x)
|
||||||
|
|
@ -362,13 +382,14 @@ impl Common {
|
||||||
|
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
let relative_pos =
|
let relative_pos =
|
||||||
self.shell.space_relative_output_geometry(position, &output);
|
self.common.shell.space_relative_output_geometry(position, &output);
|
||||||
let workspace = self.shell.active_space_mut(&output);
|
let workspace = self.common.shell.active_space_mut(&output);
|
||||||
let under = Common::surface_under(
|
let under = State::surface_under(
|
||||||
position,
|
position,
|
||||||
relative_pos,
|
relative_pos,
|
||||||
&output,
|
&output,
|
||||||
&workspace.space,
|
output_geometry,
|
||||||
|
&workspace,
|
||||||
);
|
);
|
||||||
handle_window_movement(
|
handle_window_movement(
|
||||||
under.as_ref().map(|(s, _)| s),
|
under.as_ref().map(|(s, _)| s),
|
||||||
|
|
@ -379,11 +400,11 @@ impl Common {
|
||||||
.motion(position, under, serial, event.time());
|
.motion(position, under, serial, event.time());
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
if self.seats.iter().position(|x| x == seat).unwrap() == 0 {
|
if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 {
|
||||||
self.egui
|
self.common.egui
|
||||||
.debug_state
|
.debug_state
|
||||||
.handle_pointer_motion(position.to_i32_round());
|
.handle_pointer_motion(position.to_i32_round());
|
||||||
self.egui
|
self.common.egui
|
||||||
.log_state
|
.log_state
|
||||||
.handle_pointer_motion(position.to_i32_round());
|
.handle_pointer_motion(position.to_i32_round());
|
||||||
}
|
}
|
||||||
|
|
@ -395,23 +416,24 @@ impl Common {
|
||||||
use smithay::backend::input::PointerMotionAbsoluteEvent;
|
use smithay::backend::input::PointerMotionAbsoluteEvent;
|
||||||
|
|
||||||
let device = event.device();
|
let device = event.device();
|
||||||
for seat in self.seats.clone().iter() {
|
for seat in self.common.seats.clone().iter() {
|
||||||
let userdata = seat.user_data();
|
let userdata = seat.user_data();
|
||||||
let devices = userdata.get::<Devices>().unwrap();
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
if devices.has_device(&device) {
|
if devices.has_device(&device) {
|
||||||
let output = active_output(seat, &self);
|
let output = active_output(seat, &self.common);
|
||||||
let geometry = self.shell.output_geometry(&output);
|
let geometry = self.common.shell.output_geometry(&output);
|
||||||
let position =
|
let position =
|
||||||
geometry.loc.to_f64() + event.position_transformed(geometry.size);
|
geometry.loc.to_f64() + event.position_transformed(geometry.size);
|
||||||
let relative_pos =
|
let relative_pos =
|
||||||
self.shell.space_relative_output_geometry(position, &output);
|
self.common.shell.space_relative_output_geometry(position, &output);
|
||||||
let workspace = self.shell.active_space_mut(&output);
|
let workspace = self.common.shell.active_space_mut(&output);
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
let under = Common::surface_under(
|
let under = State::surface_under(
|
||||||
position,
|
position,
|
||||||
relative_pos,
|
relative_pos,
|
||||||
&output,
|
&output,
|
||||||
&workspace.space,
|
geometry,
|
||||||
|
&workspace,
|
||||||
);
|
);
|
||||||
handle_window_movement(
|
handle_window_movement(
|
||||||
under.as_ref().map(|(s, _)| s),
|
under.as_ref().map(|(s, _)| s),
|
||||||
|
|
@ -422,11 +444,11 @@ impl Common {
|
||||||
.motion(position, under, serial, event.time());
|
.motion(position, under, serial, event.time());
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
if self.seats.iter().position(|x| x == seat).unwrap() == 0 {
|
if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 {
|
||||||
self.egui
|
self.common.egui
|
||||||
.debug_state
|
.debug_state
|
||||||
.handle_pointer_motion(position.to_i32_round());
|
.handle_pointer_motion(position.to_i32_round());
|
||||||
self.egui
|
self.common.egui
|
||||||
.log_state
|
.log_state
|
||||||
.handle_pointer_motion(position.to_i32_round());
|
.handle_pointer_motion(position.to_i32_round());
|
||||||
}
|
}
|
||||||
|
|
@ -441,30 +463,30 @@ impl Common {
|
||||||
};
|
};
|
||||||
|
|
||||||
let device = event.device();
|
let device = event.device();
|
||||||
for seat in self.seats.clone().iter() {
|
for seat in self.common.seats.clone().iter() {
|
||||||
let userdata = seat.user_data();
|
let userdata = seat.user_data();
|
||||||
let devices = userdata.get::<Devices>().unwrap();
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
if devices.has_device(&device) {
|
if devices.has_device(&device) {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
if self.seats.iter().position(|x| x == seat).unwrap() == 0
|
if self.common.seats.iter().position(|x| x == seat).unwrap() == 0
|
||||||
&& self.egui.active
|
&& self.common.egui.active
|
||||||
{
|
{
|
||||||
if self.egui.debug_state.wants_pointer() {
|
if self.common.egui.debug_state.wants_pointer() {
|
||||||
if let Some(button) = event.button() {
|
if let Some(button) = event.button() {
|
||||||
self.egui.debug_state.handle_pointer_button(
|
self.common.egui.debug_state.handle_pointer_button(
|
||||||
button,
|
button,
|
||||||
event.state() == ButtonState::Pressed,
|
event.state() == ButtonState::Pressed,
|
||||||
self.egui.modifiers.clone(),
|
self.common.egui.modifiers.clone(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if self.egui.log_state.wants_pointer() {
|
if self.common.egui.log_state.wants_pointer() {
|
||||||
if let Some(button) = event.button() {
|
if let Some(button) = event.button() {
|
||||||
self.egui.log_state.handle_pointer_button(
|
self.common.egui.log_state.handle_pointer_button(
|
||||||
button,
|
button,
|
||||||
event.state() == ButtonState::Pressed,
|
event.state() == ButtonState::Pressed,
|
||||||
self.egui.modifiers.clone(),
|
self.common.egui.modifiers.clone(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -477,58 +499,76 @@ impl Common {
|
||||||
ButtonState::Pressed => {
|
ButtonState::Pressed => {
|
||||||
// change the keyboard focus unless the pointer is grabbed
|
// change the keyboard focus unless the pointer is grabbed
|
||||||
if !seat.get_pointer().unwrap().is_grabbed() {
|
if !seat.get_pointer().unwrap().is_grabbed() {
|
||||||
let output = active_output(seat, &self);
|
let output = active_output(seat, &self.common);
|
||||||
let pos = seat.get_pointer().unwrap().current_location();
|
let pos = seat.get_pointer().unwrap().current_location();
|
||||||
let output_geo = self.shell.output_geometry(&output);
|
let output_geo = self.common.shell.output_geometry(&output);
|
||||||
let relative_pos =
|
let relative_pos =
|
||||||
self.shell.space_relative_output_geometry(pos, &output);
|
self.common.shell.space_relative_output_geometry(pos, &output);
|
||||||
let workspace = self.shell.active_space_mut(&output);
|
let workspace = self.common.shell.active_space_mut(&output);
|
||||||
let layers = layer_map_for_output(&output);
|
let layers = layer_map_for_output(&output);
|
||||||
let mut under = None;
|
let mut under = None;
|
||||||
|
|
||||||
if let Some(layer) = layers
|
if let Some(window) = workspace.get_fullscreen(&output) {
|
||||||
.layer_under(WlrLayer::Overlay, relative_pos)
|
if let Some(layer) = layers
|
||||||
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
|
.layer_under(WlrLayer::Overlay, relative_pos)
|
||||||
{
|
{
|
||||||
if layer.can_receive_keyboard_focus() {
|
if layer.can_receive_keyboard_focus() {
|
||||||
let layer_loc =
|
let layer_loc =
|
||||||
layers.layer_geometry(layer).unwrap().loc;
|
layers.layer_geometry(layer).unwrap().loc;
|
||||||
under = layer
|
under = layer
|
||||||
.surface_under(
|
.surface_under(
|
||||||
pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
pos - output_geo.loc.to_f64()
|
||||||
WindowSurfaceType::ALL,
|
- layer_loc.to_f64(),
|
||||||
)
|
WindowSurfaceType::ALL,
|
||||||
.map(|(s, _)| s);
|
)
|
||||||
}
|
.map(|(s, _)| s);
|
||||||
} else if let Some(window) =
|
}
|
||||||
workspace.space.window_under(relative_pos).cloned()
|
} else {
|
||||||
{
|
under = window.surface_under(
|
||||||
let window_loc =
|
pos - output_geo.loc.to_f64(),
|
||||||
workspace.space.window_location(&window).unwrap();
|
|
||||||
under = window
|
|
||||||
.surface_under(
|
|
||||||
relative_pos - window_loc.to_f64(),
|
|
||||||
WindowSurfaceType::TOPLEVEL
|
WindowSurfaceType::TOPLEVEL
|
||||||
| WindowSurfaceType::SUBSURFACE,
|
| WindowSurfaceType::SUBSURFACE
|
||||||
)
|
).map(|(s, _)| s);
|
||||||
.map(|(s, _)| s);
|
|
||||||
} else if let Some(layer) = layers
|
|
||||||
.layer_under(WlrLayer::Bottom, pos)
|
|
||||||
.or_else(|| layers.layer_under(WlrLayer::Background, pos))
|
|
||||||
{
|
|
||||||
if layer.can_receive_keyboard_focus() {
|
|
||||||
let layer_loc =
|
|
||||||
layers.layer_geometry(layer).unwrap().loc;
|
|
||||||
under = layer
|
|
||||||
.surface_under(
|
|
||||||
pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
|
||||||
WindowSurfaceType::ALL,
|
|
||||||
)
|
|
||||||
.map(|(s, _)| s);
|
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
|
if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Overlay, relative_pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
|
||||||
|
{
|
||||||
|
if layer.can_receive_keyboard_focus() {
|
||||||
|
let layer_loc =
|
||||||
|
layers.layer_geometry(layer).unwrap().loc;
|
||||||
|
under = layer
|
||||||
|
.surface_under(
|
||||||
|
pos - output_geo.loc.to_f64()
|
||||||
|
- layer_loc.to_f64(),
|
||||||
|
WindowSurfaceType::ALL,
|
||||||
|
)
|
||||||
|
.map(|(s, _)| s);
|
||||||
|
}
|
||||||
|
} else if let Some((_, surface, _)) =
|
||||||
|
workspace.space.surface_under(relative_pos, WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE)
|
||||||
|
{
|
||||||
|
under = Some(surface);
|
||||||
|
} else if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Bottom, pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Background, pos))
|
||||||
|
{
|
||||||
|
if layer.can_receive_keyboard_focus() {
|
||||||
|
let layer_loc =
|
||||||
|
layers.layer_geometry(layer).unwrap().loc;
|
||||||
|
under = layer
|
||||||
|
.surface_under(
|
||||||
|
pos - output_geo.loc.to_f64()
|
||||||
|
- layer_loc.to_f64(),
|
||||||
|
WindowSurfaceType::ALL,
|
||||||
|
)
|
||||||
|
.map(|(s, _)| s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
self.set_focus(under.as_ref(), seat, Some(serial));
|
self.common.set_focus(under.as_ref(), seat, Some(serial));
|
||||||
}
|
}
|
||||||
wl_pointer::ButtonState::Pressed
|
wl_pointer::ButtonState::Pressed
|
||||||
}
|
}
|
||||||
|
|
@ -549,11 +589,11 @@ impl Common {
|
||||||
};
|
};
|
||||||
|
|
||||||
let device = event.device();
|
let device = event.device();
|
||||||
for seat in self.seats.clone().iter() {
|
for seat in self.common.seats.clone().iter() {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
if self.seats.iter().position(|x| x == seat).unwrap() == 0 && self.egui.active {
|
if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 && self.common.egui.active {
|
||||||
if self.egui.debug_state.wants_pointer() {
|
if self.common.egui.debug_state.wants_pointer() {
|
||||||
self.egui.debug_state.handle_pointer_axis(
|
self.common.egui.debug_state.handle_pointer_axis(
|
||||||
event
|
event
|
||||||
.amount_discrete(Axis::Horizontal)
|
.amount_discrete(Axis::Horizontal)
|
||||||
.or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0))
|
.or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0))
|
||||||
|
|
@ -565,8 +605,8 @@ impl Common {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if self.egui.log_state.wants_pointer() {
|
if self.common.egui.log_state.wants_pointer() {
|
||||||
self.egui.log_state.handle_pointer_axis(
|
self.common.egui.log_state.handle_pointer_axis(
|
||||||
event
|
event
|
||||||
.amount_discrete(Axis::Horizontal)
|
.amount_discrete(Axis::Horizontal)
|
||||||
.or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0))
|
.or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0))
|
||||||
|
|
@ -640,55 +680,63 @@ impl Common {
|
||||||
global_pos: Point<f64, Logical>,
|
global_pos: Point<f64, Logical>,
|
||||||
relative_pos: Point<f64, Logical>,
|
relative_pos: Point<f64, Logical>,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
space: &Space,
|
output_geo: Rectangle<i32, Logical>,
|
||||||
|
workspace: &Workspace,
|
||||||
) -> Option<(WlSurface, Point<i32, Logical>)> {
|
) -> Option<(WlSurface, Point<i32, Logical>)> {
|
||||||
let layers = layer_map_for_output(output);
|
let layers = layer_map_for_output(output);
|
||||||
let output_geo = space.output_geometry(output).unwrap();
|
if let Some(window) = workspace.get_fullscreen(output) {
|
||||||
|
if let Some(layer) = layers
|
||||||
if let Some(layer) = layers
|
.layer_under(WlrLayer::Overlay, relative_pos)
|
||||||
.layer_under(WlrLayer::Overlay, relative_pos)
|
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
|
||||||
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
|
{
|
||||||
{
|
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
layer
|
||||||
layer
|
.surface_under(
|
||||||
.surface_under(
|
global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
||||||
global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
WindowSurfaceType::ALL,
|
||||||
WindowSurfaceType::ALL,
|
|
||||||
)
|
|
||||||
.map(|(s, loc)| {
|
|
||||||
(
|
|
||||||
s,
|
|
||||||
loc + layer_loc + output_geo.loc,
|
|
||||||
)
|
)
|
||||||
})
|
.map(|(s, loc)| (s, loc + layer_loc + output_geo.loc))
|
||||||
} else if let Some(window) = space.window_under(relative_pos) {
|
} else {
|
||||||
let window_loc = space.window_location(window).unwrap();
|
window
|
||||||
window
|
.surface_under(global_pos - output_geo.loc.to_f64(), WindowSurfaceType::ALL)
|
||||||
.surface_under(relative_pos - window_loc.to_f64(), WindowSurfaceType::ALL)
|
.map(|(s, loc)| {
|
||||||
.map(|(s, loc)| {
|
(
|
||||||
(
|
s,
|
||||||
s,
|
loc + output_geo.loc,
|
||||||
loc + window_loc - (relative_pos - global_pos).to_i32_round(),
|
)
|
||||||
)
|
})
|
||||||
})
|
}
|
||||||
} else if let Some(layer) = layers
|
|
||||||
.layer_under(WlrLayer::Bottom, relative_pos)
|
|
||||||
.or_else(|| layers.layer_under(WlrLayer::Background, relative_pos))
|
|
||||||
{
|
|
||||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
|
||||||
layer
|
|
||||||
.surface_under(
|
|
||||||
global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
|
||||||
WindowSurfaceType::ALL,
|
|
||||||
)
|
|
||||||
.map(|(s, loc)| {
|
|
||||||
(
|
|
||||||
s,
|
|
||||||
loc + layer_loc + output_geo.loc,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Overlay, relative_pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
|
||||||
|
{
|
||||||
|
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||||
|
layer
|
||||||
|
.surface_under(
|
||||||
|
global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
||||||
|
WindowSurfaceType::ALL,
|
||||||
|
)
|
||||||
|
.map(|(s, loc)| (s, loc + layer_loc + output_geo.loc))
|
||||||
|
} else if let Some((_, surface, loc)) = workspace.space.surface_under(relative_pos, WindowSurfaceType::ALL) {
|
||||||
|
Some((
|
||||||
|
surface,
|
||||||
|
loc + (global_pos - relative_pos).to_i32_round(),
|
||||||
|
))
|
||||||
|
} else if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Bottom, relative_pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Background, relative_pos))
|
||||||
|
{
|
||||||
|
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||||
|
layer
|
||||||
|
.surface_under(
|
||||||
|
global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
||||||
|
WindowSurfaceType::ALL,
|
||||||
|
)
|
||||||
|
.map(|(s, loc)| (s, loc + layer_loc + output_geo.loc))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,18 +30,17 @@ fn main() -> Result<()> {
|
||||||
// init wayland
|
// init wayland
|
||||||
let (display, socket) = init_wayland_display(&mut event_loop)?;
|
let (display, socket) = init_wayland_display(&mut event_loop)?;
|
||||||
// init state
|
// init state
|
||||||
let mut state = state::State::new(display, socket, event_loop.handle(), log);
|
let mut state = state::State::new(display, socket, event_loop.handle(), event_loop.get_signal(), log);
|
||||||
// init backend
|
// init backend
|
||||||
backend::init_backend_auto(&mut event_loop, &mut state)?;
|
backend::init_backend_auto(&mut event_loop, &mut state)?;
|
||||||
|
|
||||||
// run the event loop
|
// run the event loop
|
||||||
let signal = event_loop.get_signal();
|
|
||||||
event_loop.run(None, &mut state, |state| {
|
event_loop.run(None, &mut state, |state| {
|
||||||
// shall we shut down?
|
// shall we shut down?
|
||||||
if state.common.shell.outputs().next().is_none() || state.common.should_stop {
|
if state.common.shell.outputs().next().is_none() || state.common.should_stop {
|
||||||
slog_scope::info!("Shutting down");
|
slog_scope::info!("Shutting down");
|
||||||
signal.stop();
|
state.common.event_loop_signal.stop();
|
||||||
signal.wakeup();
|
state.common.event_loop_signal.wakeup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,7 @@ use smithay::{
|
||||||
seat::{PointerGrabStartData, Seat},
|
seat::{PointerGrabStartData, Seat},
|
||||||
shell::{
|
shell::{
|
||||||
wlr_layer::{
|
wlr_layer::{
|
||||||
wlr_layer_shell_init, KeyboardInteractivity, Layer, LayerShellRequest,
|
wlr_layer_shell_init, LayerShellRequest, LayerSurfaceAttributes,
|
||||||
LayerSurfaceAttributes, LayerSurfaceCachedState,
|
|
||||||
},
|
},
|
||||||
xdg::{
|
xdg::{
|
||||||
xdg_shell_init, Configure, XdgPopupSurfaceRoleAttributes, XdgRequest,
|
xdg_shell_init, Configure, XdgPopupSurfaceRoleAttributes, XdgRequest,
|
||||||
|
|
@ -26,7 +25,6 @@ use smithay::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Serial,
|
Serial,
|
||||||
SERIAL_COUNTER,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{cell::Cell, sync::Mutex};
|
use std::{cell::Cell, sync::Mutex};
|
||||||
|
|
@ -215,6 +213,31 @@ pub fn init_shell(config: &Config, display: &mut Display) -> super::Shell {
|
||||||
.set(Some(grab));
|
.set(Some(grab));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
XdgRequest::Fullscreen { surface, output } => {
|
||||||
|
let output = output
|
||||||
|
.as_ref()
|
||||||
|
.and_then(Output::from_resource)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let seat = &state.last_active_seat;
|
||||||
|
active_output(seat, &*state)
|
||||||
|
});
|
||||||
|
if let Some(surface) = surface.get_surface() {
|
||||||
|
if let Some(workspace) = state.shell.space_for_surface_mut(surface) {
|
||||||
|
let window =
|
||||||
|
workspace.space.window_for_surface(surface).unwrap().clone();
|
||||||
|
workspace.fullscreen_request(&window, &output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XdgRequest::UnFullscreen { surface } => {
|
||||||
|
if let Some(surface) = surface.get_surface() {
|
||||||
|
if let Some(workspace) = state.shell.space_for_surface_mut(surface) {
|
||||||
|
let window =
|
||||||
|
workspace.space.window_for_surface(surface).unwrap().clone();
|
||||||
|
workspace.unfullscreen_request(&window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => { /*TODO*/ }
|
_ => { /*TODO*/ }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -236,26 +259,7 @@ pub fn init_shell(config: &Config, display: &mut Display) -> super::Shell {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(Output::from_resource)
|
.and_then(Output::from_resource)
|
||||||
.unwrap_or_else(|| active_output(&seat, &*state));
|
.unwrap_or_else(|| active_output(&seat, &*state));
|
||||||
|
state.shell.active_space_mut(&output).pending_layer(LayerSurface::new(surface, namespace), &output, &seat);
|
||||||
let focus = surface
|
|
||||||
.get_surface()
|
|
||||||
.map(|surface| {
|
|
||||||
with_states(surface, |states| {
|
|
||||||
let state = states.cached_state.current::<LayerSurfaceCachedState>();
|
|
||||||
matches!(state.layer, Layer::Top | Layer::Overlay)
|
|
||||||
&& state.keyboard_interactivity != KeyboardInteractivity::None
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
let mut map = layer_map_for_output(&output);
|
|
||||||
map.map_layer(&LayerSurface::new(surface.clone(), namespace))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if focus {
|
|
||||||
state.set_focus(surface.get_surface(), &seat, Some(SERIAL_COUNTER.next_serial()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -529,6 +529,10 @@ impl TilingLayout {
|
||||||
if let Some(geo) = geo {
|
if let Some(geo) = geo {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
if let Kind::Xdg(xdg) = &window.toplevel() {
|
if let Kind::Xdg(xdg) = &window.toplevel() {
|
||||||
|
if xdg.current_state().map(|state| state.states.contains(XdgState::Fullscreen)).unwrap_or(false) ||
|
||||||
|
xdg.with_pending_state(|pending| pending.states.contains(XdgState::Fullscreen)).unwrap_or(false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let ret = xdg.with_pending_state(|state| {
|
let ret = xdg.with_pending_state(|state| {
|
||||||
state.size = Some(
|
state.size = Some(
|
||||||
(geo.size.w - inner * 2, geo.size.h - inner * 2)
|
(geo.size.w - inner * 2, geo.size.h - inner * 2)
|
||||||
|
|
@ -543,12 +547,11 @@ impl TilingLayout {
|
||||||
xdg.send_configure();
|
xdg.send_configure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let window_geo = window.geometry();
|
|
||||||
space.map_window(
|
space.map_window(
|
||||||
&window,
|
&window,
|
||||||
(
|
(
|
||||||
geo.loc.x + inner - window_geo.loc.x,
|
geo.loc.x + inner,
|
||||||
geo.loc.y + inner - window_geo.loc.y,
|
geo.loc.y + inner,
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,17 @@ use crate::{
|
||||||
state::Common,
|
state::Common,
|
||||||
};
|
};
|
||||||
pub use smithay::{
|
pub use smithay::{
|
||||||
desktop::{PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window},
|
desktop::{PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window, layer_map_for_output},
|
||||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
utils::{Logical, Point, Rectangle, Size},
|
utils::{Logical, Point, Rectangle, Size},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::with_states,
|
compositor::with_states,
|
||||||
output::{Mode as OutputMode, Output},
|
output::{Mode as OutputMode, Output, Scale},
|
||||||
seat::Seat,
|
seat::Seat,
|
||||||
shell::xdg::XdgToplevelSurfaceRoleAttributes,
|
shell::{
|
||||||
|
xdg::XdgToplevelSurfaceRoleAttributes,
|
||||||
|
wlr_layer::{Layer, KeyboardInteractivity, LayerSurfaceCachedState},
|
||||||
|
},
|
||||||
Serial, SERIAL_COUNTER,
|
Serial, SERIAL_COUNTER,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -103,15 +106,10 @@ impl Shell {
|
||||||
.borrow();
|
.borrow();
|
||||||
workspace
|
workspace
|
||||||
.space
|
.space
|
||||||
.map_output(output, config.scale, config.position);
|
.map_output(output, config.position);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for output in self.outputs.iter() {
|
for output in self.outputs.iter() {
|
||||||
let config = output
|
|
||||||
.user_data()
|
|
||||||
.get::<RefCell<OutputConfig>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow();
|
|
||||||
let active = output
|
let active = output
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ActiveWorkspace>()
|
.get::<ActiveWorkspace>()
|
||||||
|
|
@ -119,7 +117,7 @@ impl Shell {
|
||||||
.get()
|
.get()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let workspace = &mut self.spaces[active];
|
let workspace = &mut self.spaces[active];
|
||||||
workspace.space.map_output(output, config.scale, (0, 0));
|
workspace.space.map_output(output, (0, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -156,16 +154,16 @@ impl Shell {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::OutputBound => {
|
Mode::OutputBound => {
|
||||||
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
|
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
|
||||||
workspace.space.map_output(output, config.scale, (0, 0));
|
workspace.space.map_output(output, (0, 0));
|
||||||
}
|
}
|
||||||
Mode::Global { active } => {
|
Mode::Global { active } => {
|
||||||
let workspace = &mut self.spaces[active];
|
let workspace = &mut self.spaces[active];
|
||||||
workspace
|
workspace
|
||||||
.space
|
.space
|
||||||
.map_output(output, config.scale, config.position);
|
.map_output(output, config.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output.change_current_state(None, None, Some(config.scale.ceil() as i32), None);
|
output.change_current_state(None, None, Some(Scale::Fractional(config.scale)), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_output(&mut self, output: &Output) {
|
pub fn remove_output(&mut self, output: &Output) {
|
||||||
|
|
@ -271,14 +269,9 @@ impl Shell {
|
||||||
if let Some(old_idx) = active.set(idx) {
|
if let Some(old_idx) = active.set(idx) {
|
||||||
self.spaces[old_idx].space.unmap_output(output);
|
self.spaces[old_idx].space.unmap_output(output);
|
||||||
}
|
}
|
||||||
let config = output
|
|
||||||
.user_data()
|
|
||||||
.get::<RefCell<OutputConfig>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow();
|
|
||||||
self.spaces[idx]
|
self.spaces[idx]
|
||||||
.space
|
.space
|
||||||
.map_output(output, config.scale, (0, 0));
|
.map_output(output, (0, 0));
|
||||||
self.spaces[idx].refresh();
|
self.spaces[idx].refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -294,7 +287,7 @@ impl Shell {
|
||||||
.borrow();
|
.borrow();
|
||||||
self.spaces[*active]
|
self.spaces[*active]
|
||||||
.space
|
.space
|
||||||
.map_output(output, config.scale, config.position);
|
.map_output(output, config.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -353,7 +346,7 @@ impl Shell {
|
||||||
self.spaces[old_active].space.unmap_output(output);
|
self.spaces[old_active].space.unmap_output(output);
|
||||||
self.spaces[active]
|
self.spaces[active]
|
||||||
.space
|
.space
|
||||||
.map_output(output, config.scale, config.position);
|
.map_output(output, config.position);
|
||||||
self.spaces[active].refresh();
|
self.spaces[active].refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -367,11 +360,6 @@ impl Shell {
|
||||||
let mut active = Some(active.clone());
|
let mut active = Some(active.clone());
|
||||||
self.mode = new;
|
self.mode = new;
|
||||||
for output in &self.outputs {
|
for output in &self.outputs {
|
||||||
let config = output
|
|
||||||
.user_data()
|
|
||||||
.get::<RefCell<OutputConfig>>()
|
|
||||||
.unwrap()
|
|
||||||
.borrow();
|
|
||||||
let workspace = if let Some(a) = active.take() {
|
let workspace = if let Some(a) = active.take() {
|
||||||
output
|
output
|
||||||
.user_data()
|
.user_data()
|
||||||
|
|
@ -381,7 +369,7 @@ impl Shell {
|
||||||
} else {
|
} else {
|
||||||
Self::assign_next_free_output(&mut self.spaces, output)
|
Self::assign_next_free_output(&mut self.spaces, output)
|
||||||
};
|
};
|
||||||
workspace.space.map_output(output, config.scale, (0, 0));
|
workspace.space.map_output(output, (0, 0));
|
||||||
workspace.refresh();
|
workspace.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -440,6 +428,7 @@ impl Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
|
self.popups.cleanup();
|
||||||
for output in self.outputs.iter() {
|
for output in self.outputs.iter() {
|
||||||
let workspace = match &self.mode {
|
let workspace = match &self.mode {
|
||||||
Mode::OutputBound => {
|
Mode::OutputBound => {
|
||||||
|
|
@ -454,6 +443,9 @@ impl Shell {
|
||||||
Mode::Global { active } => &mut self.spaces[*active],
|
Mode::Global { active } => &mut self.spaces[*active],
|
||||||
};
|
};
|
||||||
workspace.refresh();
|
workspace.refresh();
|
||||||
|
let mut map = layer_map_for_output(output);
|
||||||
|
map.cleanup();
|
||||||
|
map.arrange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -482,7 +474,33 @@ impl Shell {
|
||||||
}
|
}
|
||||||
workspace.pending_windows.retain(|(w, _)| w != &window);
|
workspace.pending_windows.retain(|(w, _)| w != &window);
|
||||||
}
|
}
|
||||||
workspace.space.commit(surface)
|
if let Some((layer, output, seat)) = workspace
|
||||||
|
.pending_layers
|
||||||
|
.iter()
|
||||||
|
.find(|(l, _, _)| l.get_surface() == Some(surface))
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
|
let focus = layer
|
||||||
|
.get_surface()
|
||||||
|
.map(|surface| {
|
||||||
|
with_states(surface, |states| {
|
||||||
|
let state = states.cached_state.current::<LayerSurfaceCachedState>();
|
||||||
|
matches!(state.layer, Layer::Top | Layer::Overlay)
|
||||||
|
&& dbg!(state.keyboard_interactivity) != KeyboardInteractivity::None
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let mut map = layer_map_for_output(&output);
|
||||||
|
map.map_layer(&layer).unwrap();
|
||||||
|
|
||||||
|
if focus {
|
||||||
|
new_focus = Some(seat);
|
||||||
|
}
|
||||||
|
workspace.pending_layers.retain(|(l, _, _)| l != &layer);
|
||||||
|
}
|
||||||
|
workspace.space.commit(surface);
|
||||||
}
|
}
|
||||||
if let Some(seat) = new_focus {
|
if let Some(seat) = new_focus {
|
||||||
self.set_focus(Some(surface), &seat, seats, None)
|
self.set_focus(Some(surface), &seat, seats, None)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::{layout, Layout};
|
use super::{layout, Layout};
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::{Space, Window},
|
desktop::{LayerSurface, Space, Window, Kind},
|
||||||
reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge,
|
reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::{self, ResizeEdge},
|
||||||
wayland::{
|
wayland::{
|
||||||
output::Output,
|
output::Output,
|
||||||
seat::{PointerGrabStartData, Seat},
|
seat::{PointerGrabStartData, Seat},
|
||||||
|
|
@ -52,6 +52,8 @@ pub struct Workspace {
|
||||||
pub space: Space,
|
pub space: Space,
|
||||||
pub(super) layout: Box<dyn Layout>,
|
pub(super) layout: Box<dyn Layout>,
|
||||||
pub(super) pending_windows: Vec<(Window, Seat)>,
|
pub(super) pending_windows: Vec<(Window, Seat)>,
|
||||||
|
pub(super) pending_layers: Vec<(LayerSurface, Output, Seat)>,
|
||||||
|
pub fullscreen: HashMap<String, Window>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
|
|
@ -61,6 +63,8 @@ impl Workspace {
|
||||||
space: Space::new(None),
|
space: Space::new(None),
|
||||||
layout: layout::new_default_layout(),
|
layout: layout::new_default_layout(),
|
||||||
pending_windows: Vec::new(),
|
pending_windows: Vec::new(),
|
||||||
|
pending_layers: Vec::new(),
|
||||||
|
fullscreen: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,6 +93,11 @@ impl Workspace {
|
||||||
self.pending_windows.push((window, seat.clone()));
|
self.pending_windows.push((window, seat.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pending_layer(&mut self, layer: LayerSurface, output: &Output, seat: &Seat) {
|
||||||
|
self.pending_layers
|
||||||
|
.push((layer, output.clone(), seat.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn map_window(&mut self, window: &Window, seat: &Seat) {
|
pub(super) fn map_window(&mut self, window: &Window, seat: &Seat) {
|
||||||
seat.user_data()
|
seat.user_data()
|
||||||
.insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new())));
|
.insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new())));
|
||||||
|
|
@ -108,6 +117,19 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
|
let outputs = self.space.outputs().collect::<Vec<_>>();
|
||||||
|
let dead_output_windows = self.fullscreen
|
||||||
|
.iter()
|
||||||
|
.filter(|(name, _)|
|
||||||
|
!outputs.iter().any(|o| o.name() == **name)
|
||||||
|
)
|
||||||
|
.map(|(_, w)| w)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
for window in dead_output_windows {
|
||||||
|
self.unfullscreen_request(&window);
|
||||||
|
}
|
||||||
|
self.fullscreen.retain(|_, w| w.toplevel().alive());
|
||||||
self.layout.refresh(&mut self.space);
|
self.layout.refresh(&mut self.space);
|
||||||
self.space.refresh();
|
self.space.refresh();
|
||||||
}
|
}
|
||||||
|
|
@ -141,6 +163,9 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maximize_request(&mut self, window: &Window, output: &Output) {
|
pub fn maximize_request(&mut self, window: &Window, output: &Output) {
|
||||||
|
if self.fullscreen.values().any(|w| w == window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.layout
|
self.layout
|
||||||
.maximize_request(&mut self.space, window, output)
|
.maximize_request(&mut self.space, window, output)
|
||||||
}
|
}
|
||||||
|
|
@ -152,6 +177,9 @@ impl Workspace {
|
||||||
serial: Serial,
|
serial: Serial,
|
||||||
start_data: PointerGrabStartData,
|
start_data: PointerGrabStartData,
|
||||||
) {
|
) {
|
||||||
|
if self.fullscreen.values().any(|w| w == window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.layout
|
self.layout
|
||||||
.move_request(&mut self.space, window, seat, serial, start_data)
|
.move_request(&mut self.space, window, seat, serial, start_data)
|
||||||
}
|
}
|
||||||
|
|
@ -164,7 +192,73 @@ impl Workspace {
|
||||||
start_data: PointerGrabStartData,
|
start_data: PointerGrabStartData,
|
||||||
edges: ResizeEdge,
|
edges: ResizeEdge,
|
||||||
) {
|
) {
|
||||||
|
if self.fullscreen.values().any(|w| w == window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.layout
|
self.layout
|
||||||
.resize_request(&mut self.space, window, seat, serial, start_data, edges)
|
.resize_request(&mut self.space, window, seat, serial, start_data, edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fullscreen_request(&mut self, window: &Window, output: &Output) {
|
||||||
|
if self.fullscreen.contains_key(&output.name()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(irrefutable_let_patterns)]
|
||||||
|
if let Kind::Xdg(xdg) = &window.toplevel() {
|
||||||
|
if xdg.get_surface().is_some() {
|
||||||
|
let ret = xdg.with_pending_state(|state| {
|
||||||
|
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||||
|
state.size = Some(
|
||||||
|
output
|
||||||
|
.current_mode()
|
||||||
|
.map(|m| m.size)
|
||||||
|
.unwrap_or((0, 0).into())
|
||||||
|
.to_f64()
|
||||||
|
.to_logical(output.current_scale().fractional_scale())
|
||||||
|
.to_i32_round(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ret.is_ok() {
|
||||||
|
xdg.send_configure();
|
||||||
|
self.fullscreen.insert(output.name(), window.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unfullscreen_request(&mut self, window: &Window) {
|
||||||
|
if self.fullscreen.values().any(|w| w == window) {
|
||||||
|
#[allow(irrefutable_let_patterns)]
|
||||||
|
if let Kind::Xdg(xdg) = &window.toplevel() {
|
||||||
|
if xdg.get_surface().is_some() {
|
||||||
|
let ret = xdg.with_pending_state(|state| {
|
||||||
|
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||||
|
state.size = None;
|
||||||
|
});
|
||||||
|
if ret.is_ok() {
|
||||||
|
self.layout.refresh(&mut self.space);
|
||||||
|
xdg.send_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.fullscreen.retain(|_, w| w != window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fullscreen_toggle(&mut self, window: &Window, output: &Output) {
|
||||||
|
if self.fullscreen.contains_key(&output.name()) {
|
||||||
|
self.unfullscreen_request(window)
|
||||||
|
} else {
|
||||||
|
self.fullscreen_request(window, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fullscreen(&self, output: &Output) -> Option<&Window> {
|
||||||
|
if !self.space.outputs().any(|o| o == output) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.fullscreen.get(&output.name()).filter(|w| w.toplevel().get_surface().is_some())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/state.rs
11
src/state.rs
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::LoopHandle,
|
calloop::{LoopHandle, LoopSignal},
|
||||||
wayland_server::{protocol::wl_surface::WlSurface, Display},
|
wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||||
},
|
},
|
||||||
wayland::{
|
wayland::{
|
||||||
|
|
@ -18,7 +18,7 @@ use smithay::{
|
||||||
self, init_wlr_output_configuration, ConfigurationManager, ModeConfiguration,
|
self, init_wlr_output_configuration, ConfigurationManager, ModeConfiguration,
|
||||||
},
|
},
|
||||||
xdg::init_xdg_output_manager,
|
xdg::init_xdg_output_manager,
|
||||||
Mode as OutputMode, Output,
|
Mode as OutputMode, Output, Scale,
|
||||||
},
|
},
|
||||||
seat::Seat,
|
seat::Seat,
|
||||||
shell::xdg::ToplevelSurface,
|
shell::xdg::ToplevelSurface,
|
||||||
|
|
@ -47,6 +47,7 @@ pub struct Common {
|
||||||
pub display: Rc<RefCell<Display>>,
|
pub display: Rc<RefCell<Display>>,
|
||||||
pub socket: OsString,
|
pub socket: OsString,
|
||||||
pub event_loop_handle: LoopHandle<'static, State>,
|
pub event_loop_handle: LoopHandle<'static, State>,
|
||||||
|
pub event_loop_signal: LoopSignal,
|
||||||
|
|
||||||
pub output_conf: ConfigurationManager,
|
pub output_conf: ConfigurationManager,
|
||||||
pub shell: Shell,
|
pub shell: Shell,
|
||||||
|
|
@ -145,10 +146,10 @@ impl BackendData {
|
||||||
let transform =
|
let transform =
|
||||||
Some(final_config.transform.into()).filter(|x| *x != output.current_transform());
|
Some(final_config.transform.into()).filter(|x| *x != output.current_transform());
|
||||||
let scale =
|
let scale =
|
||||||
Some(final_config.scale.ceil() as i32).filter(|x| *x != output.current_scale());
|
Some(final_config.scale).filter(|x| *x != output.current_scale().fractional_scale());
|
||||||
let location =
|
let location =
|
||||||
Some(final_config.position.into()).filter(|x| *x != output.current_location());
|
Some(final_config.position.into()).filter(|x| *x != output.current_location());
|
||||||
output.change_current_state(mode, transform, scale, location);
|
output.change_current_state(mode, transform, scale.map(Scale::Fractional), location);
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
@ -182,6 +183,7 @@ impl State {
|
||||||
mut display: Display,
|
mut display: Display,
|
||||||
socket: OsString,
|
socket: OsString,
|
||||||
handle: LoopHandle<'static, State>,
|
handle: LoopHandle<'static, State>,
|
||||||
|
signal: LoopSignal,
|
||||||
log: LogState,
|
log: LogState,
|
||||||
) -> State {
|
) -> State {
|
||||||
let config = Config::load();
|
let config = Config::load();
|
||||||
|
|
@ -326,6 +328,7 @@ impl State {
|
||||||
display: Rc::new(RefCell::new(display)),
|
display: Rc::new(RefCell::new(display)),
|
||||||
socket,
|
socket,
|
||||||
event_loop_handle: handle,
|
event_loop_handle: handle,
|
||||||
|
event_loop_signal: signal,
|
||||||
|
|
||||||
output_conf,
|
output_conf,
|
||||||
shell,
|
shell,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue