autocommit 2022-04-14 16:06:11 CEST
This commit is contained in:
parent
de199a740b
commit
35ddfbcbe3
14 changed files with 203 additions and 35 deletions
|
|
@ -14,7 +14,13 @@ linear_ids!(ConnectorIds, ConnectorId);
|
||||||
linear_ids!(InputDeviceIds, InputDeviceId);
|
linear_ids!(InputDeviceIds, InputDeviceId);
|
||||||
|
|
||||||
pub trait Backend {
|
pub trait Backend {
|
||||||
fn switch_to(&self, vtnr: u32);
|
fn switch_to(&self, vtnr: u32) {
|
||||||
|
let _ = vtnr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_idle(&self, idle: bool) {
|
||||||
|
let _ = idle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,7 @@ use {
|
||||||
|
|
||||||
pub struct DummyBackend {}
|
pub struct DummyBackend {}
|
||||||
|
|
||||||
impl Backend for DummyBackend {
|
impl Backend for DummyBackend {}
|
||||||
fn switch_to(&self, vtnr: u32) {
|
|
||||||
let _ = vtnr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DummyOutput {
|
pub struct DummyOutput {
|
||||||
pub id: ConnectorId,
|
pub id: ConnectorId,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ mod video;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::{AsyncError, AsyncFd},
|
async_engine::{AsyncError, AsyncFd, Phase},
|
||||||
backend::{
|
backend::{
|
||||||
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId,
|
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId,
|
||||||
InputEvent, KeyState,
|
InputEvent, KeyState,
|
||||||
|
|
@ -25,6 +25,7 @@ use {
|
||||||
logind::{LogindError, Session},
|
logind::{LogindError, Session},
|
||||||
render::RenderError,
|
render::RenderError,
|
||||||
state::State,
|
state::State,
|
||||||
|
tasks::idle,
|
||||||
udev::{Udev, UdevError, UdevMonitor},
|
udev::{Udev, UdevError, UdevMonitor},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||||
|
|
@ -34,7 +35,10 @@ use {
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
syncqueue::SyncQueue,
|
syncqueue::SyncQueue,
|
||||||
},
|
},
|
||||||
video::{drm::DrmError, gbm::GbmError},
|
video::{
|
||||||
|
drm::{DrmError, DRM_MODE_ATOMIC_ALLOW_MODESET},
|
||||||
|
gbm::GbmError,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
|
@ -128,6 +132,34 @@ impl Backend for MetalBackend {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_idle(&self, idle: bool) {
|
||||||
|
let devices = self.device_holder.drm_devices.lock();
|
||||||
|
for device in devices.values() {
|
||||||
|
let mut change = device.dev.master.change();
|
||||||
|
for connector in device.connectors.values() {
|
||||||
|
if let Some(crtc) = connector.crtc.get() {
|
||||||
|
if idle == crtc.active.value.get() {
|
||||||
|
crtc.active.value.set(!idle);
|
||||||
|
change.change_object(crtc.id, |c| {
|
||||||
|
c.change(crtc.active.id, (!idle) as _);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = change.commit(DRM_MODE_ATOMIC_ALLOW_MODESET, 0) {
|
||||||
|
log::error!("Could not set monitors idle/not idle: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !idle {
|
||||||
|
for device in devices.values() {
|
||||||
|
for connector in device.connectors.values() {
|
||||||
|
self.present(connector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_(state: Rc<State>) -> Result<(), MetalError> {
|
async fn run_(state: Rc<State>) -> Result<(), MetalError> {
|
||||||
|
|
@ -193,6 +225,9 @@ async fn run_(state: Rc<State>) -> Result<(), MetalError> {
|
||||||
return Err(MetalError::Enumerate(Box::new(e)));
|
return Err(MetalError::Enumerate(Box::new(e)));
|
||||||
}
|
}
|
||||||
state.backend.set(Some(metal.clone()));
|
state.backend.set(Some(metal.clone()));
|
||||||
|
let _idle = state
|
||||||
|
.eng
|
||||||
|
.spawn2(Phase::PostLayout, idle(state.clone(), metal.clone()));
|
||||||
pending().await
|
pending().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1002,7 +1002,14 @@ impl MetalBackend {
|
||||||
self.present(connector);
|
self.present(connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn present(&self, connector: &Rc<MetalConnector>) {
|
pub fn present(&self, connector: &Rc<MetalConnector>) {
|
||||||
|
let crtc = match connector.crtc.get() {
|
||||||
|
Some(crtc) => crtc,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if !crtc.active.value.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let buffers = match connector.buffers.get() {
|
let buffers = match connector.buffers.get() {
|
||||||
None => return,
|
None => return,
|
||||||
Some(b) => b,
|
Some(b) => b,
|
||||||
|
|
|
||||||
|
|
@ -113,11 +113,7 @@ pub struct XBackend {
|
||||||
_grab: SpawnedFuture<()>,
|
_grab: SpawnedFuture<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend for XBackend {
|
impl Backend for XBackend {}
|
||||||
fn switch_to(&self, _vtnr: u32) {
|
|
||||||
log::error!("X backend cannot switch vts");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct XBackendData {
|
struct XBackendData {
|
||||||
state: Rc<State>,
|
state: Rc<State>,
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ pub enum Cmd {
|
||||||
pub struct ScreenshotArgs {
|
pub struct ScreenshotArgs {
|
||||||
/// The filename of the saved screenshot
|
/// The filename of the saved screenshot
|
||||||
///
|
///
|
||||||
/// If no filename is given, the screenshot will be saved under jay-%Y-%m-%d-%H:%M:%S.qoi
|
/// If no filename is given, the screenshot will be saved under %Y-%m-%d-%H%M%S_jay.qoi
|
||||||
/// in the current directory.
|
/// in the current directory.
|
||||||
///
|
///
|
||||||
/// The filename can contain the usual strftime parameters.
|
/// The filename can contain the usual strftime parameters.
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ async fn run(screenshot: Rc<Screenshot>) {
|
||||||
.args
|
.args
|
||||||
.filename
|
.filename
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or("jay-%Y-%m-%d-%H:%M:%S.qoi");
|
.unwrap_or("%Y-%m-%d-%H%M%S_jay.qoi");
|
||||||
let filename = Local::now().format(filename).to_string();
|
let filename = Local::now().format(filename).to_string();
|
||||||
if let Err(e) = std::fs::write(&filename, &data) {
|
if let Err(e) = std::fs::write(&filename, &data) {
|
||||||
fatal!("Could not write `{}`: {}", filename, ErrorFmt(e));
|
fatal!("Could not write `{}`: {}", filename, ErrorFmt(e));
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use {
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
render::{self, RenderError},
|
render::{self, RenderError},
|
||||||
sighand::{self, SighandError},
|
sighand::{self, SighandError},
|
||||||
state::{ConnectorData, State},
|
state::{ConnectorData, IdleState, State},
|
||||||
tasks,
|
tasks,
|
||||||
tree::{
|
tree::{
|
||||||
container_layout, container_render_data, float_layout, float_titles, DisplayNode,
|
container_layout, container_render_data, float_layout, float_titles, DisplayNode,
|
||||||
|
|
@ -32,12 +32,12 @@ use {
|
||||||
xwayland,
|
xwayland,
|
||||||
},
|
},
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
std::{cell::Cell, ops::Deref, rc::Rc, sync::Arc},
|
std::{cell::Cell, ops::Deref, rc::Rc, sync::Arc, time::Duration},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::c,
|
uapi::c,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MAX_EXTENTS: i32 = (1 << 24) - 1;
|
pub const MAX_EXTENTS: i32 = (1 << 22) - 1;
|
||||||
|
|
||||||
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||||
let forker = match ForkerProxy::create() {
|
let forker = match ForkerProxy::create() {
|
||||||
|
|
@ -123,6 +123,12 @@ fn main_(forker: Rc<ForkerProxy>, logger: Arc<Logger>, _args: &RunArgs) -> Resul
|
||||||
connectors: Default::default(),
|
connectors: Default::default(),
|
||||||
outputs: Default::default(),
|
outputs: Default::default(),
|
||||||
status: Default::default(),
|
status: Default::default(),
|
||||||
|
idle: IdleState {
|
||||||
|
input: Default::default(),
|
||||||
|
change: Default::default(),
|
||||||
|
timeout: Cell::new(Duration::from_secs(10)),
|
||||||
|
timeout_changed: Default::default(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
let dummy_output = Rc::new(OutputNode {
|
let dummy_output = Rc::new(OutputNode {
|
||||||
|
|
|
||||||
|
|
@ -247,10 +247,12 @@ impl Xwindow {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let extents = self.surface.extents.get();
|
let extents = self.surface.extents.get();
|
||||||
// let extents = self.xdg.extents.get();
|
|
||||||
// parent.child_active_changed(self, self.active_surfaces.get() > 0);
|
// parent.child_active_changed(self, self.active_surfaces.get() > 0);
|
||||||
parent.node_child_size_changed(self, extents.width(), extents.height());
|
parent.node_child_size_changed(self, extents.width(), extents.height());
|
||||||
// parent.child_title_changed(self, self.title.borrow_mut().deref());
|
parent.node_child_title_changed(
|
||||||
|
self,
|
||||||
|
self.data.info.title.borrow_mut().as_deref().unwrap_or(""),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_mapped(&self) -> bool {
|
pub fn is_mapped(&self) -> bool {
|
||||||
|
|
@ -414,7 +416,9 @@ impl SizedNode for Xwindow {
|
||||||
fn change_extents(self: &Rc<Self>, rect: &Rect) {
|
fn change_extents(self: &Rc<Self>, rect: &Rect) {
|
||||||
let old = self.data.info.extents.replace(*rect);
|
let old = self.data.info.extents.replace(*rect);
|
||||||
if old != *rect {
|
if old != *rect {
|
||||||
self.events.push(XWaylandEvent::Configure(self.clone()));
|
if !self.data.info.override_redirect.get() {
|
||||||
|
self.events.push(XWaylandEvent::Configure(self.clone()));
|
||||||
|
}
|
||||||
if old.position() != rect.position() {
|
if old.position() != rect.position() {
|
||||||
self.surface.set_absolute_position(rect.x1(), rect.y1());
|
self.surface.set_absolute_position(rect.x1(), rect.y1());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
src/state.rs
19
src/state.rs
|
|
@ -25,8 +25,8 @@ use {
|
||||||
OutputNode, SizedNode, WorkspaceNode,
|
OutputNode, SizedNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, fdcloser::FdCloser,
|
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
linkedlist::LinkedList, queue::AsyncQueue,
|
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, queue::AsyncQueue,
|
||||||
},
|
},
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
xkbcommon::{XkbContext, XkbKeymap},
|
xkbcommon::{XkbContext, XkbKeymap},
|
||||||
|
|
@ -37,6 +37,7 @@ use {
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -77,6 +78,14 @@ pub struct State {
|
||||||
pub connectors: CopyHashMap<ConnectorId, Rc<ConnectorData>>,
|
pub connectors: CopyHashMap<ConnectorId, Rc<ConnectorData>>,
|
||||||
pub outputs: CopyHashMap<ConnectorId, Rc<OutputData>>,
|
pub outputs: CopyHashMap<ConnectorId, Rc<OutputData>>,
|
||||||
pub status: CloneCell<Rc<String>>,
|
pub status: CloneCell<Rc<String>>,
|
||||||
|
pub idle: IdleState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IdleState {
|
||||||
|
pub input: Cell<bool>,
|
||||||
|
pub change: AsyncEvent,
|
||||||
|
pub timeout: Cell<Duration>,
|
||||||
|
pub timeout_changed: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InputDeviceData {
|
pub struct InputDeviceData {
|
||||||
|
|
@ -290,4 +299,10 @@ impl State {
|
||||||
output.set_status(&status);
|
output.set_status(&status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn input_occurred(&self) {
|
||||||
|
if !self.idle.input.replace(true) {
|
||||||
|
self.idle.change.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
mod backend;
|
mod backend;
|
||||||
mod connector;
|
mod connector;
|
||||||
|
mod idle;
|
||||||
mod input_device;
|
mod input_device;
|
||||||
mod slow_clients;
|
mod slow_clients;
|
||||||
mod start_backend;
|
mod start_backend;
|
||||||
|
|
||||||
pub use start_backend::start_backend;
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -12,6 +12,7 @@ use {
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
pub use {idle::idle, start_backend::start_backend};
|
||||||
|
|
||||||
pub async fn handle_backend_events(state: Rc<State>) {
|
pub async fn handle_backend_events(state: Rc<State>) {
|
||||||
let mut beh = BackendEventHandler { state };
|
let mut beh = BackendEventHandler { state };
|
||||||
|
|
|
||||||
101
src/tasks/idle.rs
Normal file
101
src/tasks/idle.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
async_engine::{AsyncError, Timer},
|
||||||
|
backend::Backend,
|
||||||
|
state::State,
|
||||||
|
utils::errorfmt::ErrorFmt,
|
||||||
|
},
|
||||||
|
futures_util::{select, FutureExt},
|
||||||
|
std::{rc::Rc, time::Duration},
|
||||||
|
uapi::c,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn idle(state: Rc<State>, backend: Rc<dyn Backend>) {
|
||||||
|
let timer = match state.eng.timer(c::CLOCK_MONOTONIC) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not create idle timer: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
state.idle.change.trigger();
|
||||||
|
state.idle.timeout_changed.set(true);
|
||||||
|
let mut idle = Idle {
|
||||||
|
state,
|
||||||
|
backend,
|
||||||
|
timer,
|
||||||
|
idle: false,
|
||||||
|
dead: false,
|
||||||
|
last_input: now(),
|
||||||
|
};
|
||||||
|
idle.run().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Idle {
|
||||||
|
state: Rc<State>,
|
||||||
|
backend: Rc<dyn Backend>,
|
||||||
|
timer: Timer,
|
||||||
|
idle: bool,
|
||||||
|
dead: bool,
|
||||||
|
last_input: c::timespec,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Idle {
|
||||||
|
async fn run(&mut self) {
|
||||||
|
while !self.dead {
|
||||||
|
select! {
|
||||||
|
res = self.timer.expired().fuse() => self.handle_expired(res),
|
||||||
|
_ = self.state.idle.change.triggered().fuse() => self.handle_idle_changes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_expired(&mut self, res: Result<u64, AsyncError>) {
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::error!("Could not wait for idle timer to expire: {}", ErrorFmt(e));
|
||||||
|
self.dead = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let timeout = self.state.idle.timeout.get();
|
||||||
|
let since = duration_since(self.last_input);
|
||||||
|
if since >= timeout {
|
||||||
|
self.backend.set_idle(true);
|
||||||
|
self.idle = true;
|
||||||
|
} else {
|
||||||
|
self.program_timer(timeout - since);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_idle_changes(&mut self) {
|
||||||
|
if self.state.idle.timeout_changed.replace(false) {
|
||||||
|
self.program_timer(self.state.idle.timeout.get());
|
||||||
|
}
|
||||||
|
if self.state.idle.input.replace(false) {
|
||||||
|
self.last_input = now();
|
||||||
|
if self.idle {
|
||||||
|
self.backend.set_idle(false);
|
||||||
|
self.idle = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_timer(&mut self, timeout: Duration) {
|
||||||
|
if let Err(e) = self.timer.program(Some(timeout), None) {
|
||||||
|
log::error!("Could not program idle timer: {}", ErrorFmt(e));
|
||||||
|
self.dead = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn now() -> c::timespec {
|
||||||
|
let mut now = uapi::pod_zeroed();
|
||||||
|
let _ = uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut now);
|
||||||
|
now
|
||||||
|
}
|
||||||
|
|
||||||
|
fn duration_since(start: c::timespec) -> Duration {
|
||||||
|
let now = now();
|
||||||
|
let nanos =
|
||||||
|
(now.tv_sec as i64 - start.tv_sec as i64) * 1_000_000_000 + (now.tv_nsec - start.tv_nsec);
|
||||||
|
Duration::from_nanos(nanos as u64)
|
||||||
|
}
|
||||||
|
|
@ -56,6 +56,7 @@ impl DeviceHandler {
|
||||||
}
|
}
|
||||||
if any_events {
|
if any_events {
|
||||||
seat.mark_last_active();
|
seat.mark_last_active();
|
||||||
|
self.state.input_occurred();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while self.dev.event().is_some() {
|
while self.dev.event().is_some() {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use {
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::State,
|
state::State,
|
||||||
tree::Node,
|
tree::{Node, SizedNode},
|
||||||
utils::{
|
utils::{
|
||||||
bitflags::BitflagsExt, errorfmt::ErrorFmt, linkedlist::LinkedList, queue::AsyncQueue,
|
bitflags::BitflagsExt, errorfmt::ErrorFmt, linkedlist::LinkedList, queue::AsyncQueue,
|
||||||
},
|
},
|
||||||
|
|
@ -1344,15 +1344,15 @@ impl Wm {
|
||||||
};
|
};
|
||||||
self.update_override_redirect(data, event.override_redirect);
|
self.update_override_redirect(data, event.override_redirect);
|
||||||
if data.info.override_redirect.get() {
|
if data.info.override_redirect.get() {
|
||||||
let extents = Rect::new_sized(
|
if let Some(window) = data.window.get() {
|
||||||
event.x as _,
|
let extents = Rect::new_sized(
|
||||||
event.y as _,
|
event.x as _,
|
||||||
event.width as _,
|
event.y as _,
|
||||||
event.height as _,
|
event.width as _,
|
||||||
)
|
event.height as _,
|
||||||
.unwrap();
|
)
|
||||||
let changed = data.info.extents.replace(extents) != extents;
|
.unwrap();
|
||||||
if changed {
|
window.change_extents(&extents);
|
||||||
self.state.tree_changed();
|
self.state.tree_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue