From 3ffdf58e0ff33338d512a4c5ce8250c54218cc72 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 20 Apr 2022 16:06:37 +0200 Subject: [PATCH 01/19] output_conf: Fixed scaling issues --- Cargo.lock | 2 +- src/backend/kms/mod.rs | 1 + src/backend/render/mod.rs | 2 +- src/shell/mod.rs | 35 ++++++++++------------------------- src/state.rs | 6 +++--- 5 files changed, 16 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d3340d0..2dc2f9c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1311,7 +1311,7 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/pop-os/smithay?branch=main#a9aea2275645cc4b2fc290103cc2072f9a2bfbb1" +source = "git+https://github.com/pop-os/smithay?branch=main#9a2273f4c8fed28d20952a5493627edf9d3cdcfc" dependencies = [ "appendlist", "bitflags", diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 161ffc22..1bf214a9 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -711,6 +711,7 @@ impl KmsState { false }; + shell.refresh_outputs(); if recreated { self.schedule_render(output); } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 9ae94ce8..22692964 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -125,7 +125,7 @@ where .space .output_geometry(output) .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); custom_elements.push(fps_overlay.into()); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 741393e8..99766988 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -11,7 +11,7 @@ pub use smithay::{ utils::{Logical, Point, Rectangle, Size}, wayland::{ compositor::with_states, - output::{Mode as OutputMode, Output}, + output::{Mode as OutputMode, Output, Scale}, seat::Seat, shell::xdg::XdgToplevelSurfaceRoleAttributes, Serial, SERIAL_COUNTER, @@ -103,15 +103,10 @@ impl Shell { .borrow(); workspace .space - .map_output(output, config.scale, config.position); + .map_output(output, config.position); } } else { for output in self.outputs.iter() { - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); let active = output .user_data() .get::() @@ -119,7 +114,7 @@ impl Shell { .get() .unwrap(); let workspace = &mut self.spaces[active]; - workspace.space.map_output(output, config.scale, (0, 0)); + workspace.space.map_output(output, (0, 0)); } } } @@ -156,16 +151,16 @@ impl Shell { match self.mode { Mode::OutputBound => { 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 } => { let workspace = &mut self.spaces[active]; workspace .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) { @@ -271,14 +266,9 @@ impl Shell { if let Some(old_idx) = active.set(idx) { self.spaces[old_idx].space.unmap_output(output); } - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); self.spaces[idx] .space - .map_output(output, config.scale, (0, 0)); + .map_output(output, (0, 0)); self.spaces[idx].refresh(); } } @@ -294,7 +284,7 @@ impl Shell { .borrow(); self.spaces[*active] .space - .map_output(output, config.scale, config.position); + .map_output(output, config.position); } } }; @@ -353,7 +343,7 @@ impl Shell { self.spaces[old_active].space.unmap_output(output); self.spaces[active] .space - .map_output(output, config.scale, config.position); + .map_output(output, config.position); self.spaces[active].refresh(); } @@ -367,11 +357,6 @@ impl Shell { let mut active = Some(active.clone()); self.mode = new; for output in &self.outputs { - let config = output - .user_data() - .get::>() - .unwrap() - .borrow(); let workspace = if let Some(a) = active.take() { output .user_data() @@ -381,7 +366,7 @@ impl Shell { } else { 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(); } } diff --git a/src/state.rs b/src/state.rs index feb4c7a8..650a1861 100644 --- a/src/state.rs +++ b/src/state.rs @@ -18,7 +18,7 @@ use smithay::{ self, init_wlr_output_configuration, ConfigurationManager, ModeConfiguration, }, xdg::init_xdg_output_manager, - Mode as OutputMode, Output, + Mode as OutputMode, Output, Scale, }, seat::Seat, shell::xdg::ToplevelSurface, @@ -145,10 +145,10 @@ impl BackendData { let transform = Some(final_config.transform.into()).filter(|x| *x != output.current_transform()); 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 = 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 From e9393f9ec0a7c6eed7083689dd5024841700619e Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 21 Apr 2022 12:42:02 +0200 Subject: [PATCH 02/19] render: Set texture filter to linear --- src/backend/render/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 22692964..667d76d6 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -14,7 +14,7 @@ use smithay::{ renderer::{ gles2::{Gles2Renderbuffer, Gles2Renderer, Gles2Texture}, multigpu::{egl::EglGlesBackend, Error as MultiError, MultiFrame, MultiRenderer}, - ImportAll, Renderer, + ImportAll, Renderer, TextureFilter, }, }, desktop::space::{RenderElement, RenderError, SpaceOutputTuple, SurfaceTree}, @@ -110,6 +110,8 @@ where ::TextureId: Clone + 'static, CustomElem: RenderElement, { + renderer.downscale_filter(TextureFilter::Linear).map_err(RenderError::Rendering)?; + #[cfg(feature = "debug")] { fps.start(); From 3fe082d648b9b7f1e50ed32b0c1cb09f0f96d16b Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 21 Apr 2022 12:43:31 +0200 Subject: [PATCH 03/19] kms: Disable frame pacing for now --- src/backend/kms/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 1bf214a9..169d1440 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -730,6 +730,7 @@ impl KmsState { } if !surface.pending { surface.pending = true; + /* let duration = surface .last_submit .as_ref() @@ -740,12 +741,13 @@ impl KmsState { 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); + */ let data = (*device, *crtc); - if surface.vrr { + //if surface.vrr { surface.render_timer.add_timeout(Duration::ZERO, data); - } else { - surface.render_timer.add_timeout(duration, data); - } + //} else { + // surface.render_timer.add_timeout(duration, data); + //} } } } From ff844631eaaa2a0a2aa9ca137502a61015700690 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 21 Apr 2022 14:02:44 +0200 Subject: [PATCH 04/19] layer_shell: Fix initial keyboard focus --- src/shell/handler.rs | 25 ++----------------------- src/shell/mod.rs | 37 +++++++++++++++++++++++++++++++++---- src/shell/workspace.rs | 8 +++++++- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/shell/handler.rs b/src/shell/handler.rs index 620257cd..8fd17a6d 100644 --- a/src/shell/handler.rs +++ b/src/shell/handler.rs @@ -17,8 +17,7 @@ use smithay::{ seat::{PointerGrabStartData, Seat}, shell::{ wlr_layer::{ - wlr_layer_shell_init, KeyboardInteractivity, Layer, LayerShellRequest, - LayerSurfaceAttributes, LayerSurfaceCachedState, + wlr_layer_shell_init, LayerShellRequest, LayerSurfaceAttributes, }, xdg::{ xdg_shell_init, Configure, XdgPopupSurfaceRoleAttributes, XdgRequest, @@ -26,7 +25,6 @@ use smithay::{ }, }, Serial, - SERIAL_COUNTER, }, }; use std::{cell::Cell, sync::Mutex}; @@ -236,26 +234,7 @@ pub fn init_shell(config: &Config, display: &mut Display) -> super::Shell { .as_ref() .and_then(Output::from_resource) .unwrap_or_else(|| active_output(&seat, &*state)); - - let focus = surface - .get_surface() - .map(|surface| { - with_states(surface, |states| { - let state = states.cached_state.current::(); - 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())); - } + state.shell.active_space_mut(&output).pending_layer(LayerSurface::new(surface, namespace), &output, &seat); } _ => {} }, diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 99766988..dbc507b5 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -6,14 +6,17 @@ use crate::{ state::Common, }; 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, utils::{Logical, Point, Rectangle, Size}, wayland::{ compositor::with_states, output::{Mode as OutputMode, Output, Scale}, seat::Seat, - shell::xdg::XdgToplevelSurfaceRoleAttributes, + shell::{ + xdg::XdgToplevelSurfaceRoleAttributes, + wlr_layer::{Layer, KeyboardInteractivity, LayerSurfaceCachedState}, + }, Serial, SERIAL_COUNTER, }, }; @@ -465,9 +468,35 @@ impl Shell { } { new_focus = Some(seat); } - 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::(); + 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 { self.set_focus(Some(surface), &seat, seats, None) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 720378b8..deb32817 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,7 +1,7 @@ use super::{layout, Layout}; use indexmap::IndexSet; use smithay::{ - desktop::{Space, Window}, + desktop::{LayerSurface, Space, Window}, reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge, wayland::{ output::Output, @@ -52,6 +52,7 @@ pub struct Workspace { pub space: Space, pub(super) layout: Box, pub(super) pending_windows: Vec<(Window, Seat)>, + pub(super) pending_layers: Vec<(LayerSurface, Output, Seat)>, } impl Workspace { @@ -61,6 +62,7 @@ impl Workspace { space: Space::new(None), layout: layout::new_default_layout(), pending_windows: Vec::new(), + pending_layers: Vec::new(), } } @@ -88,6 +90,10 @@ impl Workspace { pub fn pending_window(&mut self, window: Window, seat: &Seat) { 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) { seat.user_data() From 0e42f3406573c788bc4ff2abee11dabe1bbff13d Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 22 Apr 2022 09:46:29 +0200 Subject: [PATCH 05/19] layer_shell: Fixed multi-monitor pointer input bug --- src/input/mod.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index f0403ee3..c62ecf53 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -5,7 +5,7 @@ use smithay::{ backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState}, desktop::{layer_map_for_output, Kind, Space, WindowSurfaceType}, reexports::wayland_server::{protocol::wl_surface::WlSurface, Display}, - utils::{Logical, Point}, + utils::{Logical, Point, Rectangle}, wayland::{ data_device::set_data_device_focus, output::Output, @@ -368,6 +368,7 @@ impl Common { position, relative_pos, &output, + output_geometry, &workspace.space, ); handle_window_movement( @@ -411,6 +412,7 @@ impl Common { position, relative_pos, &output, + geometry, &workspace.space, ); handle_window_movement( @@ -495,7 +497,8 @@ impl Common { layers.layer_geometry(layer).unwrap().loc; under = layer .surface_under( - pos - output_geo.loc.to_f64() - layer_loc.to_f64(), + pos - output_geo.loc.to_f64() + - layer_loc.to_f64(), WindowSurfaceType::ALL, ) .map(|(s, _)| s); @@ -521,7 +524,8 @@ impl Common { layers.layer_geometry(layer).unwrap().loc; under = layer .surface_under( - pos - output_geo.loc.to_f64() - layer_loc.to_f64(), + pos - output_geo.loc.to_f64() + - layer_loc.to_f64(), WindowSurfaceType::ALL, ) .map(|(s, _)| s); @@ -640,10 +644,10 @@ impl Common { global_pos: Point, relative_pos: Point, output: &Output, + output_geo: Rectangle, space: &Space, ) -> Option<(WlSurface, Point)> { let layers = layer_map_for_output(output); - let output_geo = space.output_geometry(output).unwrap(); if let Some(layer) = layers .layer_under(WlrLayer::Overlay, relative_pos) @@ -655,12 +659,7 @@ impl Common { global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(), 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) { let window_loc = space.window_location(window).unwrap(); window @@ -681,12 +680,7 @@ impl Common { global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(), WindowSurfaceType::ALL, ) - .map(|(s, loc)| { - ( - s, - loc + layer_loc + output_geo.loc, - ) - }) + .map(|(s, loc)| (s, loc + layer_loc + output_geo.loc)) } else { None } From f1f51e171431e020fa91cfff59198b3bdf28c84d Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 22 Apr 2022 15:18:28 +0200 Subject: [PATCH 06/19] shell: Handle fullscreen surfaces --- config.ron | 2 + src/backend/render/mod.rs | 171 +++++++++++++++++++++++++-- src/config.rs | 1 + src/input/mod.rs | 207 +++++++++++++++++++++------------ src/shell/handler.rs | 25 ++++ src/shell/layout/tiling/mod.rs | 3 + src/shell/workspace.rs | 93 ++++++++++++++- 7 files changed, 411 insertions(+), 91 deletions(-) diff --git a/config.ron b/config.ron index f645f455..a0ef7208 100644 --- a/config.ron +++ b/config.ron @@ -34,12 +34,14 @@ //TODO: automatic orientation with Logo+o toggling (modifiers: [Logo], key: "v"): Orientation(Vertical), (modifiers: [Logo], key: "o"): Orientation(Horizontal), + (modifiers: [Logo, Shift], key: "f"): Fullscreen, //TODO: ability to select default web browser (modifiers: [Logo], key: "b"): Spawn("firefox"), //TODO: ability to select default file browser (modifiers: [Logo], key: "f"): Spawn("nautilus"), //TODO: ability to select default terminal (modifiers: [Logo], key: "t"): Spawn("gnome-terminal"), + (modifiers: [Ctrl], key: "t"): Spawn("wofi --show drun"), }, workspace_mode: OutputBound, ) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 667d76d6..c87fe816 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -14,12 +14,18 @@ use smithay::{ renderer::{ gles2::{Gles2Renderbuffer, Gles2Renderer, Gles2Texture}, multigpu::{egl::EglGlesBackend, Error as MultiError, MultiFrame, MultiRenderer}, - ImportAll, Renderer, TextureFilter, + ImportAll, Renderer, Frame, TextureFilter, }, }, - desktop::space::{RenderElement, RenderError, SpaceOutputTuple, SurfaceTree}, - utils::{Logical, Point, Rectangle}, - wayland::output::Output, + desktop::{ + space::{RenderElement, RenderError, SpaceOutputTuple, SurfaceTree}, + 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; @@ -29,6 +35,8 @@ pub type GlMultiRenderer<'a> = MultiRenderer<'a, 'a, EglGlesBackend, EglGlesBackend, Gles2Renderbuffer>; pub type GlMultiFrame = MultiFrame; +static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; + smithay::custom_elements! { pub CustomElem<=Gles2Renderer>; SurfaceTree=SurfaceTree, @@ -97,7 +105,7 @@ impl AsGles2Renderer for GlMultiRenderer<'_> { } pub fn render_output( - _gpu: Option<&DrmNode>, + gpu: Option<&DrmNode>, renderer: &mut R, age: u8, state: &mut Common, @@ -117,7 +125,50 @@ where fps.start(); } - #[allow(unused_mut)] + 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( + _gpu: Option<&DrmNode>, + renderer: &mut R, + age: u8, + state: &mut Common, + output: &Output, + hardware_cursor: bool, + #[cfg(feature = "debug")] fps: &mut Fps, +) -> Result>>, RenderError> +where + R: Renderer + ImportAll + AsGles2Renderer, + ::TextureId: Clone + 'static, + CustomElem: RenderElement, +{ let mut custom_elements = Vec::::new(); #[cfg(feature = "debug")] @@ -162,18 +213,116 @@ where } } - let res = state.shell.active_space_mut(output).space.render_output( + state.shell.active_space_mut(output).space.render_output( renderer, &output, age as usize, - [0.153, 0.161, 0.165, 1.0], + CLEAR_COLOR, &*custom_elements, - ); + ) +} + +fn render_fullscreen( + _gpu: Option<&DrmNode>, + renderer: &mut R, + window: Window, + state: &mut Common, + output: &Output, + hardware_cursor: bool, + #[cfg(feature = "debug")] fps: &mut Fps, +) -> Result>>, RenderError> +where + R: Renderer + ImportAll + AsGles2Renderer, + ::TextureId: Clone + 'static, + CustomElem: RenderElement, +{ + 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::::new(); #[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()); + } + + 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) + } } - res + 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 location = geo.loc - output_geo.loc; + let elem_damage = elem.accumulated_damage(None); + elem.draw( + renderer, + frame, + scale, + location, + &[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::::Rendering) } diff --git a/src/config.rs b/src/config.rs index 63862f8a..e9a07476 100644 --- a/src/config.rs +++ b/src/config.rs @@ -471,5 +471,6 @@ pub enum Action { MoveToWorkspace(u8), Focus(FocusDirection), Orientation(crate::shell::layout::Orientation), + Fullscreen, Spawn(String), } diff --git a/src/input/mod.rs b/src/input/mod.rs index c62ecf53..37956bfc 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{config::Action, state::Common}; +use crate::{config::Action, state::Common, shell::Workspace}; use smithay::{ backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState}, desktop::{layer_map_for_output, Kind, Space, WindowSurfaceType}, @@ -304,6 +304,14 @@ impl Common { self.seats.iter(), ); } + Action::Fullscreen => { + let current_output = active_output(seat, &self); + let workspace = self.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) => { let output = active_output(seat, &self); self.shell.set_orientation(&seat, &output, *orientation); @@ -369,7 +377,7 @@ impl Common { relative_pos, &output, output_geometry, - &workspace.space, + &workspace, ); handle_window_movement( under.as_ref().map(|(s, _)| s), @@ -413,7 +421,7 @@ impl Common { relative_pos, &output, geometry, - &workspace.space, + &workspace, ); handle_window_movement( under.as_ref().map(|(s, _)| s), @@ -488,49 +496,73 @@ impl Common { let layers = layer_map_for_output(&output); let mut under = None; - 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(window) = - workspace.space.window_under(relative_pos).cloned() - { - let window_loc = - workspace.space.window_location(&window).unwrap(); - under = window - .surface_under( - relative_pos - window_loc.to_f64(), + if let Some(window) = workspace.get_fullscreen(&output) { + if let Some(layer) = layers + .layer_under(WlrLayer::Overlay, 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 { + under = window.surface_under( + pos - output_geo.loc.to_f64(), WindowSurfaceType::TOPLEVEL - | WindowSurfaceType::SUBSURFACE, - ) - .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 + | WindowSurfaceType::SUBSURFACE + ).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(window) = + workspace.space.window_under(relative_pos).cloned() + { + let window_loc = + workspace.space.window_location(&window).unwrap(); + under = window .surface_under( - pos - output_geo.loc.to_f64() - - layer_loc.to_f64(), - WindowSurfaceType::ALL, + relative_pos - window_loc.to_f64(), + WindowSurfaceType::TOPLEVEL + | WindowSurfaceType::SUBSURFACE, ) .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); + } + }; + } self.set_focus(under.as_ref(), seat, Some(serial)); } @@ -645,44 +677,67 @@ impl Common { relative_pos: Point, output: &Output, output_geo: Rectangle, - space: &Space, + workspace: &Workspace, ) -> Option<(WlSurface, Point)> { let layers = layer_map_for_output(output); - - 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(window) = space.window_under(relative_pos) { - let window_loc = space.window_location(window).unwrap(); - window - .surface_under(relative_pos - window_loc.to_f64(), WindowSurfaceType::ALL) - .map(|(s, loc)| { - ( - s, - loc + window_loc - (relative_pos - global_pos).to_i32_round(), + if let Some(window) = workspace.get_fullscreen(output) { + 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, ) - }) - } 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)) + .map(|(s, loc)| (s, loc + layer_loc + output_geo.loc)) + } else { + window + .surface_under(global_pos - output_geo.loc.to_f64(), WindowSurfaceType::ALL) + .map(|(s, loc)| { + ( + s, + loc + output_geo.loc, + ) + }) + } } 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(window) = workspace.space.window_under(relative_pos) { + let window_loc = workspace.space.window_location(window).unwrap(); + window + .surface_under(relative_pos - window_loc.to_f64(), WindowSurfaceType::ALL) + .map(|(s, loc)| { + ( + s, + 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 { + None + } } } } diff --git a/src/shell/handler.rs b/src/shell/handler.rs index 8fd17a6d..33007c9d 100644 --- a/src/shell/handler.rs +++ b/src/shell/handler.rs @@ -213,6 +213,31 @@ pub fn init_shell(config: &Config, display: &mut Display) -> super::Shell { .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*/ } } }, diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index a93e028f..1385b25c 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -529,6 +529,9 @@ impl TilingLayout { if let Some(geo) = geo { #[allow(irrefutable_let_patterns)] if let Kind::Xdg(xdg) = &window.toplevel() { + if xdg.current_state().map(|state| state.states.contains(XdgState::Fullscreen)).unwrap_or(false) { + continue; + } let ret = xdg.with_pending_state(|state| { state.size = Some( (geo.size.w - inner * 2, geo.size.h - inner * 2) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index deb32817..c698dcbe 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,8 +1,8 @@ use super::{layout, Layout}; use indexmap::IndexSet; use smithay::{ - desktop::{LayerSurface, Space, Window}, - reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge, + desktop::{LayerSurface, Space, Window, Kind}, + reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::{self, ResizeEdge}, wayland::{ output::Output, seat::{PointerGrabStartData, Seat}, @@ -53,6 +53,7 @@ pub struct Workspace { pub(super) layout: Box, pub(super) pending_windows: Vec<(Window, Seat)>, pub(super) pending_layers: Vec<(LayerSurface, Output, Seat)>, + pub fullscreen: HashMap, } impl Workspace { @@ -63,6 +64,7 @@ impl Workspace { layout: layout::new_default_layout(), pending_windows: Vec::new(), pending_layers: Vec::new(), + fullscreen: HashMap::new(), } } @@ -90,9 +92,10 @@ impl Workspace { pub fn pending_window(&mut self, window: Window, seat: &Seat) { 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())); + self.pending_layers + .push((layer, output.clone(), seat.clone())); } pub(super) fn map_window(&mut self, window: &Window, seat: &Seat) { @@ -114,6 +117,18 @@ impl Workspace { } pub fn refresh(&mut self) { + let outputs = self.space.outputs().collect::>(); + let dead_output_windows = self.fullscreen + .iter() + .filter(|(name, _)| + !outputs.iter().any(|o| o.name() == **name) + ) + .map(|(_, w)| w) + .cloned() + .collect::>(); + for window in dead_output_windows { + self.unfullscreen_request(&window); + } self.layout.refresh(&mut self.space); self.space.refresh(); } @@ -147,6 +162,9 @@ impl Workspace { } pub fn maximize_request(&mut self, window: &Window, output: &Output) { + if self.fullscreen.values().any(|w| w == window) { + return; + } self.layout .maximize_request(&mut self.space, window, output) } @@ -158,6 +176,9 @@ impl Workspace { serial: Serial, start_data: PointerGrabStartData, ) { + if self.fullscreen.values().any(|w| w == window) { + return; + } self.layout .move_request(&mut self.space, window, seat, serial, start_data) } @@ -170,7 +191,71 @@ impl Workspace { start_data: PointerGrabStartData, edges: ResizeEdge, ) { + if self.fullscreen.values().any(|w| w == window) { + return; + } self.layout .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_logical(output.current_scale().integer_scale()), + ); + }); + + 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()) + } } From b5ef2272c02f88c3d64a86f26e529bd278fc0112 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 25 Apr 2022 12:35:55 +0200 Subject: [PATCH 07/19] kms: Enable VT switching --- src/backend/kms/mod.rs | 9 ++- src/backend/winit.rs | 2 +- src/backend/x11.rs | 2 +- src/input/mod.rs | 156 ++++++++++++++++++++++------------------- 4 files changed, 94 insertions(+), 75 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 169d1440..e8f2d478 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -102,7 +102,7 @@ pub fn init_backend(event_loop: &mut EventLoop, state: &mut State) -> Res let libinput_event_source = event_loop .handle() .insert_source(libinput_backend, move |event, _, state| { - state.common.process_input_event(event); + state.process_input_event(event); for output in state.common.shell.outputs() { state.backend.kms().schedule_render(output); } @@ -624,6 +624,13 @@ impl Surface { } 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( &mut self, output: &Output, diff --git a/src/backend/winit.rs b/src/backend/winit.rs index ab472437..175221a2 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -276,7 +276,7 @@ impl State { 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), _ => {} }; } diff --git a/src/backend/x11.rs b/src/backend/x11.rs index d7133c9d..0b81eb48 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -410,7 +410,7 @@ impl State { _ => {} }; - self.common.process_input_event(event); + self.process_input_event(event); // TODO actually figure out the output for output in self.common.shell.outputs() { self.backend.schedule_render(output); diff --git a/src/input/mod.rs b/src/input/mod.rs index 37956bfc..42c6058d 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{config::Action, state::Common, shell::Workspace}; +use crate::{config::Action, state::{Common, State}, shell::Workspace}; use smithay::{ backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState}, desktop::{layer_map_for_output, Kind, Space, WindowSurfaceType}, @@ -9,11 +9,12 @@ use smithay::{ wayland::{ data_device::set_data_device_focus, output::Output, - seat::{CursorImageStatus, FilterResult, KeysymHandle, Seat, XkbConfig}, + seat::{CursorImageStatus, FilterResult, KeysymHandle, Seat, XkbConfig, keysyms}, shell::wlr_layer::Layer as WlrLayer, SERIAL_COUNTER, }, }; +use xkbcommon::xkb::KEY_XF86Switch_VT_12; use std::{cell::RefCell, collections::HashMap}; pub struct ActiveOutput(pub RefCell); @@ -114,13 +115,13 @@ pub fn set_active_output(seat: &Seat, output: &Output) { } } -impl Common { +impl State { pub fn process_input_event(&mut self, event: InputEvent) { use smithay::backend::input::Event; match event { InputEvent::DeviceAdded { device } => { - let seat = &mut self.last_active_seat; + let seat = &mut self.common.last_active_seat; let userdata = seat.user_data(); let devices = userdata.get::().unwrap(); for cap in devices.add_device(&device) { @@ -136,6 +137,7 @@ impl Common { } DeviceCapability::Pointer => { let output = self + .common .shell .outputs() .next() @@ -157,12 +159,12 @@ impl Common { } #[cfg(feature = "debug")] { - self.egui.debug_state.handle_device_added(&device); - self.egui.log_state.handle_device_added(&device); + self.common.egui.debug_state.handle_device_added(&device); + self.common.egui.log_state.handle_device_added(&device); } } InputEvent::DeviceRemoved { device } => { - for seat in &mut self.seats { + for seat in &mut self.common.seats { let userdata = seat.user_data(); let devices = userdata.get::().unwrap(); if devices.has_device(&device) { @@ -182,15 +184,15 @@ impl Common { } #[cfg(feature = "debug")] { - self.egui.debug_state.handle_device_added(&device); - self.egui.log_state.handle_device_added(&device); + self.common.egui.debug_state.handle_device_added(&device); + self.common.egui.log_state.handle_device_added(&device); } } InputEvent::Keyboard { event, .. } => { use smithay::backend::input::KeyboardKeyEvent; 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 devices = userdata.get::().unwrap(); if devices.has_device(&device) { @@ -212,11 +214,11 @@ impl Common { #[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_keyboard() { - self.egui.debug_state.handle_keyboard( + if self.common.egui.debug_state.wants_keyboard() { + self.common.egui.debug_state.handle_keyboard( &handle, state == KeyState::Pressed, modifiers.clone(), @@ -224,8 +226,8 @@ impl Common { userdata.get::().unwrap().add(&handle); return FilterResult::Intercept(None); } - if self.egui.log_state.wants_keyboard() { - self.egui.log_state.handle_keyboard( + if self.common.egui.log_state.wants_keyboard() { + self.common.egui.log_state.handle_keyboard( &handle, state == KeyState::Pressed, 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::().unwrap().add(&handle); + return FilterResult::Intercept(None); + } + // 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 && binding.modifiers == *modifiers @@ -254,19 +264,19 @@ impl Common { { match action { Action::Terminate => { - self.should_stop = true; + self.common.should_stop = true; } #[cfg(feature = "debug")] Action::Debug => { - self.egui.active = !self.egui.active; + self.common.egui.active = !self.common.egui.active; } #[cfg(not(feature = "debug"))] Action::Debug => { slog_scope::info!("Debug overlay not included in this version") } Action::Close => { - let current_output = active_output(seat, &self); - let workspace = self.shell.active_space_mut(¤t_output); + let current_output = active_output(seat, &self.common); + let workspace = self.common.shell.active_space_mut(¤t_output); if let Some(window) = workspace.focus_stack(seat).last() { #[allow(irrefutable_let_patterns)] if let Kind::Xdg(xdg) = &window.toplevel() { @@ -275,52 +285,52 @@ impl Common { } } Action::Workspace(key_num) => { - let current_output = active_output(seat, &self); + let current_output = active_output(seat, &self.common); let workspace = match key_num { 0 => 9, x => x - 1, }; - self.shell + self.common.shell .activate(seat, ¤t_output, workspace as usize); } Action::MoveToWorkspace(key_num) => { - let current_output = active_output(seat, &self); + let current_output = active_output(seat, &self.common); let workspace = match key_num { 0 => 9, x => x - 1, }; - self.shell.move_current_window( + self.common.shell.move_current_window( seat, ¤t_output, workspace as usize, ); } Action::Focus(focus) => { - let current_output = active_output(seat, &self); - self.shell.move_focus( + let current_output = active_output(seat, &self.common); + self.common.shell.move_focus( seat, ¤t_output, *focus, - self.seats.iter(), + self.common.seats.iter(), ); } Action::Fullscreen => { - let current_output = active_output(seat, &self); - let workspace = self.shell.active_space_mut(¤t_output); + 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) => { - let output = active_output(seat, &self); - self.shell.set_orientation(&seat, &output, *orientation); + let output = active_output(seat, &self.common); + self.common.shell.set_orientation(&seat, &output, *orientation); } Action::Spawn(command) => { if let Err(err) = std::process::Command::new("/bin/sh") .arg("-c") .arg(command) - .env("WAYLAND_DISPLAY", &self.socket) + .env("WAYLAND_DISPLAY", &self.common.socket) .spawn() { slog_scope::warn!("Failed to spawn: {}", err); @@ -336,20 +346,22 @@ impl Common { use smithay::backend::input::PointerMotionEvent; 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 devices = userdata.get::().unwrap(); 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(); position += event.delta(); let output = self + .common .shell .outputs() .find(|output| { - self.shell + self.common + .shell .output_geometry(output) .to_f64() .contains(position) @@ -359,7 +371,7 @@ impl Common { if output != current_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 .max(position.x) @@ -370,9 +382,9 @@ impl Common { let serial = SERIAL_COUNTER.next_serial(); let relative_pos = - self.shell.space_relative_output_geometry(position, &output); - let workspace = self.shell.active_space_mut(&output); - let under = Common::surface_under( + self.common.shell.space_relative_output_geometry(position, &output); + let workspace = self.common.shell.active_space_mut(&output); + let under = State::surface_under( position, relative_pos, &output, @@ -388,11 +400,11 @@ impl Common { .motion(position, under, serial, event.time()); #[cfg(feature = "debug")] - if self.seats.iter().position(|x| x == seat).unwrap() == 0 { - self.egui + if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 { + self.common.egui .debug_state .handle_pointer_motion(position.to_i32_round()); - self.egui + self.common.egui .log_state .handle_pointer_motion(position.to_i32_round()); } @@ -404,19 +416,19 @@ impl Common { use smithay::backend::input::PointerMotionAbsoluteEvent; 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 devices = userdata.get::().unwrap(); if devices.has_device(&device) { - let output = active_output(seat, &self); - let geometry = self.shell.output_geometry(&output); + let output = active_output(seat, &self.common); + let geometry = self.common.shell.output_geometry(&output); let position = geometry.loc.to_f64() + event.position_transformed(geometry.size); let relative_pos = - self.shell.space_relative_output_geometry(position, &output); - let workspace = self.shell.active_space_mut(&output); + self.common.shell.space_relative_output_geometry(position, &output); + let workspace = self.common.shell.active_space_mut(&output); let serial = SERIAL_COUNTER.next_serial(); - let under = Common::surface_under( + let under = State::surface_under( position, relative_pos, &output, @@ -432,11 +444,11 @@ impl Common { .motion(position, under, serial, event.time()); #[cfg(feature = "debug")] - if self.seats.iter().position(|x| x == seat).unwrap() == 0 { - self.egui + if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 { + self.common.egui .debug_state .handle_pointer_motion(position.to_i32_round()); - self.egui + self.common.egui .log_state .handle_pointer_motion(position.to_i32_round()); } @@ -451,30 +463,30 @@ impl Common { }; 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 devices = userdata.get::().unwrap(); if devices.has_device(&device) { #[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() { if let Some(button) = event.button() { - self.egui.debug_state.handle_pointer_button( + self.common.egui.debug_state.handle_pointer_button( button, event.state() == ButtonState::Pressed, - self.egui.modifiers.clone(), + self.common.egui.modifiers.clone(), ); } break; } - if self.egui.log_state.wants_pointer() { + if self.common.egui.log_state.wants_pointer() { if let Some(button) = event.button() { - self.egui.log_state.handle_pointer_button( + self.common.egui.log_state.handle_pointer_button( button, event.state() == ButtonState::Pressed, - self.egui.modifiers.clone(), + self.common.egui.modifiers.clone(), ); } break; @@ -487,12 +499,12 @@ impl Common { ButtonState::Pressed => { // change the keyboard focus unless the pointer 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 output_geo = self.shell.output_geometry(&output); + let output_geo = self.common.shell.output_geometry(&output); let relative_pos = - self.shell.space_relative_output_geometry(pos, &output); - let workspace = self.shell.active_space_mut(&output); + self.common.shell.space_relative_output_geometry(pos, &output); + let workspace = self.common.shell.active_space_mut(&output); let layers = layer_map_for_output(&output); let mut under = None; @@ -564,7 +576,7 @@ impl Common { }; } - self.set_focus(under.as_ref(), seat, Some(serial)); + self.common.set_focus(under.as_ref(), seat, Some(serial)); } wl_pointer::ButtonState::Pressed } @@ -585,11 +597,11 @@ impl Common { }; let device = event.device(); - for seat in self.seats.clone().iter() { + for seat in self.common.seats.clone().iter() { #[cfg(feature = "debug")] - if self.seats.iter().position(|x| x == seat).unwrap() == 0 && self.egui.active { - if self.egui.debug_state.wants_pointer() { - self.egui.debug_state.handle_pointer_axis( + if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 && self.common.egui.active { + if self.common.egui.debug_state.wants_pointer() { + self.common.egui.debug_state.handle_pointer_axis( event .amount_discrete(Axis::Horizontal) .or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0)) @@ -601,8 +613,8 @@ impl Common { ); break; } - if self.egui.log_state.wants_pointer() { - self.egui.log_state.handle_pointer_axis( + if self.common.egui.log_state.wants_pointer() { + self.common.egui.log_state.handle_pointer_axis( event .amount_discrete(Axis::Horizontal) .or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0)) From e5cd473a3a3bc03304dbcf6d529f8c86feff19b5 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 25 Apr 2022 23:00:30 +0200 Subject: [PATCH 08/19] config: input device configuration --- src/backend/kms/mod.rs | 6 +- src/config.rs | 476 ------------------------------ src/config/mod.rs | 650 +++++++++++++++++++++++++++++++++++++++++ src/config/types.rs | 251 ++++++++++++++++ 4 files changed, 906 insertions(+), 477 deletions(-) delete mode 100644 src/config.rs create mode 100644 src/config/mod.rs create mode 100644 src/config/types.rs diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index e8f2d478..1ab0de7d 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -16,6 +16,7 @@ use smithay::{ allocator::{gbm::GbmDevice, Format}, drm::{DrmDevice, DrmEvent, DrmEventTime, DrmNode, GbmBufferedSurface, NodeType}, egl::{EGLContext, EGLDevice, EGLDisplay}, + input::InputEvent, libinput::{LibinputInputBackend, LibinputSessionInterface}, renderer::{ multigpu::{egl::EglGlesBackend, GpuManager}, @@ -101,7 +102,10 @@ pub fn init_backend(event_loop: &mut EventLoop, state: &mut State) -> Res let libinput_event_source = event_loop .handle() - .insert_source(libinput_backend, move |event, _, state| { + .insert_source(libinput_backend, move |mut event, _, state| { + 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() { state.backend.kms().schedule_render(output); diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index e9a07476..00000000 --- a/src/config.rs +++ /dev/null @@ -1,476 +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, - pub workspace_mode: crate::shell::Mode, -} - -pub struct DynamicConfig { - outputs: (Option, OutputsConfig), -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct OutputsConfig { - pub config: HashMap, Vec>, -} - -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct OutputInfo { - pub connector: String, - pub make: String, - pub model: String, -} - -impl From 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), - 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 { - 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) -> 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>, - backend: &mut BackendData, - shell: &mut Shell, - ) { - let outputs = outputs.map(|x| x.borrow().clone()).collect::>(); - let mut infos = outputs - .iter() - .cloned() - .map(Into::::into) - .collect::>(); - 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::>() - .unwrap() - .borrow() - .clone() - }) - .collect::>(); - - 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::>() - .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::>() - .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>) { - let mut infos = outputs - .map(|o| { - let o = o.borrow(); - ( - Into::::into(o.clone()), - o.user_data() - .get::>() - .unwrap() - .borrow() - .clone(), - ) - }) - .collect::>(); - 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, &'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 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 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); - -impl From 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 -where - D: serde::Deserializer<'de>, -{ - KeyModifiersDef::deserialize(deserializer).map(Into::into) -} - -#[allow(non_snake_case)] -fn deserialize_Keysym<'de, D>(deserializer: D) -> Result -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(::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, 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), -} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 00000000..a0e1276c --- /dev/null +++ b/src/config/mod.rs @@ -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, + pub workspace_mode: crate::shell::Mode, +} + +pub struct DynamicConfig { + outputs: (Option, OutputsConfig), + inputs: (Option, InputsConfig), +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct OutputsConfig { + pub config: HashMap, Vec>, +} + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct OutputInfo { + pub connector: String, + pub make: String, + pub model: String, +} + +impl From 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), + 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 { + 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, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct InputConfig { + state: DeviceState, + #[serde(skip_serializing_if="Option::is_none")] + acceleration: Option, + #[serde(skip_serializing_if="Option::is_none")] + calibration: Option<[f32; 6]>, + #[serde(with = "ClickMethodDef")] + #[serde(skip_serializing_if="Option::is_none")] + click_method: Option, + #[serde(skip_serializing_if="Option::is_none")] + disable_while_typing: Option, + #[serde(skip_serializing_if="Option::is_none")] + left_handed: Option, + #[serde(skip_serializing_if="Option::is_none")] + middle_button_emulation: Option, + #[serde(skip_serializing_if="Option::is_none")] + rotation_angle: Option, + #[serde(skip_serializing_if="Option::is_none")] + scroll_config: Option, + #[serde(skip_serializing_if="Option::is_none")] + tap_config: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct AccelConfig { + #[serde(with = "AccelProfileDef")] + profile: Option, + speed: f64, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ScrollConfig { + #[serde(with = "ScrollMethodDef")] + method: Option, + natural_scroll: Option, + scroll_button: Option, +} + +#[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, + 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) -> 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) -> 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>, + backend: &mut BackendData, + shell: &mut Shell, + ) { + let outputs = outputs.map(|x| x.borrow().clone()).collect::>(); + let mut infos = outputs + .iter() + .cloned() + .map(Into::::into) + .collect::>(); + 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::>() + .unwrap() + .borrow() + .clone() + }) + .collect::>(); + + 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::>() + .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::>() + .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>) { + let mut infos = outputs + .map(|o| { + let o = o.borrow(); + ( + Into::::into(o.clone()), + o.user_data() + .get::>() + .unwrap() + .borrow() + .clone(), + ) + }) + .collect::>(); + 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.sysname().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, &'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 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 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, 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), +} diff --git a/src/config/types.rs b/src/config/types.rs new file mode 100644 index 00000000..ccb2f36e --- /dev/null +++ b/src/config/types.rs @@ -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, +} + +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, 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(arg: &Option, ser: S) -> Result + 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, 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(arg: &Option, ser: S) -> Result + 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, 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(arg: &Option, ser: S) -> Result + 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, 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(arg: &Option, ser: S) -> Result + 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); + +impl From 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 +where + D: serde::Deserializer<'de>, +{ + KeyModifiersDef::deserialize(deserializer).map(Into::into) +} + +#[allow(non_snake_case)] +pub fn deserialize_Keysym<'de, D>(deserializer: D) -> Result +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(::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), + } +} + From 81374ed282870b3b03541a0599bc6fd08197780a Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 12:28:50 +0200 Subject: [PATCH 09/19] kms: Corrently resume when switching back VTs --- src/backend/kms/mod.rs | 119 +++++++++++++++++++++++++++++------------ src/backend/mod.rs | 2 +- 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 1ab0de7d..395085d0 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -35,7 +35,7 @@ use smithay::{ nix::{fcntl::OFlag, sys::stat::dev_t}, wayland_server::protocol::wl_output, }, - utils::signaling::{Linkable, Signaler}, + utils::signaling::{Linkable, Signaler, SignalToken}, wayland::output::{Mode as OutputMode, Output, PhysicalProperties}, }; @@ -59,7 +59,8 @@ pub struct KmsState { primary: DrmNode, session: AutoSession, signaler: Signaler, - tokens: Vec, + _restart_token: SignalToken, + _tokens: Vec, } pub struct Device { @@ -87,7 +88,7 @@ pub struct Surface { fps: Fps, } -pub fn init_backend(event_loop: &mut EventLoop, 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 signaler = notifier.signaler(); @@ -147,51 +148,94 @@ pub fn init_backend(event_loop: &mut EventLoop, state: &mut State) -> Res }; 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 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 output in state.common.shell.outputs() { + state.backend.kms().schedule_render(output); + } + }); + } + }); + state.backend = BackendData::Kms(KmsState { api, - tokens: vec![libinput_event_source, session_event_source], + _tokens: vec![libinput_event_source, session_event_source, udev_event_source], primary, session, signaler, + _restart_token, devices: HashMap::new(), }); - for (dev, path) in udev_backend.device_list() { + for (dev, path) in udev_dispatcher.as_source_ref().device_list() { state .device_added(dev, path.into()) .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(()) } impl State { fn device_added(&mut self, dev: dev_t, path: PathBuf) -> Result<()> { + if !self.backend.kms().session.is_active() { + return Ok(()); + } + let fd = SessionFd::new( self.backend .kms() @@ -335,6 +379,10 @@ impl State { } 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 mut outputs_removed = Vec::new(); let mut outputs_added = Vec::new(); @@ -404,9 +452,12 @@ impl State { self.common .output_conf .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(); - self.common.config.write_outputs(self.common.output_conf.outputs()); + + if self.backend.kms().session.is_active() { + 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(()) } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index b3487e47..adde5bd8 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -12,7 +12,7 @@ pub mod x11; // TODO // pub mod wayland; // tbd in smithay -pub fn init_backend_auto(event_loop: &mut EventLoop, 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") { Ok(x) if x == "x11" => x11::init_backend(event_loop, state), Ok(x) if x == "winit" => winit::init_backend(event_loop, state), From 969cc8dae5ea20c13561d198fd1bf657506ebba6 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 16:52:49 +0200 Subject: [PATCH 10/19] render: Reset buffers after non-space-tracked rendering --- src/backend/kms/mod.rs | 20 ++++++++++++++++---- src/backend/render/mod.rs | 16 +++++++++++++++- src/backend/winit.rs | 17 ++++++++++++++++- src/backend/x11.rs | 4 ++++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 395085d0..4b71068b 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -612,11 +612,23 @@ impl Surface { return Ok(()); } - let nodes = state + if render::needs_buffer_reset(&self.output, state) { + self.surface.as_mut().unwrap().reset_buffers(); + } + + let workspace = state .shell - .active_space(&self.output) - .space - .windows() + .active_space(&self.output); + let nodes = workspace + .get_fullscreen(&self.output) + .map(|w| vec![w]) + .unwrap_or_else(|| + workspace + .space + .windows() + .collect::>() + ) + .into_iter() .flat_map(|w| { w.toplevel() .get_surface()? diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index c87fe816..3da7aafc 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -104,6 +104,20 @@ 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::().unwrap().0.swap(will_render_custom, Ordering::AcqRel) != will_render_custom +} + pub fn render_output( gpu: Option<&DrmNode>, renderer: &mut R, @@ -128,7 +142,7 @@ where 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"))] + #[cfg(not(feature = "debug"))] { render_fullscreen(gpu, renderer, window, state, output, hardware_cursor) } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 175221a2..70ed9cca 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -35,15 +35,25 @@ pub struct WinitState { backend: Rc>, //_global: GlobalDrop, output: Output, + age_reset: u8, #[cfg(feature = "debug")] fps: Fps, } impl WinitState { 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(); 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( None, @@ -98,6 +108,10 @@ impl WinitState { Ok(()) } } + + pub fn reset_buffers(&mut self) { + self.age_reset = 3; + } } pub fn init_backend(event_loop: &mut EventLoop, state: &mut State) -> Result<()> { @@ -186,6 +200,7 @@ pub fn init_backend(event_loop: &mut EventLoop, state: &mut State) -> Res output: output.clone(), #[cfg(feature = "debug")] fps: Fps::default(), + age_reset: 0, }); state.common.output_conf.add_heads(std::iter::once(&output)); state diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 0b81eb48..2ba5264b 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -179,6 +179,10 @@ impl Surface { renderer: &mut Gles2Renderer, state: &mut Common, ) -> Result<()> { + if render::needs_buffer_reset(&self.output, state) { + self.surface.reset_buffers(); + } + let (buffer, age) = self .surface .buffer() From 83a8b37ee565ecafcd63ceea0adb06df1c81cc14 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 16:53:12 +0200 Subject: [PATCH 11/19] config: Correctly deserialize input config --- src/config/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index a0e1276c..aac12e9a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -116,24 +116,24 @@ pub struct InputsConfig { #[derive(Debug, Deserialize, Serialize)] pub struct InputConfig { state: DeviceState, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] acceleration: Option, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] calibration: Option<[f32; 6]>, #[serde(with = "ClickMethodDef")] - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] click_method: Option, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] disable_while_typing: Option, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] left_handed: Option, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] middle_button_emulation: Option, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] rotation_angle: Option, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] scroll_config: Option, - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none", default)] tap_config: Option, } From 2890c512df78166a6197c60dc996f233c362fe3a Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 16:54:26 +0200 Subject: [PATCH 12/19] config: Use input device name instead of sysname --- src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index aac12e9a..f0768d72 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -367,7 +367,7 @@ impl Config { use std::collections::hash_map::Entry; let mut inputs = self.dynamic_conf.inputs_mut(); - match inputs.devices.entry(device.sysname().into()) { + match inputs.devices.entry(device.name().into()) { Entry::Occupied(entry) => { let config = entry.get(); if let Err(err) = match config.state { From 9f767a1d133ed6d55aa12ea0fcd5ef6965d2b4b9 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 16:56:08 +0200 Subject: [PATCH 13/19] shell: Cleanup dead fullscreen surfaces --- src/shell/workspace.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index c698dcbe..c4015e47 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -129,6 +129,7 @@ impl Workspace { for window in dead_output_windows { self.unfullscreen_request(&window); } + self.fullscreen.retain(|_, w| w.toplevel().alive()); self.layout.refresh(&mut self.space); self.space.refresh(); } From 11fabec166977f8ecd62d0fbc7ae573f3d03d752 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 16:56:39 +0200 Subject: [PATCH 14/19] shell: Don't resize pending fullscreen surfaces --- src/shell/layout/tiling/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 1385b25c..594d733f 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -529,7 +529,8 @@ impl TilingLayout { if let Some(geo) = geo { #[allow(irrefutable_let_patterns)] if let Kind::Xdg(xdg) = &window.toplevel() { - if xdg.current_state().map(|state| state.states.contains(XdgState::Fullscreen)).unwrap_or(false) { + 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| { From 4ecc043cab488bad902614f98c91a300c5c58c34 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 18:55:04 +0200 Subject: [PATCH 15/19] shell: More cleanups --- src/shell/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index dbc507b5..1e647e38 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -428,6 +428,7 @@ impl Shell { } pub fn refresh(&mut self) { + self.popups.cleanup(); for output in self.outputs.iter() { let workspace = match &self.mode { Mode::OutputBound => { @@ -442,6 +443,9 @@ impl Shell { Mode::Global { active } => &mut self.spaces[*active], }; workspace.refresh(); + let mut map = layer_map_for_output(output); + map.cleanup(); + map.arrange(); } } From 8eff406a4f3acb1ce0ff9f4b2e9c24916b9ccc61 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 26 Apr 2022 18:56:17 +0200 Subject: [PATCH 16/19] input: Update for newest smithay master --- src/input/mod.rs | 29 ++++++++--------------------- src/shell/layout/tiling/mod.rs | 5 ++--- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 42c6058d..75ac7655 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -546,18 +546,10 @@ impl State { ) .map(|(s, _)| s); } - } else if let Some(window) = - workspace.space.window_under(relative_pos).cloned() + } else if let Some((_, surface, _)) = + workspace.space.surface_under(relative_pos, WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE) { - let window_loc = - workspace.space.window_location(&window).unwrap(); - under = window - .surface_under( - relative_pos - window_loc.to_f64(), - WindowSurfaceType::TOPLEVEL - | WindowSurfaceType::SUBSURFACE, - ) - .map(|(s, _)| s); + under = Some(surface); } else if let Some(layer) = layers .layer_under(WlrLayer::Bottom, pos) .or_else(|| layers.layer_under(WlrLayer::Background, pos)) @@ -726,16 +718,11 @@ impl State { WindowSurfaceType::ALL, ) .map(|(s, loc)| (s, loc + layer_loc + output_geo.loc)) - } else if let Some(window) = workspace.space.window_under(relative_pos) { - let window_loc = workspace.space.window_location(window).unwrap(); - window - .surface_under(relative_pos - window_loc.to_f64(), WindowSurfaceType::ALL) - .map(|(s, loc)| { - ( - s, - loc + window_loc - (relative_pos - global_pos).to_i32_round(), - ) - }) + } 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)) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 594d733f..20336c15 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -547,12 +547,11 @@ impl TilingLayout { xdg.send_configure(); } } - let window_geo = window.geometry(); space.map_window( &window, ( - geo.loc.x + inner - window_geo.loc.x, - geo.loc.y + inner - window_geo.loc.y, + geo.loc.x + inner, + geo.loc.y + inner, ), false, ); From c567448fc8ae20d5f6026d31701d5498a2e8bec3 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 27 Apr 2022 13:25:17 +0200 Subject: [PATCH 17/19] kms: Fix VT switching back to cosmic --- src/backend/kms/mod.rs | 5 +++++ src/main.rs | 7 +++---- src/state.rs | 5 ++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 4b71068b..2d959724 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -174,6 +174,7 @@ pub fn init_backend(event_loop: &mut EventLoop<'static, State>, state: &mut Stat .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 { @@ -205,10 +206,14 @@ pub fn init_backend(event_loop: &mut EventLoop<'static, State>, state: &mut Stat 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(); } }); diff --git a/src/main.rs b/src/main.rs index ab1bd453..42845d2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,18 +30,17 @@ fn main() -> Result<()> { // init wayland let (display, socket) = init_wayland_display(&mut event_loop)?; // 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 backend::init_backend_auto(&mut event_loop, &mut state)?; // run the event loop - let signal = event_loop.get_signal(); event_loop.run(None, &mut state, |state| { // shall we shut down? if state.common.shell.outputs().next().is_none() || state.common.should_stop { slog_scope::info!("Shutting down"); - signal.stop(); - signal.wakeup(); + state.common.event_loop_signal.stop(); + state.common.event_loop_signal.wakeup(); return; } diff --git a/src/state.rs b/src/state.rs index 650a1861..0bc7a4d5 100644 --- a/src/state.rs +++ b/src/state.rs @@ -8,7 +8,7 @@ use crate::{ }; use smithay::{ reexports::{ - calloop::LoopHandle, + calloop::{LoopHandle, LoopSignal}, wayland_server::{protocol::wl_surface::WlSurface, Display}, }, wayland::{ @@ -47,6 +47,7 @@ pub struct Common { pub display: Rc>, pub socket: OsString, pub event_loop_handle: LoopHandle<'static, State>, + pub event_loop_signal: LoopSignal, pub output_conf: ConfigurationManager, pub shell: Shell, @@ -182,6 +183,7 @@ impl State { mut display: Display, socket: OsString, handle: LoopHandle<'static, State>, + signal: LoopSignal, log: LogState, ) -> State { let config = Config::load(); @@ -326,6 +328,7 @@ impl State { display: Rc::new(RefCell::new(display)), socket, event_loop_handle: handle, + event_loop_signal: signal, output_conf, shell, From 85d47d190376aab89ad0a850e83ce16c2cb7f5a3 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 27 Apr 2022 13:52:47 +0200 Subject: [PATCH 18/19] shell: Fix fullscreen window size on fractional scaled outputs --- src/shell/workspace.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index c4015e47..e5317c2c 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -214,7 +214,9 @@ impl Workspace { .current_mode() .map(|m| m.size) .unwrap_or((0, 0).into()) - .to_logical(output.current_scale().integer_scale()), + .to_f64() + .to_logical(output.current_scale().fractional_scale()) + .to_i32_round(), ); }); From 3c675d3b37d85dfd67f2b7b077f6254a0cbacbbd Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 27 Apr 2022 13:53:48 +0200 Subject: [PATCH 19/19] render: Fix cursor rendering for fullscreen surfaces --- src/backend/render/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 3da7aafc..34813ecd 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -320,13 +320,12 @@ where } for elem in custom_elements { let geo = elem.geometry(); - let location = geo.loc - output_geo.loc; let elem_damage = elem.accumulated_damage(None); elem.draw( renderer, frame, scale, - location, + geo.loc, &[Rectangle::from_loc_and_size((0, 0), geo.size)], &slog_scope::logger(), )?;