Merge branch 'master_jammy' into applibrary-laucher_jammy
This commit is contained in:
commit
fa25b9f1a4
17 changed files with 1624 additions and 763 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1311,7 +1311,7 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
|||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/pop-os/smithay?branch=main#78cfeb604a0563c2357a2ecd1ee048b71f96e1a4"
|
||||
source = "git+https://github.com/pop-os/smithay?branch=main#9a2273f4c8fed28d20952a5493627edf9d3cdcfc"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags",
|
||||
|
|
|
|||
|
|
@ -31,9 +31,10 @@
|
|||
(modifiers: [Logo], key: "j"): Focus(Down),
|
||||
(modifiers: [Logo], key: "k"): Focus(Up),
|
||||
(modifiers: [Logo], key: "l"): Focus(Right),
|
||||
//TODO: automatic orientation with Logo+o toggling
|
||||
//TODO: automatic orientation with Logo+o toggling
|
||||
(modifiers: [Logo], key: "v"): Orientation(Vertical),
|
||||
(modifiers: [Logo], key: "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
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
@ -34,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},
|
||||
};
|
||||
|
||||
|
|
@ -58,7 +59,8 @@ pub struct KmsState {
|
|||
primary: DrmNode,
|
||||
session: AutoSession,
|
||||
signaler: Signaler<Signal>,
|
||||
tokens: Vec<RegistrationToken>,
|
||||
_restart_token: SignalToken,
|
||||
_tokens: Vec<RegistrationToken>,
|
||||
}
|
||||
|
||||
pub struct Device {
|
||||
|
|
@ -86,7 +88,7 @@ pub struct Surface {
|
|||
fps: Fps,
|
||||
}
|
||||
|
||||
pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
|
||||
pub fn init_backend(event_loop: &mut EventLoop<'static, State>, state: &mut State) -> Result<()> {
|
||||
let (session, notifier) = AutoSession::new(None).context("Failed to acquire session")?;
|
||||
let signaler = notifier.signaler();
|
||||
|
||||
|
|
@ -101,8 +103,11 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
|||
|
||||
let libinput_event_source = event_loop
|
||||
.handle()
|
||||
.insert_source(libinput_backend, move |event, _, state| {
|
||||
state.common.process_input_event(event);
|
||||
.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);
|
||||
}
|
||||
|
|
@ -143,51 +148,99 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
|||
};
|
||||
slog_scope::info!("Using {} as primary gpu for rendering", primary);
|
||||
|
||||
let udev_dispatcher = Dispatcher::new(udev_backend, move |event, _, state: &mut State| {
|
||||
match match event {
|
||||
UdevEvent::Added { device_id, path } => state
|
||||
.device_added(device_id, path)
|
||||
.with_context(|| format!("Failed to add drm device: {}", device_id)),
|
||||
UdevEvent::Changed { device_id } => state
|
||||
.device_changed(device_id)
|
||||
.with_context(|| format!("Failed to update drm device: {}", device_id)),
|
||||
UdevEvent::Removed { device_id } => state
|
||||
.device_removed(device_id)
|
||||
.with_context(|| format!("Failed to remove drm device: {}", device_id)),
|
||||
} {
|
||||
Ok(()) => {
|
||||
slog_scope::debug!("Successfully handled udev event")
|
||||
}
|
||||
Err(err) => {
|
||||
slog_scope::error!("Error while handling udev event: {}", err)
|
||||
}
|
||||
}
|
||||
});
|
||||
let udev_event_source = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(udev_dispatcher.clone())
|
||||
.unwrap();
|
||||
|
||||
let handle = event_loop.handle();
|
||||
let loop_signal = state.common.event_loop_signal.clone();
|
||||
let dispatcher = udev_dispatcher.clone();
|
||||
let _restart_token = signaler.register(move |signal| {
|
||||
if let Signal::ActivateSession = signal {
|
||||
let dispatcher = dispatcher.clone();
|
||||
handle.insert_idle(move |state| {
|
||||
for (dev, path) in dispatcher.as_source_ref().device_list() {
|
||||
let drm_node = match DrmNode::from_dev_id(dev) {
|
||||
Ok(node) => node,
|
||||
Err(err) => {
|
||||
slog_scope::error!("Failed to read drm device {}: {}", path.display(), err);
|
||||
continue
|
||||
},
|
||||
};
|
||||
if state.backend.kms().devices.contains_key(&drm_node) {
|
||||
if let Err(err) = state.device_changed(dev) {
|
||||
slog_scope::error!("Failed to update drm device {}: {}", path.display(), err);
|
||||
}
|
||||
} else {
|
||||
if let Err(err) = state.device_added(dev, path.into()) {
|
||||
slog_scope::error!("Failed to add drm device {}: {}", path.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
state.common
|
||||
.output_conf
|
||||
.update(&mut *state.common.display.borrow_mut());
|
||||
|
||||
state.common.config.read_outputs(state.common.output_conf.outputs(), &mut state.backend, &mut state.common.shell);
|
||||
state.common.shell.refresh_outputs();
|
||||
state.common.config.write_outputs(state.common.output_conf.outputs());
|
||||
|
||||
for surface in state.backend.kms().devices.values_mut().flat_map(|d| d.surfaces.values_mut()) {
|
||||
surface.pending = false;
|
||||
}
|
||||
for output in state.common.shell.outputs() {
|
||||
state.backend.kms().schedule_render(output);
|
||||
}
|
||||
});
|
||||
loop_signal.wakeup();
|
||||
}
|
||||
});
|
||||
|
||||
state.backend = BackendData::Kms(KmsState {
|
||||
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()
|
||||
|
|
@ -331,6 +384,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();
|
||||
|
|
@ -400,9 +457,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(())
|
||||
}
|
||||
|
|
@ -557,11 +617,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::<Vec<_>>()
|
||||
)
|
||||
.into_iter()
|
||||
.flat_map(|w| {
|
||||
w.toplevel()
|
||||
.get_surface()?
|
||||
|
|
@ -624,6 +696,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,
|
||||
|
|
@ -711,6 +790,7 @@ impl KmsState {
|
|||
false
|
||||
};
|
||||
|
||||
shell.refresh_outputs();
|
||||
if recreated {
|
||||
self.schedule_render(output);
|
||||
}
|
||||
|
|
@ -729,6 +809,7 @@ impl KmsState {
|
|||
}
|
||||
if !surface.pending {
|
||||
surface.pending = true;
|
||||
/*
|
||||
let duration = surface
|
||||
.last_submit
|
||||
.as_ref()
|
||||
|
|
@ -739,12 +820,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);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub mod x11;
|
|||
// TODO
|
||||
// pub mod wayland; // tbd in smithay
|
||||
|
||||
pub fn init_backend_auto(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
|
||||
pub fn init_backend_auto(event_loop: &mut EventLoop<'static, State>, state: &mut State) -> Result<()> {
|
||||
match std::env::var("COSMIC_BACKEND") {
|
||||
Ok(x) if x == "x11" => x11::init_backend(event_loop, state),
|
||||
Ok(x) if x == "winit" => winit::init_backend(event_loop, state),
|
||||
|
|
|
|||
|
|
@ -14,12 +14,18 @@ use smithay::{
|
|||
renderer::{
|
||||
gles2::{Gles2Renderbuffer, Gles2Renderer, Gles2Texture},
|
||||
multigpu::{egl::EglGlesBackend, Error as MultiError, MultiFrame, MultiRenderer},
|
||||
ImportAll, Renderer,
|
||||
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<EglGlesBackend, EglGlesBackend>;
|
||||
|
||||
static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
|
||||
|
||||
smithay::custom_elements! {
|
||||
pub CustomElem<=Gles2Renderer>;
|
||||
SurfaceTree=SurfaceTree,
|
||||
|
|
@ -96,7 +104,72 @@ impl AsGles2Renderer for GlMultiRenderer<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn needs_buffer_reset(output: &Output, state: &Common) -> bool {
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
struct DidCustomRendering(AtomicBool);
|
||||
|
||||
let will_render_custom = {
|
||||
let workspace = state.shell.active_space(output);
|
||||
workspace.get_fullscreen(output).is_some()
|
||||
};
|
||||
|
||||
let userdata = output.user_data();
|
||||
userdata.insert_if_missing(|| DidCustomRendering(AtomicBool::new(false)));
|
||||
userdata.get::<DidCustomRendering>().unwrap().0.swap(will_render_custom, Ordering::AcqRel) != will_render_custom
|
||||
}
|
||||
|
||||
pub fn render_output<R>(
|
||||
gpu: Option<&DrmNode>,
|
||||
renderer: &mut R,
|
||||
age: u8,
|
||||
state: &mut Common,
|
||||
output: &Output,
|
||||
hardware_cursor: bool,
|
||||
#[cfg(feature = "debug")] fps: &mut Fps,
|
||||
) -> Result<Option<Vec<Rectangle<i32, Logical>>>, RenderError<R>>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGles2Renderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
CustomElem: RenderElement<R>,
|
||||
{
|
||||
renderer.downscale_filter(TextureFilter::Linear).map_err(RenderError::Rendering)?;
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
fps.start();
|
||||
}
|
||||
|
||||
let workspace = state.shell.active_space(output);
|
||||
let maybe_fullscreen_window = workspace.get_fullscreen(output).cloned();
|
||||
let res = if let Some(window) = maybe_fullscreen_window {
|
||||
#[cfg(not(feature = "debug"))]
|
||||
{
|
||||
render_fullscreen(gpu, renderer, window, state, output, hardware_cursor)
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
render_fullscreen(gpu, renderer, window, state, output, hardware_cursor, fps)
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(feature = "debug"))]
|
||||
{
|
||||
render_desktop(gpu, renderer, age, state, output, hardware_cursor)
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
render_desktop(gpu, renderer, age, state, output, hardware_cursor, fps)
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
fps.end();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn render_desktop<R>(
|
||||
_gpu: Option<&DrmNode>,
|
||||
renderer: &mut R,
|
||||
age: u8,
|
||||
|
|
@ -110,12 +183,6 @@ where
|
|||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
CustomElem: RenderElement<R>,
|
||||
{
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
fps.start();
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut custom_elements = Vec::<CustomElem>::new();
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
|
|
@ -125,7 +192,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());
|
||||
|
|
@ -160,18 +227,115 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let res = state.shell.active_space_mut(output).space.render_output(
|
||||
state.shell.active_space_mut(output).space.render_output(
|
||||
renderer,
|
||||
&output,
|
||||
age as usize,
|
||||
[0.153, 0.161, 0.165, 1.0],
|
||||
CLEAR_COLOR,
|
||||
&*custom_elements,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
fn render_fullscreen<R>(
|
||||
_gpu: Option<&DrmNode>,
|
||||
renderer: &mut R,
|
||||
window: Window,
|
||||
state: &mut Common,
|
||||
output: &Output,
|
||||
hardware_cursor: bool,
|
||||
#[cfg(feature = "debug")] fps: &mut Fps,
|
||||
) -> Result<Option<Vec<Rectangle<i32, Logical>>>, RenderError<R>>
|
||||
where
|
||||
R: Renderer + ImportAll + AsGles2Renderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
CustomElem: RenderElement<R>,
|
||||
{
|
||||
let transform = Transform::from(output.current_transform());
|
||||
let mode = output.current_mode().unwrap();
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
let output_geo = state.shell.output_geometry(output);
|
||||
|
||||
let mut custom_elements = Vec::<CustomElem>::new();
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
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 elem_damage = elem.accumulated_damage(None);
|
||||
elem.draw(
|
||||
renderer,
|
||||
frame,
|
||||
scale,
|
||||
geo.loc,
|
||||
&[Rectangle::from_loc_and_size((0, 0), geo.size)],
|
||||
&slog_scope::logger(),
|
||||
)?;
|
||||
damage.extend(elem_damage.into_iter().map(|mut rect| {
|
||||
rect.loc += geo.loc;
|
||||
rect
|
||||
}))
|
||||
}
|
||||
Ok(Some(damage))
|
||||
})
|
||||
.and_then(std::convert::identity)
|
||||
.map_err(RenderError::<R>::Rendering)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,15 +35,25 @@ pub struct WinitState {
|
|||
backend: Rc<RefCell<WinitGraphicsBackend>>,
|
||||
//_global: GlobalDrop<WlOutput>,
|
||||
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>, state: &mut State) -> Result<()> {
|
||||
|
|
@ -186,6 +200,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, 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
|
||||
|
|
@ -276,7 +291,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),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -410,7 +414,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);
|
||||
|
|
|
|||
475
src/config.rs
475
src/config.rs
|
|
@ -1,475 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
shell::{
|
||||
Shell,
|
||||
layout::FocusDirection,
|
||||
},
|
||||
state::BackendData,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use smithay::{
|
||||
backend::input::KeyState,
|
||||
utils::{Logical, Physical, Point, Size, Transform},
|
||||
wayland::{
|
||||
output::{Mode, Output},
|
||||
seat::{keysyms as KeySyms, Keysym, ModifiersState as KeyModifiers},
|
||||
},
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf};
|
||||
use xkbcommon::xkb;
|
||||
|
||||
pub struct Config {
|
||||
pub static_conf: StaticConfig,
|
||||
pub dynamic_conf: DynamicConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct StaticConfig {
|
||||
pub key_bindings: HashMap<KeyPattern, Action>,
|
||||
pub workspace_mode: crate::shell::Mode,
|
||||
}
|
||||
|
||||
pub struct DynamicConfig {
|
||||
outputs: (Option<PathBuf>, OutputsConfig),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct OutputsConfig {
|
||||
pub config: HashMap<Vec<OutputInfo>, Vec<OutputConfig>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct OutputInfo {
|
||||
pub connector: String,
|
||||
pub make: String,
|
||||
pub model: String,
|
||||
}
|
||||
|
||||
impl From<Output> for OutputInfo {
|
||||
fn from(o: Output) -> OutputInfo {
|
||||
let physical = o.physical_properties();
|
||||
OutputInfo {
|
||||
connector: o.name(),
|
||||
make: physical.make,
|
||||
model: physical.model,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
pub struct OutputConfig {
|
||||
pub mode: ((i32, i32), Option<u32>),
|
||||
pub vrr: bool,
|
||||
pub scale: f64,
|
||||
#[serde(with = "TransformDef")]
|
||||
pub transform: Transform,
|
||||
pub position: (i32, i32),
|
||||
#[serde(default = "default_enabled")]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for OutputConfig {
|
||||
fn default() -> OutputConfig {
|
||||
OutputConfig {
|
||||
mode: ((0, 0), None),
|
||||
vrr: false,
|
||||
scale: 1.0,
|
||||
transform: Transform::Normal,
|
||||
position: (0, 0),
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputConfig {
|
||||
pub fn mode_size(&self) -> Size<i32, Physical> {
|
||||
self.mode.0.into()
|
||||
}
|
||||
|
||||
pub fn mode_refresh(&self) -> u32 {
|
||||
self.mode.1.unwrap_or(60_000)
|
||||
}
|
||||
|
||||
pub fn output_mode(&self) -> Mode {
|
||||
Mode {
|
||||
size: self.mode_size(),
|
||||
refresh: self.mode_refresh() as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(remote = "Transform")]
|
||||
enum TransformDef {
|
||||
Normal,
|
||||
_90,
|
||||
_180,
|
||||
_270,
|
||||
Flipped,
|
||||
Flipped90,
|
||||
Flipped180,
|
||||
Flipped270,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> Config {
|
||||
let xdg = xdg::BaseDirectories::new().ok();
|
||||
Config {
|
||||
static_conf: Self::load_static(xdg.as_ref()),
|
||||
dynamic_conf: Self::load_dynamic(xdg.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_static(xdg: Option<&xdg::BaseDirectories>) -> StaticConfig {
|
||||
let mut locations = if let Some(base) = xdg {
|
||||
vec![
|
||||
base.get_config_file("cosmic-comp.ron"),
|
||||
base.get_config_file("cosmic-comp/config.ron"),
|
||||
]
|
||||
} else {
|
||||
Vec::with_capacity(3)
|
||||
};
|
||||
if cfg!(debug_assertions) {
|
||||
if let Ok(mut cwd) = std::env::current_dir() {
|
||||
cwd.push("config.ron");
|
||||
locations.push(cwd);
|
||||
}
|
||||
}
|
||||
locations.push(PathBuf::from("/etc/cosmic-comp/config.ron"));
|
||||
locations.push(PathBuf::from("/etc/cosmic-comp.ron"));
|
||||
|
||||
for path in locations {
|
||||
slog_scope::debug!("Trying config location: {}", path.display());
|
||||
if path.exists() {
|
||||
slog_scope::info!("Using config at {}", path.display());
|
||||
return ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap())
|
||||
.expect("Malformed config file");
|
||||
}
|
||||
}
|
||||
|
||||
StaticConfig {
|
||||
key_bindings: HashMap::new(),
|
||||
workspace_mode: crate::shell::Mode::global(),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_dynamic(xdg: Option<&xdg::BaseDirectories>) -> DynamicConfig {
|
||||
let output_path =
|
||||
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
||||
let outputs = Self::load_outputs(&output_path);
|
||||
|
||||
DynamicConfig {
|
||||
outputs: (output_path, outputs),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_outputs(path: &Option<PathBuf>) -> OutputsConfig {
|
||||
if let Some(path) = path.as_ref() {
|
||||
if path.exists() {
|
||||
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
||||
Ok(config) => return config,
|
||||
Err(err) => {
|
||||
slog_scope::warn!("Failed to read output_config ({}), resetting..", err);
|
||||
if let Err(err) = std::fs::remove_file(path) {
|
||||
slog_scope::error!("Failed to remove output_config {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
OutputsConfig {
|
||||
config: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_outputs<'a>(
|
||||
&mut self,
|
||||
outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>,
|
||||
backend: &mut BackendData,
|
||||
shell: &mut Shell,
|
||||
) {
|
||||
let outputs = outputs.map(|x| x.borrow().clone()).collect::<Vec<_>>();
|
||||
let mut infos = outputs
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Into::<crate::config::OutputInfo>::into)
|
||||
.collect::<Vec<_>>();
|
||||
infos.sort();
|
||||
if let Some(configs) = self.dynamic_conf.outputs().config.get(&infos).cloned() {
|
||||
let mut reset = false;
|
||||
let known_good_configs = outputs
|
||||
.iter()
|
||||
.map(|output| {
|
||||
output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.clone()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
|
||||
{
|
||||
let output = outputs
|
||||
.iter()
|
||||
.find(|o| &o.name() == name)
|
||||
.unwrap()
|
||||
.clone();
|
||||
*output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut() = output_config;
|
||||
if let Err(err) = backend.apply_config_for_output(&output, false, shell) {
|
||||
slog_scope::warn!(
|
||||
"Failed to set new config for output {}: {}",
|
||||
output.name(),
|
||||
err
|
||||
);
|
||||
reset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if reset {
|
||||
for (output, output_config) in outputs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.zip(known_good_configs.into_iter())
|
||||
{
|
||||
*output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut() = output_config;
|
||||
if let Err(err) = backend.apply_config_for_output(&output, false, shell)
|
||||
{
|
||||
slog_scope::error!(
|
||||
"Failed to reset config for output {}: {}",
|
||||
output.name(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_outputs<'a>(&mut self, outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>) {
|
||||
let mut infos = outputs
|
||||
.map(|o| {
|
||||
let o = o.borrow();
|
||||
(
|
||||
Into::<crate::config::OutputInfo>::into(o.clone()),
|
||||
o.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(OutputInfo, OutputConfig)>>();
|
||||
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
||||
let (infos, configs) = infos.into_iter().unzip();
|
||||
self
|
||||
.dynamic_conf
|
||||
.outputs_mut()
|
||||
.config
|
||||
.insert(infos, configs);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PersistenceGuard<'a, T: Serialize>(Option<PathBuf>, &'a mut T);
|
||||
|
||||
impl<'a, T: Serialize> std::ops::Deref for PersistenceGuard<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Serialize> std::ops::DerefMut for PersistenceGuard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Serialize> Drop for PersistenceGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(path) = self.0.as_ref() {
|
||||
let writer = match OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
{
|
||||
Ok(writer) => writer,
|
||||
Err(err) => {
|
||||
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(err) = ron::ser::to_writer_pretty(writer, &self.1, Default::default()) {
|
||||
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DynamicConfig {
|
||||
pub fn outputs(&self) -> &OutputsConfig {
|
||||
&self.outputs.1
|
||||
}
|
||||
|
||||
pub fn outputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, OutputsConfig> {
|
||||
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub enum KeyModifier {
|
||||
Ctrl,
|
||||
Alt,
|
||||
Shift,
|
||||
Logo,
|
||||
CapsLock,
|
||||
NumLock,
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<KeyModifier> for KeyModifiers {
|
||||
fn add_assign(&mut self, rhs: KeyModifier) {
|
||||
match rhs {
|
||||
KeyModifier::Ctrl => self.ctrl = true,
|
||||
KeyModifier::Alt => self.alt = true,
|
||||
KeyModifier::Shift => self.shift = true,
|
||||
KeyModifier::Logo => self.logo = true,
|
||||
KeyModifier::CapsLock => self.caps_lock = true,
|
||||
KeyModifier::NumLock => self.num_lock = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOr for KeyModifier {
|
||||
type Output = KeyModifiers;
|
||||
|
||||
fn bitor(self, rhs: KeyModifier) -> Self::Output {
|
||||
let mut modifiers = self.into();
|
||||
modifiers += rhs;
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<KeyModifiers> for KeyModifier {
|
||||
fn into(self) -> KeyModifiers {
|
||||
let mut modifiers = KeyModifiers {
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
caps_lock: false,
|
||||
logo: false,
|
||||
num_lock: false,
|
||||
};
|
||||
modifiers += self;
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct KeyModifiersDef(Vec<KeyModifier>);
|
||||
|
||||
impl From<KeyModifiersDef> for KeyModifiers {
|
||||
fn from(src: KeyModifiersDef) -> Self {
|
||||
src.0.into_iter().fold(
|
||||
KeyModifiers {
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
caps_lock: false,
|
||||
logo: false,
|
||||
num_lock: false,
|
||||
},
|
||||
|mut modis, modi| {
|
||||
modis += modi;
|
||||
modis
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn deserialize_KeyModifiers<'de, D>(deserializer: D) -> Result<KeyModifiers, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
KeyModifiersDef::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn deserialize_Keysym<'de, D>(deserializer: D) -> Result<Keysym, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, Unexpected};
|
||||
|
||||
let name = String::deserialize(deserializer)?;
|
||||
//let name = format!("KEY_{}", code);
|
||||
match xkb::keysym_from_name(&name, xkb::KEYSYM_NO_FLAGS) {
|
||||
KeySyms::KEY_NoSymbol => match xkb::keysym_from_name(&name, xkb::KEYSYM_CASE_INSENSITIVE) {
|
||||
KeySyms::KEY_NoSymbol => Err(<D::Error as Error>::invalid_value(
|
||||
Unexpected::Str(&name),
|
||||
&"One of the keysym names of xkbcommon.h without the 'KEY_' prefix",
|
||||
)),
|
||||
x => {
|
||||
slog_scope::warn!(
|
||||
"Key-Binding '{}' only matched case insensitive for {:?}",
|
||||
name,
|
||||
xkb::keysym_get_name(x)
|
||||
);
|
||||
Ok(x)
|
||||
}
|
||||
},
|
||||
x => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
/// Describtion of a key combination that might be
|
||||
/// handled by the compositor.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct KeyPattern {
|
||||
/// What modifiers are expected to be pressed alongside the key
|
||||
#[serde(deserialize_with = "deserialize_KeyModifiers")]
|
||||
pub modifiers: KeyModifiers,
|
||||
/// The actual key, that was pressed
|
||||
#[serde(deserialize_with = "deserialize_Keysym")]
|
||||
pub key: u32,
|
||||
}
|
||||
|
||||
impl KeyPattern {
|
||||
pub fn new(modifiers: impl Into<KeyModifiers>, key: u32) -> KeyPattern {
|
||||
KeyPattern {
|
||||
modifiers: modifiers.into(),
|
||||
key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub enum Action {
|
||||
Terminate,
|
||||
Debug,
|
||||
Close,
|
||||
Workspace(u8),
|
||||
MoveToWorkspace(u8),
|
||||
Focus(FocusDirection),
|
||||
Orientation(crate::shell::layout::Orientation),
|
||||
Spawn(String),
|
||||
}
|
||||
650
src/config/mod.rs
Normal file
650
src/config/mod.rs
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
shell::{
|
||||
Shell,
|
||||
layout::FocusDirection,
|
||||
},
|
||||
state::BackendData,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use smithay::{
|
||||
backend::input::KeyState,
|
||||
reexports::input::{AccelProfile, ClickMethod, ScrollMethod, SendEventsMode, TapButtonMap, Device as InputDevice},
|
||||
utils::{Logical, Physical, Point, Size, Transform},
|
||||
wayland::{
|
||||
output::{Mode, Output},
|
||||
seat::{keysyms as KeySyms, Keysym, ModifiersState as KeyModifiers},
|
||||
},
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf};
|
||||
|
||||
mod types;
|
||||
pub use self::types::*;
|
||||
|
||||
pub struct Config {
|
||||
pub static_conf: StaticConfig,
|
||||
pub dynamic_conf: DynamicConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct StaticConfig {
|
||||
pub key_bindings: HashMap<KeyPattern, Action>,
|
||||
pub workspace_mode: crate::shell::Mode,
|
||||
}
|
||||
|
||||
pub struct DynamicConfig {
|
||||
outputs: (Option<PathBuf>, OutputsConfig),
|
||||
inputs: (Option<PathBuf>, InputsConfig),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct OutputsConfig {
|
||||
pub config: HashMap<Vec<OutputInfo>, Vec<OutputConfig>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct OutputInfo {
|
||||
pub connector: String,
|
||||
pub make: String,
|
||||
pub model: String,
|
||||
}
|
||||
|
||||
impl From<Output> for OutputInfo {
|
||||
fn from(o: Output) -> OutputInfo {
|
||||
let physical = o.physical_properties();
|
||||
OutputInfo {
|
||||
connector: o.name(),
|
||||
make: physical.make,
|
||||
model: physical.model,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
pub struct OutputConfig {
|
||||
pub mode: ((i32, i32), Option<u32>),
|
||||
pub vrr: bool,
|
||||
pub scale: f64,
|
||||
#[serde(with = "TransformDef")]
|
||||
pub transform: Transform,
|
||||
pub position: (i32, i32),
|
||||
#[serde(default = "default_enabled")]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for OutputConfig {
|
||||
fn default() -> OutputConfig {
|
||||
OutputConfig {
|
||||
mode: ((0, 0), None),
|
||||
vrr: false,
|
||||
scale: 1.0,
|
||||
transform: Transform::Normal,
|
||||
position: (0, 0),
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputConfig {
|
||||
pub fn mode_size(&self) -> Size<i32, Physical> {
|
||||
self.mode.0.into()
|
||||
}
|
||||
|
||||
pub fn mode_refresh(&self) -> u32 {
|
||||
self.mode.1.unwrap_or(60_000)
|
||||
}
|
||||
|
||||
pub fn output_mode(&self) -> Mode {
|
||||
Mode {
|
||||
size: self.mode_size(),
|
||||
refresh: self.mode_refresh() as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct InputsConfig {
|
||||
xkb: XkbConfig,
|
||||
devices: HashMap<String, InputConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct InputConfig {
|
||||
state: DeviceState,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
acceleration: Option<AccelConfig>,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
calibration: Option<[f32; 6]>,
|
||||
#[serde(with = "ClickMethodDef")]
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
click_method: Option<ClickMethod>,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
disable_while_typing: Option<bool>,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
left_handed: Option<bool>,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
middle_button_emulation: Option<bool>,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
rotation_angle: Option<u32>,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
scroll_config: Option<ScrollConfig>,
|
||||
#[serde(skip_serializing_if="Option::is_none", default)]
|
||||
tap_config: Option<TapConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct AccelConfig {
|
||||
#[serde(with = "AccelProfileDef")]
|
||||
profile: Option<AccelProfile>,
|
||||
speed: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ScrollConfig {
|
||||
#[serde(with = "ScrollMethodDef")]
|
||||
method: Option<ScrollMethod>,
|
||||
natural_scroll: Option<bool>,
|
||||
scroll_button: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum DeviceState {
|
||||
Enabled,
|
||||
Disabled,
|
||||
DisabledOnExternalMouse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TapConfig {
|
||||
enabled: bool,
|
||||
#[serde(with = "TapButtonMapDef")]
|
||||
button_map: Option<TapButtonMap>,
|
||||
drag: bool,
|
||||
drag_lock: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> Config {
|
||||
let xdg = xdg::BaseDirectories::new().ok();
|
||||
Config {
|
||||
static_conf: Self::load_static(xdg.as_ref()),
|
||||
dynamic_conf: Self::load_dynamic(xdg.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_static(xdg: Option<&xdg::BaseDirectories>) -> StaticConfig {
|
||||
let mut locations = if let Some(base) = xdg {
|
||||
vec![
|
||||
base.get_config_file("cosmic-comp.ron"),
|
||||
base.get_config_file("cosmic-comp/config.ron"),
|
||||
]
|
||||
} else {
|
||||
Vec::with_capacity(3)
|
||||
};
|
||||
if cfg!(debug_assertions) {
|
||||
if let Ok(mut cwd) = std::env::current_dir() {
|
||||
cwd.push("config.ron");
|
||||
locations.push(cwd);
|
||||
}
|
||||
}
|
||||
locations.push(PathBuf::from("/etc/cosmic-comp/config.ron"));
|
||||
locations.push(PathBuf::from("/etc/cosmic-comp.ron"));
|
||||
|
||||
for path in locations {
|
||||
slog_scope::debug!("Trying config location: {}", path.display());
|
||||
if path.exists() {
|
||||
slog_scope::info!("Using config at {}", path.display());
|
||||
return ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap())
|
||||
.expect("Malformed config file");
|
||||
}
|
||||
}
|
||||
|
||||
StaticConfig {
|
||||
key_bindings: HashMap::new(),
|
||||
workspace_mode: crate::shell::Mode::global(),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_dynamic(xdg: Option<&xdg::BaseDirectories>) -> DynamicConfig {
|
||||
let output_path =
|
||||
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
||||
let outputs = Self::load_outputs(&output_path);
|
||||
|
||||
let input_path =
|
||||
xdg.and_then(|base| base.place_state_file("cosmic-comp/inputs.ron").ok());
|
||||
let inputs = Self::load_inputs(&input_path);
|
||||
|
||||
DynamicConfig {
|
||||
outputs: (output_path, outputs),
|
||||
inputs: (input_path, inputs),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_outputs(path: &Option<PathBuf>) -> OutputsConfig {
|
||||
if let Some(path) = path.as_ref() {
|
||||
if path.exists() {
|
||||
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
||||
Ok(config) => return config,
|
||||
Err(err) => {
|
||||
slog_scope::warn!("Failed to read output_config ({}), resetting..", err);
|
||||
if let Err(err) = std::fs::remove_file(path) {
|
||||
slog_scope::error!("Failed to remove output_config {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
OutputsConfig {
|
||||
config: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_inputs(path: &Option<PathBuf>) -> InputsConfig {
|
||||
if let Some(path) = path.as_ref() {
|
||||
if path.exists() {
|
||||
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
||||
Ok(config) => return config,
|
||||
Err(err) => {
|
||||
slog_scope::warn!("Failed to read input_config ({}), resetting..", err);
|
||||
if let Err(err) = std::fs::remove_file(path) {
|
||||
slog_scope::error!("Failed to remove input_config {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
InputsConfig {
|
||||
xkb: XkbConfig::default(),
|
||||
devices: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_outputs(
|
||||
&mut self,
|
||||
outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>,
|
||||
backend: &mut BackendData,
|
||||
shell: &mut Shell,
|
||||
) {
|
||||
let outputs = outputs.map(|x| x.borrow().clone()).collect::<Vec<_>>();
|
||||
let mut infos = outputs
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Into::<crate::config::OutputInfo>::into)
|
||||
.collect::<Vec<_>>();
|
||||
infos.sort();
|
||||
if let Some(configs) = self.dynamic_conf.outputs().config.get(&infos).cloned() {
|
||||
let mut reset = false;
|
||||
let known_good_configs = outputs
|
||||
.iter()
|
||||
.map(|output| {
|
||||
output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.clone()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
|
||||
{
|
||||
let output = outputs
|
||||
.iter()
|
||||
.find(|o| &o.name() == name)
|
||||
.unwrap()
|
||||
.clone();
|
||||
*output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut() = output_config;
|
||||
if let Err(err) = backend.apply_config_for_output(&output, false, shell) {
|
||||
slog_scope::warn!(
|
||||
"Failed to set new config for output {}: {}",
|
||||
output.name(),
|
||||
err
|
||||
);
|
||||
reset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if reset {
|
||||
for (output, output_config) in outputs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.zip(known_good_configs.into_iter())
|
||||
{
|
||||
*output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut() = output_config;
|
||||
if let Err(err) = backend.apply_config_for_output(&output, false, shell)
|
||||
{
|
||||
slog_scope::error!(
|
||||
"Failed to reset config for output {}: {}",
|
||||
output.name(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_outputs(&mut self, outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>) {
|
||||
let mut infos = outputs
|
||||
.map(|o| {
|
||||
let o = o.borrow();
|
||||
(
|
||||
Into::<crate::config::OutputInfo>::into(o.clone()),
|
||||
o.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(OutputInfo, OutputConfig)>>();
|
||||
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
||||
let (infos, configs) = infos.into_iter().unzip();
|
||||
self
|
||||
.dynamic_conf
|
||||
.outputs_mut()
|
||||
.config
|
||||
.insert(infos, configs);
|
||||
}
|
||||
|
||||
pub fn read_device(&mut self, device: &mut InputDevice) {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let mut inputs = self.dynamic_conf.inputs_mut();
|
||||
match inputs.devices.entry(device.name().into()) {
|
||||
Entry::Occupied(entry) => {
|
||||
let config = entry.get();
|
||||
if let Err(err) = match config.state {
|
||||
DeviceState::Enabled => device.config_send_events_set_mode(SendEventsMode::ENABLED),
|
||||
DeviceState::Disabled => device.config_send_events_set_mode(SendEventsMode::DISABLED),
|
||||
DeviceState::DisabledOnExternalMouse => device.config_send_events_set_mode(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE),
|
||||
} {
|
||||
slog_scope::warn!("Failed to apply mode {:?} for device {:?}: {:?}", config.state, device.name(), err);
|
||||
}
|
||||
if let Some(accel) = config.acceleration.as_ref() {
|
||||
if let Some(profile) = accel.profile {
|
||||
if let Err(err) = device.config_accel_set_profile(profile) {
|
||||
slog_scope::warn!("Failed to apply acceleration profile {:?} for device {:?}: {:?}", profile, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Err(err) = device.config_accel_set_speed(accel.speed) {
|
||||
slog_scope::warn!("Failed to apply acceleration speed {:?} for device {:?}: {:?}", accel.speed, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(matrix) = config.calibration {
|
||||
if let Err(err) = device.config_calibration_set_matrix(matrix) {
|
||||
slog_scope::warn!("Failed to apply calibration matrix {:?} for device {:?}: {:?}", matrix, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(dwt) = config.disable_while_typing {
|
||||
if let Err(err) = device.config_dwt_set_enabled(dwt) {
|
||||
slog_scope::warn!("Failed to apply disable-while-typing {:?} for device {:?}: {:?}", dwt, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(left) = config.left_handed {
|
||||
if let Err(err) = device.config_left_handed_set(left) {
|
||||
slog_scope::warn!("Failed to apply left-handed {:?} for device {:?}: {:?}", left, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(middle) = config.middle_button_emulation {
|
||||
if let Err(err) = device.config_middle_emulation_set_enabled(middle) {
|
||||
slog_scope::warn!("Failed to apply middle-button-emulation {:?} for device {:?}: {:?}", middle, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(angle) = config.rotation_angle {
|
||||
if let Err(err) = device.config_rotation_set_angle(angle) {
|
||||
slog_scope::warn!("Failed to apply rotation-angle {:?} for device {:?}: {:?}", angle, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(scroll) = config.scroll_config.as_ref() {
|
||||
if let Some(method) = scroll.method {
|
||||
if let Err(err) = device.config_scroll_set_method(method) {
|
||||
slog_scope::warn!("Failed to apply scroll method {:?} for device {:?}: {:?}", method, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(natural) = scroll.natural_scroll {
|
||||
if let Err(err) = device.config_scroll_set_natural_scroll_enabled(natural) {
|
||||
slog_scope::warn!("Failed to apply natural scrolling {:?} for device {:?}: {:?}", natural, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Some(button) = scroll.scroll_button {
|
||||
if let Err(err) = device.config_scroll_set_button(button) {
|
||||
slog_scope::warn!("Failed to apply scroll button {:?} for device {:?}: {:?}", button, device.name(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(tap) = config.tap_config.as_ref() {
|
||||
if let Err(err) = device.config_tap_set_enabled(tap.enabled) {
|
||||
slog_scope::warn!("Failed to apply tap-to-click {:?} for device {:?}: {:?}", tap.enabled, device.name(), err);
|
||||
}
|
||||
if let Some(button_map) = tap.button_map {
|
||||
if let Err(err) = device.config_tap_set_button_map(button_map) {
|
||||
slog_scope::warn!("Failed to apply button map {:?} for device {:?}: {:?}", button_map, device.name(), err);
|
||||
}
|
||||
}
|
||||
if let Err(err) = device.config_tap_set_drag_enabled(tap.drag) {
|
||||
slog_scope::warn!("Failed to apply tap-drag {:?} for device {:?}: {:?}", tap.drag, device.name(), err);
|
||||
}
|
||||
if let Err(err) = device.config_tap_set_drag_lock_enabled(tap.drag_lock) {
|
||||
slog_scope::warn!("Failed to apply tap-drag-lock {:?} for device {:?}: {:?}", tap.drag_lock, device.name(), err);
|
||||
}
|
||||
}
|
||||
},
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(InputConfig {
|
||||
state: match device.config_send_events_mode() {
|
||||
x if x.contains(SendEventsMode::ENABLED) => DeviceState::Enabled,
|
||||
x if x.contains(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE) => DeviceState::DisabledOnExternalMouse,
|
||||
x if x.contains(SendEventsMode::DISABLED) => DeviceState::Disabled,
|
||||
_ => DeviceState::Disabled,
|
||||
},
|
||||
acceleration: if device.config_accel_is_available() {
|
||||
Some(AccelConfig {
|
||||
profile: device.config_accel_profile(),
|
||||
speed: device.config_accel_speed(),
|
||||
})
|
||||
} else { None },
|
||||
calibration: device.config_calibration_matrix(),
|
||||
click_method: device.config_click_method(),
|
||||
disable_while_typing: if device.config_dwt_is_available() {
|
||||
Some(device.config_dwt_enabled())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
left_handed: if device.config_left_handed_is_available() {
|
||||
Some(device.config_left_handed())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
middle_button_emulation: if device.config_middle_emulation_is_available() {
|
||||
Some(device.config_middle_emulation_enabled())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
rotation_angle: if device.config_rotation_is_available() {
|
||||
Some(device.config_rotation_angle())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
scroll_config: if device.config_scroll_methods().iter().any(|x| *x != ScrollMethod::NoScroll) {
|
||||
Some(ScrollConfig {
|
||||
method: device.config_scroll_method(),
|
||||
natural_scroll: if device.config_scroll_has_natural_scroll() {
|
||||
Some(device.config_scroll_natural_scroll_enabled())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
scroll_button: if device.config_scroll_method() == Some(ScrollMethod::OnButtonDown) {
|
||||
Some(device.config_scroll_button())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
} else { None },
|
||||
tap_config: if device.config_tap_finger_count() > 0 {
|
||||
Some(TapConfig {
|
||||
enabled: device.config_tap_enabled(),
|
||||
button_map: device.config_tap_button_map(),
|
||||
drag: device.config_tap_drag_enabled(),
|
||||
drag_lock: device.config_tap_drag_lock_enabled(),
|
||||
})
|
||||
} else { None },
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PersistenceGuard<'a, T: Serialize>(Option<PathBuf>, &'a mut T);
|
||||
|
||||
impl<'a, T: Serialize> std::ops::Deref for PersistenceGuard<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Serialize> std::ops::DerefMut for PersistenceGuard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Serialize> Drop for PersistenceGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(path) = self.0.as_ref() {
|
||||
let writer = match OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
{
|
||||
Ok(writer) => writer,
|
||||
Err(err) => {
|
||||
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(err) = ron::ser::to_writer_pretty(writer, &self.1, Default::default()) {
|
||||
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DynamicConfig {
|
||||
pub fn outputs(&self) -> &OutputsConfig {
|
||||
&self.outputs.1
|
||||
}
|
||||
|
||||
pub fn outputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, OutputsConfig> {
|
||||
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
|
||||
}
|
||||
|
||||
pub fn inputs(&self) -> &InputsConfig {
|
||||
&self.inputs.1
|
||||
}
|
||||
|
||||
pub fn inputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, InputsConfig> {
|
||||
PersistenceGuard(self.inputs.0.clone(), &mut self.inputs.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub enum KeyModifier {
|
||||
Ctrl,
|
||||
Alt,
|
||||
Shift,
|
||||
Logo,
|
||||
CapsLock,
|
||||
NumLock,
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<KeyModifier> for KeyModifiers {
|
||||
fn add_assign(&mut self, rhs: KeyModifier) {
|
||||
match rhs {
|
||||
KeyModifier::Ctrl => self.ctrl = true,
|
||||
KeyModifier::Alt => self.alt = true,
|
||||
KeyModifier::Shift => self.shift = true,
|
||||
KeyModifier::Logo => self.logo = true,
|
||||
KeyModifier::CapsLock => self.caps_lock = true,
|
||||
KeyModifier::NumLock => self.num_lock = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOr for KeyModifier {
|
||||
type Output = KeyModifiers;
|
||||
|
||||
fn bitor(self, rhs: KeyModifier) -> Self::Output {
|
||||
let mut modifiers = self.into();
|
||||
modifiers += rhs;
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<KeyModifiers> for KeyModifier {
|
||||
fn into(self) -> KeyModifiers {
|
||||
let mut modifiers = KeyModifiers {
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
caps_lock: false,
|
||||
logo: false,
|
||||
num_lock: false,
|
||||
};
|
||||
modifiers += self;
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
/// Describtion of a key combination that might be
|
||||
/// handled by the compositor.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct KeyPattern {
|
||||
/// What modifiers are expected to be pressed alongside the key
|
||||
#[serde(deserialize_with = "deserialize_KeyModifiers")]
|
||||
pub modifiers: KeyModifiers,
|
||||
/// The actual key, that was pressed
|
||||
#[serde(deserialize_with = "deserialize_Keysym")]
|
||||
pub key: u32,
|
||||
}
|
||||
|
||||
impl KeyPattern {
|
||||
pub fn new(modifiers: impl Into<KeyModifiers>, key: u32) -> KeyPattern {
|
||||
KeyPattern {
|
||||
modifiers: modifiers.into(),
|
||||
key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub enum Action {
|
||||
Terminate,
|
||||
Debug,
|
||||
Close,
|
||||
Workspace(u8),
|
||||
MoveToWorkspace(u8),
|
||||
Focus(FocusDirection),
|
||||
Orientation(crate::shell::layout::Orientation),
|
||||
Fullscreen,
|
||||
Spawn(String),
|
||||
}
|
||||
251
src/config/types.rs
Normal file
251
src/config/types.rs
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use super::KeyModifier;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use smithay::{
|
||||
backend::input::KeyState,
|
||||
reexports::input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap},
|
||||
utils::{Logical, Physical, Point, Size, Transform},
|
||||
wayland::{
|
||||
output::{Mode, Output},
|
||||
seat::{keysyms as KeySyms, Keysym, ModifiersState as KeyModifiers},
|
||||
},
|
||||
};
|
||||
use xkbcommon::xkb;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct XkbConfig {
|
||||
pub rules: String,
|
||||
pub model: String,
|
||||
pub layout: String,
|
||||
pub variant: String,
|
||||
pub options: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for XkbConfig {
|
||||
fn default() -> XkbConfig {
|
||||
XkbConfig {
|
||||
rules: String::new(),
|
||||
model: String::new(),
|
||||
layout: String::new(),
|
||||
variant: String::new(),
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ClickMethodDef {
|
||||
use smithay::reexports::input::ClickMethod as ClickMethodOrig;
|
||||
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ClickMethod {
|
||||
ButtonAreas,
|
||||
Clickfinger,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ClickMethodOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
ClickMethod::ButtonAreas => ClickMethodOrig::ButtonAreas,
|
||||
ClickMethod::Clickfinger => ClickMethodOrig::Clickfinger,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<ClickMethodOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(ClickMethodOrig::ButtonAreas) => Some(ClickMethod::ButtonAreas),
|
||||
Some(ClickMethodOrig::Clickfinger) => Some(ClickMethod::Clickfinger),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod AccelProfileDef {
|
||||
use smithay::reexports::input::AccelProfile as AccelProfileOrig;
|
||||
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum AccelProfile {
|
||||
Flat,
|
||||
Adaptive,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<AccelProfileOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
AccelProfile::Flat => AccelProfileOrig::Flat,
|
||||
AccelProfile::Adaptive => AccelProfileOrig::Adaptive,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<AccelProfileOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(AccelProfileOrig::Flat) => Some(AccelProfile::Flat),
|
||||
Some(AccelProfileOrig::Adaptive) => Some(AccelProfile::Adaptive),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ScrollMethodDef {
|
||||
use smithay::reexports::input::ScrollMethod as ScrollMethodOrig;
|
||||
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ScrollMethod {
|
||||
NoScroll,
|
||||
TwoFinger,
|
||||
Edge,
|
||||
OnButtonDown,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ScrollMethodOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
ScrollMethod::NoScroll => ScrollMethodOrig::NoScroll,
|
||||
ScrollMethod::TwoFinger => ScrollMethodOrig::TwoFinger,
|
||||
ScrollMethod::Edge => ScrollMethodOrig::Edge,
|
||||
ScrollMethod::OnButtonDown => ScrollMethodOrig::OnButtonDown,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<ScrollMethodOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(ScrollMethodOrig::NoScroll) => Some(ScrollMethod::NoScroll),
|
||||
Some(ScrollMethodOrig::TwoFinger) => Some(ScrollMethod::TwoFinger),
|
||||
Some(ScrollMethodOrig::Edge) => Some(ScrollMethod::Edge),
|
||||
Some(ScrollMethodOrig::OnButtonDown) => Some(ScrollMethod::OnButtonDown),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(remote = "Transform")]
|
||||
pub enum TransformDef {
|
||||
Normal,
|
||||
_90,
|
||||
_180,
|
||||
_270,
|
||||
Flipped,
|
||||
Flipped90,
|
||||
Flipped180,
|
||||
Flipped270,
|
||||
}
|
||||
|
||||
pub mod TapButtonMapDef {
|
||||
use smithay::reexports::input::TapButtonMap as TapButtonMapOrig;
|
||||
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum TapButtonMap {
|
||||
LeftRightMiddle,
|
||||
LeftMiddleRight,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<TapButtonMapOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
TapButtonMap::LeftRightMiddle => TapButtonMapOrig::LeftRightMiddle,
|
||||
TapButtonMap::LeftMiddleRight => TapButtonMapOrig::LeftMiddleRight,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<TapButtonMapOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(TapButtonMapOrig::LeftRightMiddle) => Some(TapButtonMap::LeftRightMiddle),
|
||||
Some(TapButtonMapOrig::LeftMiddleRight) => Some(TapButtonMap::LeftMiddleRight),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct KeyModifiersDef(Vec<KeyModifier>);
|
||||
|
||||
impl From<KeyModifiersDef> for KeyModifiers {
|
||||
fn from(src: KeyModifiersDef) -> Self {
|
||||
src.0.into_iter().fold(
|
||||
KeyModifiers {
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
caps_lock: false,
|
||||
logo: false,
|
||||
num_lock: false,
|
||||
},
|
||||
|mut modis, modi: KeyModifier| {
|
||||
modis += modi;
|
||||
modis
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn deserialize_KeyModifiers<'de, D>(deserializer: D) -> Result<KeyModifiers, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
KeyModifiersDef::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn deserialize_Keysym<'de, D>(deserializer: D) -> Result<Keysym, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, Unexpected};
|
||||
|
||||
let name = String::deserialize(deserializer)?;
|
||||
//let name = format!("KEY_{}", code);
|
||||
match xkb::keysym_from_name(&name, xkb::KEYSYM_NO_FLAGS) {
|
||||
KeySyms::KEY_NoSymbol => match xkb::keysym_from_name(&name, xkb::KEYSYM_CASE_INSENSITIVE) {
|
||||
KeySyms::KEY_NoSymbol => Err(<D::Error as Error>::invalid_value(
|
||||
Unexpected::Str(&name),
|
||||
&"One of the keysym names of xkbcommon.h without the 'KEY_' prefix",
|
||||
)),
|
||||
x => {
|
||||
slog_scope::warn!(
|
||||
"Key-Binding '{}' only matched case insensitive for {:?}",
|
||||
name,
|
||||
xkb::keysym_get_name(x)
|
||||
);
|
||||
Ok(x)
|
||||
}
|
||||
},
|
||||
x => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
362
src/input/mod.rs
362
src/input/mod.rs
|
|
@ -1,19 +1,20 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{config::Action, state::Common};
|
||||
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},
|
||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||
utils::{Logical, Point},
|
||||
utils::{Logical, Point, Rectangle},
|
||||
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<Output>);
|
||||
|
|
@ -114,13 +115,13 @@ pub fn set_active_output(seat: &Seat, output: &Output) {
|
|||
}
|
||||
}
|
||||
|
||||
impl Common {
|
||||
impl State {
|
||||
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
|
||||
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::<Devices>().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::<Devices>().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::<Devices>().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::<SupressedKeys>().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::<SupressedKeys>().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,44 +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.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);
|
||||
|
|
@ -328,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::<Devices>().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)
|
||||
|
|
@ -351,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)
|
||||
|
|
@ -362,13 +382,14 @@ 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,
|
||||
&workspace.space,
|
||||
output_geometry,
|
||||
&workspace,
|
||||
);
|
||||
handle_window_movement(
|
||||
under.as_ref().map(|(s, _)| s),
|
||||
|
|
@ -379,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());
|
||||
}
|
||||
|
|
@ -395,23 +416,24 @@ 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::<Devices>().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,
|
||||
&workspace.space,
|
||||
geometry,
|
||||
&workspace,
|
||||
);
|
||||
handle_window_movement(
|
||||
under.as_ref().map(|(s, _)| s),
|
||||
|
|
@ -422,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());
|
||||
}
|
||||
|
|
@ -441,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::<Devices>().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;
|
||||
|
|
@ -477,58 +499,76 @@ 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;
|
||||
|
||||
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
|
||||
.surface_under(
|
||||
pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.map(|(s, _)| s);
|
||||
| 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((_, surface, _)) =
|
||||
workspace.space.surface_under(relative_pos, WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE)
|
||||
{
|
||||
under = Some(surface);
|
||||
} else if let Some(layer) = layers
|
||||
.layer_under(WlrLayer::Bottom, pos)
|
||||
.or_else(|| layers.layer_under(WlrLayer::Background, pos))
|
||||
{
|
||||
if layer.can_receive_keyboard_focus() {
|
||||
let layer_loc =
|
||||
layers.layer_geometry(layer).unwrap().loc;
|
||||
under = layer
|
||||
.surface_under(
|
||||
pos - output_geo.loc.to_f64()
|
||||
- layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.map(|(s, _)| s);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.set_focus(under.as_ref(), seat, Some(serial));
|
||||
self.common.set_focus(under.as_ref(), seat, Some(serial));
|
||||
}
|
||||
wl_pointer::ButtonState::Pressed
|
||||
}
|
||||
|
|
@ -549,11 +589,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))
|
||||
|
|
@ -565,8 +605,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))
|
||||
|
|
@ -640,55 +680,63 @@ impl Common {
|
|||
global_pos: Point<f64, Logical>,
|
||||
relative_pos: Point<f64, Logical>,
|
||||
output: &Output,
|
||||
space: &Space,
|
||||
output_geo: Rectangle<i32, Logical>,
|
||||
workspace: &Workspace,
|
||||
) -> Option<(WlSurface, Point<i32, Logical>)> {
|
||||
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)
|
||||
.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,
|
||||
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(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(),
|
||||
)
|
||||
})
|
||||
} 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((_, surface, loc)) = workspace.space.surface_under(relative_pos, WindowSurfaceType::ALL) {
|
||||
Some((
|
||||
surface,
|
||||
loc + (global_pos - relative_pos).to_i32_round(),
|
||||
))
|
||||
} else if let Some(layer) = layers
|
||||
.layer_under(WlrLayer::Bottom, relative_pos)
|
||||
.or_else(|| layers.layer_under(WlrLayer::Background, relative_pos))
|
||||
{
|
||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||
layer
|
||||
.surface_under(
|
||||
global_pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.map(|(s, loc)| (s, loc + layer_loc + output_geo.loc))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,18 +30,17 @@ fn main() -> Result<()> {
|
|||
// init wayland
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -215,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*/ }
|
||||
}
|
||||
},
|
||||
|
|
@ -236,26 +259,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::<LayerSurfaceCachedState>();
|
||||
matches!(state.layer, Layer::Top | Layer::Overlay)
|
||||
&& state.keyboard_interactivity != KeyboardInteractivity::None
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
let mut map = layer_map_for_output(&output);
|
||||
map.map_layer(&LayerSurface::new(surface.clone(), namespace))
|
||||
.unwrap();
|
||||
|
||||
if focus {
|
||||
state.set_focus(surface.get_surface(), &seat, Some(SERIAL_COUNTER.next_serial()));
|
||||
}
|
||||
state.shell.active_space_mut(&output).pending_layer(LayerSurface::new(surface, namespace), &output, &seat);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -529,6 +529,10 @@ 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) ||
|
||||
xdg.with_pending_state(|pending| pending.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)
|
||||
|
|
@ -543,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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
output::{Mode as OutputMode, Output, Scale},
|
||||
seat::Seat,
|
||||
shell::xdg::XdgToplevelSurfaceRoleAttributes,
|
||||
shell::{
|
||||
xdg::XdgToplevelSurfaceRoleAttributes,
|
||||
wlr_layer::{Layer, KeyboardInteractivity, LayerSurfaceCachedState},
|
||||
},
|
||||
Serial, SERIAL_COUNTER,
|
||||
},
|
||||
};
|
||||
|
|
@ -103,15 +106,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::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow();
|
||||
let active = output
|
||||
.user_data()
|
||||
.get::<ActiveWorkspace>()
|
||||
|
|
@ -119,7 +117,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 +154,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 +269,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::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow();
|
||||
self.spaces[idx]
|
||||
.space
|
||||
.map_output(output, config.scale, (0, 0));
|
||||
.map_output(output, (0, 0));
|
||||
self.spaces[idx].refresh();
|
||||
}
|
||||
}
|
||||
|
|
@ -294,7 +287,7 @@ impl Shell {
|
|||
.borrow();
|
||||
self.spaces[*active]
|
||||
.space
|
||||
.map_output(output, config.scale, config.position);
|
||||
.map_output(output, config.position);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -353,7 +346,7 @@ impl Shell {
|
|||
self.spaces[old_active].space.unmap_output(output);
|
||||
self.spaces[active]
|
||||
.space
|
||||
.map_output(output, config.scale, config.position);
|
||||
.map_output(output, config.position);
|
||||
self.spaces[active].refresh();
|
||||
}
|
||||
|
||||
|
|
@ -367,11 +360,6 @@ impl Shell {
|
|||
let mut active = Some(active.clone());
|
||||
self.mode = new;
|
||||
for output in &self.outputs {
|
||||
let config = output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow();
|
||||
let workspace = if let Some(a) = active.take() {
|
||||
output
|
||||
.user_data()
|
||||
|
|
@ -381,7 +369,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();
|
||||
}
|
||||
}
|
||||
|
|
@ -440,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 => {
|
||||
|
|
@ -454,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -480,9 +472,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::<LayerSurfaceCachedState>();
|
||||
matches!(state.layer, Layer::Top | Layer::Overlay)
|
||||
&& dbg!(state.keyboard_interactivity) != KeyboardInteractivity::None
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
let mut map = layer_map_for_output(&output);
|
||||
map.map_layer(&layer).unwrap();
|
||||
|
||||
if focus {
|
||||
new_focus = Some(seat);
|
||||
}
|
||||
workspace.pending_layers.retain(|(l, _, _)| l != &layer);
|
||||
}
|
||||
workspace.space.commit(surface);
|
||||
}
|
||||
if let Some(seat) = new_focus {
|
||||
self.set_focus(Some(surface), &seat, seats, None)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use super::{layout, Layout};
|
||||
use indexmap::IndexSet;
|
||||
use smithay::{
|
||||
desktop::{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},
|
||||
|
|
@ -52,6 +52,8 @@ pub struct Workspace {
|
|||
pub space: Space,
|
||||
pub(super) layout: Box<dyn Layout>,
|
||||
pub(super) pending_windows: Vec<(Window, Seat)>,
|
||||
pub(super) pending_layers: Vec<(LayerSurface, Output, Seat)>,
|
||||
pub fullscreen: HashMap<String, Window>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
|
|
@ -61,6 +63,8 @@ impl Workspace {
|
|||
space: Space::new(None),
|
||||
layout: layout::new_default_layout(),
|
||||
pending_windows: Vec::new(),
|
||||
pending_layers: Vec::new(),
|
||||
fullscreen: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,6 +93,11 @@ impl Workspace {
|
|||
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()
|
||||
.insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new())));
|
||||
|
|
@ -108,6 +117,19 @@ impl Workspace {
|
|||
}
|
||||
|
||||
pub fn refresh(&mut self) {
|
||||
let outputs = self.space.outputs().collect::<Vec<_>>();
|
||||
let dead_output_windows = self.fullscreen
|
||||
.iter()
|
||||
.filter(|(name, _)|
|
||||
!outputs.iter().any(|o| o.name() == **name)
|
||||
)
|
||||
.map(|(_, w)| w)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for window in dead_output_windows {
|
||||
self.unfullscreen_request(&window);
|
||||
}
|
||||
self.fullscreen.retain(|_, w| w.toplevel().alive());
|
||||
self.layout.refresh(&mut self.space);
|
||||
self.space.refresh();
|
||||
}
|
||||
|
|
@ -141,6 +163,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)
|
||||
}
|
||||
|
|
@ -152,6 +177,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)
|
||||
}
|
||||
|
|
@ -164,7 +192,73 @@ 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_f64()
|
||||
.to_logical(output.current_scale().fractional_scale())
|
||||
.to_i32_round(),
|
||||
);
|
||||
});
|
||||
|
||||
if ret.is_ok() {
|
||||
xdg.send_configure();
|
||||
self.fullscreen.insert(output.name(), window.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unfullscreen_request(&mut self, window: &Window) {
|
||||
if self.fullscreen.values().any(|w| w == window) {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
if let Kind::Xdg(xdg) = &window.toplevel() {
|
||||
if xdg.get_surface().is_some() {
|
||||
let ret = xdg.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||
state.size = None;
|
||||
});
|
||||
if ret.is_ok() {
|
||||
self.layout.refresh(&mut self.space);
|
||||
xdg.send_configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.fullscreen.retain(|_, w| w != window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fullscreen_toggle(&mut self, window: &Window, output: &Output) {
|
||||
if self.fullscreen.contains_key(&output.name()) {
|
||||
self.unfullscreen_request(window)
|
||||
} else {
|
||||
self.fullscreen_request(window, output)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fullscreen(&self, output: &Output) -> Option<&Window> {
|
||||
if !self.space.outputs().any(|o| o == output) {
|
||||
return None;
|
||||
}
|
||||
self.fullscreen.get(&output.name()).filter(|w| w.toplevel().get_surface().is_some())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
src/state.rs
11
src/state.rs
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
};
|
||||
use smithay::{
|
||||
reexports::{
|
||||
calloop::LoopHandle,
|
||||
calloop::{LoopHandle, LoopSignal},
|
||||
wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||
},
|
||||
wayland::{
|
||||
|
|
@ -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,
|
||||
|
|
@ -47,6 +47,7 @@ pub struct Common {
|
|||
pub display: Rc<RefCell<Display>>,
|
||||
pub socket: OsString,
|
||||
pub event_loop_handle: LoopHandle<'static, State>,
|
||||
pub event_loop_signal: LoopSignal,
|
||||
|
||||
pub output_conf: ConfigurationManager,
|
||||
pub shell: Shell,
|
||||
|
|
@ -145,10 +146,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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue