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);
|
||||
|
||||
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)]
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@ use {
|
|||
|
||||
pub struct DummyBackend {}
|
||||
|
||||
impl Backend for DummyBackend {
|
||||
fn switch_to(&self, vtnr: u32) {
|
||||
let _ = vtnr;
|
||||
}
|
||||
}
|
||||
impl Backend for DummyBackend {}
|
||||
|
||||
pub struct DummyOutput {
|
||||
pub id: ConnectorId,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ mod video;
|
|||
|
||||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncError, AsyncFd},
|
||||
async_engine::{AsyncError, AsyncFd, Phase},
|
||||
backend::{
|
||||
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId,
|
||||
InputEvent, KeyState,
|
||||
|
|
@ -25,6 +25,7 @@ use {
|
|||
logind::{LogindError, Session},
|
||||
render::RenderError,
|
||||
state::State,
|
||||
tasks::idle,
|
||||
udev::{Udev, UdevError, UdevMonitor},
|
||||
utils::{
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
|
|
@ -34,7 +35,10 @@ use {
|
|||
smallmap::SmallMap,
|
||||
syncqueue::SyncQueue,
|
||||
},
|
||||
video::{drm::DrmError, gbm::GbmError},
|
||||
video::{
|
||||
drm::{DrmError, DRM_MODE_ATOMIC_ALLOW_MODESET},
|
||||
gbm::GbmError,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
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> {
|
||||
|
|
@ -193,6 +225,9 @@ async fn run_(state: Rc<State>) -> Result<(), MetalError> {
|
|||
return Err(MetalError::Enumerate(Box::new(e)));
|
||||
}
|
||||
state.backend.set(Some(metal.clone()));
|
||||
let _idle = state
|
||||
.eng
|
||||
.spawn2(Phase::PostLayout, idle(state.clone(), metal.clone()));
|
||||
pending().await
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1002,7 +1002,14 @@ impl MetalBackend {
|
|||
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() {
|
||||
None => return,
|
||||
Some(b) => b,
|
||||
|
|
|
|||
|
|
@ -113,11 +113,7 @@ pub struct XBackend {
|
|||
_grab: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
impl Backend for XBackend {
|
||||
fn switch_to(&self, _vtnr: u32) {
|
||||
log::error!("X backend cannot switch vts");
|
||||
}
|
||||
}
|
||||
impl Backend for XBackend {}
|
||||
|
||||
struct XBackendData {
|
||||
state: Rc<State>,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ pub enum Cmd {
|
|||
pub struct ScreenshotArgs {
|
||||
/// 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.
|
||||
///
|
||||
/// The filename can contain the usual strftime parameters.
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ async fn run(screenshot: Rc<Screenshot>) {
|
|||
.args
|
||||
.filename
|
||||
.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();
|
||||
if let Err(e) = std::fs::write(&filename, &data) {
|
||||
fatal!("Could not write `{}`: {}", filename, ErrorFmt(e));
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use {
|
|||
logger::Logger,
|
||||
render::{self, RenderError},
|
||||
sighand::{self, SighandError},
|
||||
state::{ConnectorData, State},
|
||||
state::{ConnectorData, IdleState, State},
|
||||
tasks,
|
||||
tree::{
|
||||
container_layout, container_render_data, float_layout, float_titles, DisplayNode,
|
||||
|
|
@ -32,12 +32,12 @@ use {
|
|||
xwayland,
|
||||
},
|
||||
forker::ForkerProxy,
|
||||
std::{cell::Cell, ops::Deref, rc::Rc, sync::Arc},
|
||||
std::{cell::Cell, ops::Deref, rc::Rc, sync::Arc, time::Duration},
|
||||
thiserror::Error,
|
||||
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) {
|
||||
let forker = match ForkerProxy::create() {
|
||||
|
|
@ -123,6 +123,12 @@ fn main_(forker: Rc<ForkerProxy>, logger: Arc<Logger>, _args: &RunArgs) -> Resul
|
|||
connectors: Default::default(),
|
||||
outputs: 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 {
|
||||
|
|
|
|||
|
|
@ -247,10 +247,12 @@ impl Xwindow {
|
|||
_ => return,
|
||||
};
|
||||
let extents = self.surface.extents.get();
|
||||
// let extents = self.xdg.extents.get();
|
||||
// parent.child_active_changed(self, self.active_surfaces.get() > 0);
|
||||
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 {
|
||||
|
|
@ -414,7 +416,9 @@ impl SizedNode for Xwindow {
|
|||
fn change_extents(self: &Rc<Self>, rect: &Rect) {
|
||||
let old = self.data.info.extents.replace(*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() {
|
||||
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,
|
||||
},
|
||||
utils::{
|
||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, fdcloser::FdCloser,
|
||||
linkedlist::LinkedList, queue::AsyncQueue,
|
||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, queue::AsyncQueue,
|
||||
},
|
||||
wheel::Wheel,
|
||||
xkbcommon::{XkbContext, XkbKeymap},
|
||||
|
|
@ -37,6 +37,7 @@ use {
|
|||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -77,6 +78,14 @@ pub struct State {
|
|||
pub connectors: CopyHashMap<ConnectorId, Rc<ConnectorData>>,
|
||||
pub outputs: CopyHashMap<ConnectorId, Rc<OutputData>>,
|
||||
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 {
|
||||
|
|
@ -290,4 +299,10 @@ impl State {
|
|||
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 connector;
|
||||
mod idle;
|
||||
mod input_device;
|
||||
mod slow_clients;
|
||||
mod start_backend;
|
||||
|
||||
pub use start_backend::start_backend;
|
||||
use {
|
||||
crate::{
|
||||
state::State,
|
||||
|
|
@ -12,6 +12,7 @@ use {
|
|||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
pub use {idle::idle, start_backend::start_backend};
|
||||
|
||||
pub async fn handle_backend_events(state: Rc<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 {
|
||||
seat.mark_last_active();
|
||||
self.state.input_occurred();
|
||||
}
|
||||
} else {
|
||||
while self.dev.event().is_some() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use {
|
|||
},
|
||||
rect::Rect,
|
||||
state::State,
|
||||
tree::Node,
|
||||
tree::{Node, SizedNode},
|
||||
utils::{
|
||||
bitflags::BitflagsExt, errorfmt::ErrorFmt, linkedlist::LinkedList, queue::AsyncQueue,
|
||||
},
|
||||
|
|
@ -1344,15 +1344,15 @@ impl Wm {
|
|||
};
|
||||
self.update_override_redirect(data, event.override_redirect);
|
||||
if data.info.override_redirect.get() {
|
||||
let extents = Rect::new_sized(
|
||||
event.x as _,
|
||||
event.y as _,
|
||||
event.width as _,
|
||||
event.height as _,
|
||||
)
|
||||
.unwrap();
|
||||
let changed = data.info.extents.replace(extents) != extents;
|
||||
if changed {
|
||||
if let Some(window) = data.window.get() {
|
||||
let extents = Rect::new_sized(
|
||||
event.x as _,
|
||||
event.y as _,
|
||||
event.width as _,
|
||||
event.height as _,
|
||||
)
|
||||
.unwrap();
|
||||
window.change_extents(&extents);
|
||||
self.state.tree_changed();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue