output_schedule: move cursor scheduler into workspace crate
This commit is contained in:
parent
11940fb6a5
commit
6d569bd4b7
9 changed files with 286 additions and 209 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
|
@ -740,6 +740,7 @@ dependencies = [
|
||||||
"jay-layout-animation",
|
"jay-layout-animation",
|
||||||
"jay-libinput",
|
"jay-libinput",
|
||||||
"jay-logger",
|
"jay-logger",
|
||||||
|
"jay-output-schedule",
|
||||||
"jay-pango",
|
"jay-pango",
|
||||||
"jay-pr-caps",
|
"jay-pr-caps",
|
||||||
"jay-sighand",
|
"jay-sighand",
|
||||||
|
|
@ -937,6 +938,18 @@ dependencies = [
|
||||||
"uapi",
|
"uapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-output-schedule"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-pango"
|
name = "jay-pango"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ members = [
|
||||||
"theme",
|
"theme",
|
||||||
"clientmem",
|
"clientmem",
|
||||||
"allocator",
|
"allocator",
|
||||||
|
"output-schedule",
|
||||||
"pango",
|
"pango",
|
||||||
"libinput",
|
"libinput",
|
||||||
"toml-config",
|
"toml-config",
|
||||||
|
|
@ -97,6 +98,7 @@ jay-gfx-types = { version = "0.1.0", path = "gfx-types" }
|
||||||
jay-theme = { version = "0.1.0", path = "theme" }
|
jay-theme = { version = "0.1.0", path = "theme" }
|
||||||
jay-clientmem = { version = "0.1.0", path = "clientmem" }
|
jay-clientmem = { version = "0.1.0", path = "clientmem" }
|
||||||
jay-allocator = { version = "0.1.0", path = "allocator" }
|
jay-allocator = { version = "0.1.0", path = "allocator" }
|
||||||
|
jay-output-schedule = { version = "0.1.0", path = "output-schedule" }
|
||||||
jay-pango = { version = "0.1.0", path = "pango" }
|
jay-pango = { version = "0.1.0", path = "pango" }
|
||||||
jay-libinput = { version = "0.1.0", path = "libinput" }
|
jay-libinput = { version = "0.1.0", path = "libinput" }
|
||||||
|
|
||||||
|
|
|
||||||
14
output-schedule/Cargo.toml
Normal file
14
output-schedule/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-output-schedule"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-async-engine = { version = "0.1.0", path = "../async-engine" }
|
||||||
|
jay-io-uring = { version = "0.1.0", path = "../io-uring" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
futures-util = "0.3.30"
|
||||||
|
log = "0.4.20"
|
||||||
|
num-traits = "0.2.17"
|
||||||
219
output-schedule/src/lib.rs
Normal file
219
output-schedule/src/lib.rs
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
use {
|
||||||
|
jay_async_engine::AsyncEngine,
|
||||||
|
jay_io_uring::{IoUring, IoUringError},
|
||||||
|
jay_utils::{
|
||||||
|
asyncevent::AsyncEvent, cell_ext::CellExt, clonecell::CloneCell, errorfmt::ErrorFmt,
|
||||||
|
numcell::NumCell,
|
||||||
|
},
|
||||||
|
futures_util::{FutureExt, select},
|
||||||
|
num_traits::ToPrimitive,
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
enum Change {
|
||||||
|
/// The backend has applied the latest changes.
|
||||||
|
None,
|
||||||
|
/// There are changes that the backend is not yet aware of.
|
||||||
|
Scheduled,
|
||||||
|
/// The backend is aware that there are changes and will apply them as part of the
|
||||||
|
/// next latch event.
|
||||||
|
AwaitingLatch,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OutputSchedule {
|
||||||
|
changed: AsyncEvent,
|
||||||
|
run: Cell<bool>,
|
||||||
|
|
||||||
|
damage_connector: Rc<dyn Fn()>,
|
||||||
|
hardware_cursor_damage: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||||
|
cursor_hz_changed: Rc<dyn Fn(Option<f64>)>,
|
||||||
|
|
||||||
|
persistent: Rc<dyn OutputSchedulePersistent>,
|
||||||
|
|
||||||
|
last_present_nsec: Cell<u64>,
|
||||||
|
cursor_delta_nsec: Cell<Option<u64>>,
|
||||||
|
|
||||||
|
ring: Rc<IoUring>,
|
||||||
|
eng: Rc<AsyncEngine>,
|
||||||
|
|
||||||
|
vrr_enabled: Cell<bool>,
|
||||||
|
|
||||||
|
hardware_cursor_change: Cell<Change>,
|
||||||
|
software_cursor_change: Cell<Change>,
|
||||||
|
|
||||||
|
iteration: NumCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait OutputSchedulePersistent {
|
||||||
|
fn vrr_cursor_hz(&self) -> Option<f64>;
|
||||||
|
fn set_vrr_cursor_hz(&self, hz: Option<f64>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputSchedule {
|
||||||
|
pub fn new(
|
||||||
|
ring: Rc<IoUring>,
|
||||||
|
eng: Rc<AsyncEngine>,
|
||||||
|
persistent: Rc<dyn OutputSchedulePersistent>,
|
||||||
|
damage_connector: Rc<dyn Fn()>,
|
||||||
|
cursor_hz_changed: Rc<dyn Fn(Option<f64>)>,
|
||||||
|
) -> Self {
|
||||||
|
let slf = Self {
|
||||||
|
changed: Default::default(),
|
||||||
|
run: Default::default(),
|
||||||
|
damage_connector,
|
||||||
|
cursor_hz_changed,
|
||||||
|
ring,
|
||||||
|
eng,
|
||||||
|
vrr_enabled: Default::default(),
|
||||||
|
hardware_cursor_change: Cell::new(Change::None),
|
||||||
|
software_cursor_change: Cell::new(Change::None),
|
||||||
|
hardware_cursor_damage: Default::default(),
|
||||||
|
persistent: persistent.clone(),
|
||||||
|
last_present_nsec: Default::default(),
|
||||||
|
cursor_delta_nsec: Default::default(),
|
||||||
|
iteration: Default::default(),
|
||||||
|
};
|
||||||
|
if let Some(hz) = persistent.vrr_cursor_hz() {
|
||||||
|
slf.set_cursor_hz(hz);
|
||||||
|
}
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn drive(self: Rc<Self>) {
|
||||||
|
loop {
|
||||||
|
self.run_once().await;
|
||||||
|
while !self.run.take() {
|
||||||
|
self.changed.triggered().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger(&self) {
|
||||||
|
let trigger = self.vrr_enabled.get()
|
||||||
|
&& self.cursor_delta_nsec.is_some()
|
||||||
|
&& (self.software_cursor_change.get() == Change::Scheduled
|
||||||
|
|| self.hardware_cursor_change.get() == Change::Scheduled);
|
||||||
|
if trigger {
|
||||||
|
self.run.set(true);
|
||||||
|
self.changed.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latched(&self) {
|
||||||
|
self.last_present_nsec.set(self.eng.now().nsec());
|
||||||
|
if self.software_cursor_change.get() == Change::AwaitingLatch {
|
||||||
|
self.software_cursor_change.set(Change::None);
|
||||||
|
}
|
||||||
|
if self.hardware_cursor_change.get() == Change::AwaitingLatch {
|
||||||
|
self.hardware_cursor_change.set(Change::None);
|
||||||
|
}
|
||||||
|
self.iteration.fetch_add(1);
|
||||||
|
self.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vrr_enabled(&self) -> bool {
|
||||||
|
self.vrr_enabled.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_vrr_enabled(&self, enabled: bool) {
|
||||||
|
self.vrr_enabled.set(enabled);
|
||||||
|
self.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_hz(&self, hz: f64) {
|
||||||
|
let (hz, delta) = match map_cursor_hz(hz) {
|
||||||
|
None => {
|
||||||
|
log::warn!("Ignoring cursor frequency {hz}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(v) => v,
|
||||||
|
};
|
||||||
|
self.persistent.set_vrr_cursor_hz(hz);
|
||||||
|
(self.cursor_hz_changed)(hz);
|
||||||
|
self.cursor_delta_nsec.set(delta);
|
||||||
|
self.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_hardware_cursor_damage(&self, damage: &Option<Rc<dyn Fn()>>) {
|
||||||
|
self.hardware_cursor_damage.set(damage.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn defer_cursor_updates(&self) -> bool {
|
||||||
|
self.vrr_enabled.get() && self.cursor_delta_nsec.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hardware_cursor_changed(&self) {
|
||||||
|
if self.hardware_cursor_change.get() == Change::None {
|
||||||
|
self.hardware_cursor_change.set(Change::Scheduled);
|
||||||
|
self.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn software_cursor_changed(&self) {
|
||||||
|
if self.software_cursor_change.get() == Change::None {
|
||||||
|
self.software_cursor_change.set(Change::Scheduled);
|
||||||
|
self.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_once(&self) {
|
||||||
|
loop {
|
||||||
|
if self.hardware_cursor_change.get() != Change::Scheduled
|
||||||
|
&& self.software_cursor_change.get() != Change::Scheduled
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if !self.vrr_enabled.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(duration) = self.cursor_delta_nsec.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let iteration = self.iteration.get();
|
||||||
|
let next_present = self.last_present_nsec.get().saturating_add(duration);
|
||||||
|
let res: Result<(), IoUringError> = select! {
|
||||||
|
_ = self.changed.triggered().fuse() => continue,
|
||||||
|
v = self.ring.timeout(next_present).fuse() => v,
|
||||||
|
};
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::error!("Could not wait for timer to expire: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if iteration == self.iteration.get() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.commit_cursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit_cursor(&self) {
|
||||||
|
if self.hardware_cursor_change.get() == Change::Scheduled {
|
||||||
|
if let Some(damage) = self.hardware_cursor_damage.get() {
|
||||||
|
damage();
|
||||||
|
}
|
||||||
|
self.hardware_cursor_change.set(Change::AwaitingLatch);
|
||||||
|
}
|
||||||
|
if self.software_cursor_change.get() == Change::Scheduled {
|
||||||
|
(self.damage_connector)();
|
||||||
|
self.software_cursor_change.set(Change::AwaitingLatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_cursor_hz(hz: f64) -> Option<(Option<f64>, Option<u64>)> {
|
||||||
|
if hz <= 0.0 {
|
||||||
|
return Some((Some(0.0), Some(u64::MAX)));
|
||||||
|
}
|
||||||
|
let delta = (1_000_000_000.0 / hz).to_u64();
|
||||||
|
if delta.is_none() {
|
||||||
|
if hz > 0.0 {
|
||||||
|
return Some((None, None));
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if delta == Some(0) {
|
||||||
|
return Some((None, None));
|
||||||
|
}
|
||||||
|
Some((Some(hz), delta))
|
||||||
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ use {
|
||||||
kbvm::KbvmContext,
|
kbvm::KbvmContext,
|
||||||
leaks,
|
leaks,
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
output_schedule::OutputSchedule,
|
output_schedule::create_output_schedule,
|
||||||
pr_caps::{PrCapsThread, pr_caps},
|
pr_caps::{PrCapsThread, pr_caps},
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
sighand::{self, SighandError},
|
sighand::{self, SighandError},
|
||||||
|
|
@ -713,7 +713,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
head_managers: HeadManagers::new(head_name, head_state),
|
head_managers: HeadManagers::new(head_name, head_state),
|
||||||
wlr_output_heads: Default::default(),
|
wlr_output_heads: Default::default(),
|
||||||
});
|
});
|
||||||
let schedule = Rc::new(OutputSchedule::new(
|
let schedule = Rc::new(create_output_schedule(
|
||||||
state,
|
state,
|
||||||
&connector_data,
|
&connector_data,
|
||||||
&persistent_state,
|
&persistent_state,
|
||||||
|
|
|
||||||
|
|
@ -1528,7 +1528,7 @@ impl ConfigProxyHandler {
|
||||||
match connector {
|
match connector {
|
||||||
Some(c) => {
|
Some(c) => {
|
||||||
let connector = self.get_output_node(c)?;
|
let connector = self.get_output_node(c)?;
|
||||||
connector.schedule.set_cursor_hz(&self.state, hz);
|
connector.schedule.set_cursor_hz(hz);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let Some((hz, _)) = map_cursor_hz(hz) else {
|
let Some((hz, _)) = map_cursor_hz(hz) else {
|
||||||
|
|
|
||||||
|
|
@ -474,7 +474,7 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
let Some(c) = self.get_output_node(req.output) else {
|
let Some(c) = self.get_output_node(req.output) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
c.schedule.set_cursor_hz(&self.state, req.hz);
|
c.schedule.set_cursor_hz(req.hz);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,215 +1,35 @@
|
||||||
|
pub use jay_output_schedule::*;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::AsyncEngine,
|
|
||||||
backend::HardwareCursor,
|
|
||||||
ifs::wl_output::PersistentOutputState,
|
ifs::wl_output::PersistentOutputState,
|
||||||
io_uring::{IoUring, IoUringError},
|
|
||||||
state::{ConnectorData, State},
|
state::{ConnectorData, State},
|
||||||
utils::{
|
|
||||||
asyncevent::AsyncEvent, cell_ext::CellExt, clonecell::CloneCell, errorfmt::ErrorFmt,
|
|
||||||
numcell::NumCell,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
futures_util::{FutureExt, select},
|
std::rc::Rc,
|
||||||
num_traits::ToPrimitive,
|
|
||||||
std::{cell::Cell, rc::Rc},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
impl OutputSchedulePersistent for PersistentOutputState {
|
||||||
enum Change {
|
fn vrr_cursor_hz(&self) -> Option<f64> {
|
||||||
/// The backend has applied the latest changes.
|
self.vrr_cursor_hz.get()
|
||||||
None,
|
|
||||||
/// There are changes that the backend is not yet aware of.
|
|
||||||
Scheduled,
|
|
||||||
/// The backend is aware that there are changes and will apply them as part of the
|
|
||||||
/// next latch event.
|
|
||||||
AwaitingLatch,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OutputSchedule {
|
|
||||||
changed: AsyncEvent,
|
|
||||||
run: Cell<bool>,
|
|
||||||
|
|
||||||
connector: Rc<ConnectorData>,
|
|
||||||
hardware_cursor: CloneCell<Option<Rc<dyn HardwareCursor>>>,
|
|
||||||
|
|
||||||
persistent: Rc<PersistentOutputState>,
|
|
||||||
|
|
||||||
last_present_nsec: Cell<u64>,
|
|
||||||
cursor_delta_nsec: Cell<Option<u64>>,
|
|
||||||
|
|
||||||
ring: Rc<IoUring>,
|
|
||||||
eng: Rc<AsyncEngine>,
|
|
||||||
|
|
||||||
vrr_enabled: Cell<bool>,
|
|
||||||
|
|
||||||
hardware_cursor_change: Cell<Change>,
|
|
||||||
software_cursor_change: Cell<Change>,
|
|
||||||
|
|
||||||
iteration: NumCell<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputSchedule {
|
|
||||||
pub fn new(
|
|
||||||
state: &State,
|
|
||||||
connector: &Rc<ConnectorData>,
|
|
||||||
persistent: &Rc<PersistentOutputState>,
|
|
||||||
) -> Self {
|
|
||||||
let slf = Self {
|
|
||||||
changed: Default::default(),
|
|
||||||
run: Default::default(),
|
|
||||||
connector: connector.clone(),
|
|
||||||
ring: state.ring.clone(),
|
|
||||||
eng: state.eng.clone(),
|
|
||||||
vrr_enabled: Default::default(),
|
|
||||||
hardware_cursor_change: Cell::new(Change::None),
|
|
||||||
software_cursor_change: Cell::new(Change::None),
|
|
||||||
hardware_cursor: Default::default(),
|
|
||||||
persistent: persistent.clone(),
|
|
||||||
last_present_nsec: Default::default(),
|
|
||||||
cursor_delta_nsec: Default::default(),
|
|
||||||
iteration: Default::default(),
|
|
||||||
};
|
|
||||||
if let Some(hz) = persistent.vrr_cursor_hz.get() {
|
|
||||||
slf.set_cursor_hz(state, hz);
|
|
||||||
}
|
|
||||||
slf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn drive(self: Rc<Self>) {
|
fn set_vrr_cursor_hz(&self, hz: Option<f64>) {
|
||||||
loop {
|
self.vrr_cursor_hz.set(hz);
|
||||||
self.run_once().await;
|
|
||||||
while !self.run.take() {
|
|
||||||
self.changed.triggered().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trigger(&self) {
|
|
||||||
let trigger = self.vrr_enabled.get()
|
|
||||||
&& self.cursor_delta_nsec.is_some()
|
|
||||||
&& (self.software_cursor_change.get() == Change::Scheduled
|
|
||||||
|| self.hardware_cursor_change.get() == Change::Scheduled);
|
|
||||||
if trigger {
|
|
||||||
self.run.set(true);
|
|
||||||
self.changed.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn latched(&self) {
|
|
||||||
self.last_present_nsec.set(self.eng.now().nsec());
|
|
||||||
if self.software_cursor_change.get() == Change::AwaitingLatch {
|
|
||||||
self.software_cursor_change.set(Change::None);
|
|
||||||
}
|
|
||||||
if self.hardware_cursor_change.get() == Change::AwaitingLatch {
|
|
||||||
self.hardware_cursor_change.set(Change::None);
|
|
||||||
}
|
|
||||||
self.iteration.fetch_add(1);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vrr_enabled(&self) -> bool {
|
|
||||||
self.vrr_enabled.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_vrr_enabled(&self, enabled: bool) {
|
|
||||||
self.vrr_enabled.set(enabled);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cursor_hz(&self, _state: &State, hz: f64) {
|
|
||||||
let (hz, delta) = match map_cursor_hz(hz) {
|
|
||||||
None => {
|
|
||||||
log::warn!("Ignoring cursor frequency {hz}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Some(v) => v,
|
|
||||||
};
|
|
||||||
self.persistent.vrr_cursor_hz.set(hz);
|
|
||||||
self.connector.head_managers.handle_cursor_hz_change(hz);
|
|
||||||
self.cursor_delta_nsec.set(delta);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_hardware_cursor(&self, hc: &Option<Rc<dyn HardwareCursor>>) {
|
|
||||||
self.hardware_cursor.set(hc.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn defer_cursor_updates(&self) -> bool {
|
|
||||||
self.vrr_enabled.get() && self.cursor_delta_nsec.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hardware_cursor_changed(&self) {
|
|
||||||
if self.hardware_cursor_change.get() == Change::None {
|
|
||||||
self.hardware_cursor_change.set(Change::Scheduled);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn software_cursor_changed(&self) {
|
|
||||||
if self.software_cursor_change.get() == Change::None {
|
|
||||||
self.software_cursor_change.set(Change::Scheduled);
|
|
||||||
self.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_once(&self) {
|
|
||||||
loop {
|
|
||||||
if self.hardware_cursor_change.get() != Change::Scheduled
|
|
||||||
&& self.software_cursor_change.get() != Change::Scheduled
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !self.vrr_enabled.get() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Some(duration) = self.cursor_delta_nsec.get() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let iteration = self.iteration.get();
|
|
||||||
let next_present = self.last_present_nsec.get().saturating_add(duration);
|
|
||||||
let res: Result<(), IoUringError> = select! {
|
|
||||||
_ = self.changed.triggered().fuse() => continue,
|
|
||||||
v = self.ring.timeout(next_present).fuse() => v,
|
|
||||||
};
|
|
||||||
if let Err(e) = res {
|
|
||||||
log::error!("Could not wait for timer to expire: {}", ErrorFmt(e));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if iteration == self.iteration.get() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.commit_cursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn commit_cursor(&self) {
|
|
||||||
if self.hardware_cursor_change.get() == Change::Scheduled {
|
|
||||||
if let Some(hc) = self.hardware_cursor.get() {
|
|
||||||
hc.damage();
|
|
||||||
}
|
|
||||||
self.hardware_cursor_change.set(Change::AwaitingLatch);
|
|
||||||
}
|
|
||||||
if self.software_cursor_change.get() == Change::Scheduled {
|
|
||||||
self.connector.damage();
|
|
||||||
self.software_cursor_change.set(Change::AwaitingLatch);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_cursor_hz(hz: f64) -> Option<(Option<f64>, Option<u64>)> {
|
pub fn create_output_schedule(
|
||||||
if hz <= 0.0 {
|
state: &State,
|
||||||
return Some((Some(0.0), Some(u64::MAX)));
|
connector: &Rc<ConnectorData>,
|
||||||
}
|
persistent: &Rc<PersistentOutputState>,
|
||||||
let delta = (1_000_000_000.0 / hz).to_u64();
|
) -> OutputSchedule {
|
||||||
if delta.is_none() {
|
let damage_connector = connector.clone();
|
||||||
if hz > 0.0 {
|
let cursor_hz_connector = connector.clone();
|
||||||
return Some((None, None));
|
OutputSchedule::new(
|
||||||
}
|
state.ring.clone(),
|
||||||
return None;
|
state.eng.clone(),
|
||||||
}
|
persistent.clone(),
|
||||||
if delta == Some(0) {
|
Rc::new(move || damage_connector.damage()),
|
||||||
return Some((None, None));
|
Rc::new(move |hz| cursor_hz_connector.head_managers.handle_cursor_hz_change(hz)),
|
||||||
}
|
)
|
||||||
Some((Some(hz), delta))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use {
|
||||||
jay_tray_v1::JayTrayV1Global,
|
jay_tray_v1::JayTrayV1Global,
|
||||||
wl_output::{BlendSpace, WlOutputGlobal},
|
wl_output::{BlendSpace, WlOutputGlobal},
|
||||||
},
|
},
|
||||||
output_schedule::OutputSchedule,
|
output_schedule::create_output_schedule,
|
||||||
state::{ConnectorData, OutputData, State},
|
state::{ConnectorData, OutputData, State},
|
||||||
tree::{OutputNode, Transform, WsMoveConfig, move_ws_to_output},
|
tree::{OutputNode, Transform, WsMoveConfig, move_ws_to_output},
|
||||||
utils::{
|
utils::{
|
||||||
|
|
@ -177,7 +177,11 @@ impl ConnectorHandler {
|
||||||
info.primaries,
|
info.primaries,
|
||||||
info.luminance,
|
info.luminance,
|
||||||
));
|
));
|
||||||
let schedule = Rc::new(OutputSchedule::new(&self.state, &self.data, &desired_state));
|
let schedule = Rc::new(create_output_schedule(
|
||||||
|
&self.state,
|
||||||
|
&self.data,
|
||||||
|
&desired_state,
|
||||||
|
));
|
||||||
let _schedule = self
|
let _schedule = self
|
||||||
.state
|
.state
|
||||||
.eng
|
.eng
|
||||||
|
|
@ -309,7 +313,12 @@ impl ConnectorHandler {
|
||||||
match event {
|
match event {
|
||||||
ConnectorEvent::Disconnected => break 'outer,
|
ConnectorEvent::Disconnected => break 'outer,
|
||||||
ConnectorEvent::HardwareCursor(hc) => {
|
ConnectorEvent::HardwareCursor(hc) => {
|
||||||
on.schedule.set_hardware_cursor(&hc);
|
let hardware_cursor_damage = hc.as_ref().map(|hc| {
|
||||||
|
let hc = hc.clone();
|
||||||
|
Rc::new(move || hc.damage()) as Rc<dyn Fn()>
|
||||||
|
});
|
||||||
|
on.schedule
|
||||||
|
.set_hardware_cursor_damage(&hardware_cursor_damage);
|
||||||
on.set_hardware_cursor(hc);
|
on.set_hardware_cursor(hc);
|
||||||
self.state.refresh_hardware_cursors();
|
self.state.refresh_hardware_cursors();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue