Merge pull request #223 from mahkoh/jorth/damage-tracking
Implement damage tracking
This commit is contained in:
commit
618fdfefb8
68 changed files with 1533 additions and 403 deletions
|
|
@ -180,6 +180,5 @@ Jay supports the following wayland protocols:
|
|||
|
||||
The following features are currently not supported but might get implemented in the future:
|
||||
|
||||
- Fine-grained damage tracking.
|
||||
- Touch support.
|
||||
- Tearing updates of fullscreen games.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Unreleased
|
||||
|
||||
- Add fine-grained damage tracking.
|
||||
|
||||
# 1.4.0 (2024-07-07)
|
||||
|
||||
- Add window management mode.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub use {crate::async_engine::ae_yield::Yield, ae_task::SpawnedFuture};
|
|||
use {
|
||||
crate::{
|
||||
async_engine::ae_task::Runnable,
|
||||
time::Time,
|
||||
utils::{array, numcell::NumCell, syncqueue::SyncQueue},
|
||||
},
|
||||
std::{
|
||||
|
|
@ -33,6 +34,7 @@ pub struct AsyncEngine {
|
|||
stash: RefCell<VecDeque<Runnable>>,
|
||||
yield_stash: RefCell<VecDeque<Waker>>,
|
||||
stopped: Cell<bool>,
|
||||
now: Cell<Option<Time>>,
|
||||
}
|
||||
|
||||
impl AsyncEngine {
|
||||
|
|
@ -45,6 +47,7 @@ impl AsyncEngine {
|
|||
stash: Default::default(),
|
||||
yield_stash: Default::default(),
|
||||
stopped: Cell::new(false),
|
||||
now: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +87,7 @@ impl AsyncEngine {
|
|||
let mut stash = self.stash.borrow_mut();
|
||||
let mut yield_stash = self.yield_stash.borrow_mut();
|
||||
while self.num_queued.get() > 0 {
|
||||
self.now.take();
|
||||
self.iteration.fetch_add(1);
|
||||
let mut phase = 0;
|
||||
while phase < NUM_PHASES {
|
||||
|
|
@ -119,4 +123,15 @@ impl AsyncEngine {
|
|||
fn iteration(&self) -> u64 {
|
||||
self.iteration.get()
|
||||
}
|
||||
|
||||
pub fn now(&self) -> Time {
|
||||
match self.now.get() {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
let now = Time::now_unchecked();
|
||||
self.now.set(Some(now));
|
||||
now
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ use {
|
|||
},
|
||||
logind::{LogindError, Session},
|
||||
state::State,
|
||||
time::now_usec,
|
||||
udev::{Udev, UdevError, UdevMonitor},
|
||||
utils::{
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
|
|
@ -469,7 +468,7 @@ impl MetalInputDevice {
|
|||
}
|
||||
|
||||
fn pre_pause(&self) {
|
||||
let time_usec = now_usec();
|
||||
let time_usec = self.state.now_usec();
|
||||
for (key, _) in self.pressed_keys.take() {
|
||||
self.event(InputEvent::Key {
|
||||
time_usec,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ use {
|
|||
renderer::RenderResult,
|
||||
state::State,
|
||||
theme::Color,
|
||||
time::now_nsec,
|
||||
tree::OutputNode,
|
||||
udev::UdevDevice,
|
||||
utils::{
|
||||
|
|
@ -591,7 +590,7 @@ impl MetalConnector {
|
|||
});
|
||||
if let Some(delta) = *DELTA {
|
||||
let next_present = self.next_flip_nsec.get().saturating_sub(delta);
|
||||
if now_nsec() < next_present {
|
||||
if self.state.now_nsec() < next_present {
|
||||
self.state.ring.timeout(next_present).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -836,6 +835,7 @@ impl MetalConnector {
|
|||
render_hw_cursor,
|
||||
output.has_fullscreen(),
|
||||
output.global.persistent.transform.get(),
|
||||
Some(&self.state.damage_visualizer),
|
||||
);
|
||||
let try_direct_scanout = try_direct_scanout
|
||||
&& self.direct_scanout_enabled()
|
||||
|
|
@ -923,9 +923,10 @@ impl MetalConnector {
|
|||
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
||||
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
||||
let mut rr = self.render_result.borrow_mut();
|
||||
rr.output_id = node.id;
|
||||
let fb =
|
||||
self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout)?;
|
||||
rr.dispatch_frame_requests();
|
||||
rr.dispatch_frame_requests(self.state.now_msec());
|
||||
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
|
||||
match &fb.direct_scanout_data {
|
||||
None => {
|
||||
|
|
@ -2172,9 +2173,9 @@ impl MetalBackend {
|
|||
_ => return,
|
||||
};
|
||||
connector.can_present.set(true);
|
||||
connector
|
||||
.active_framebuffer
|
||||
.set(connector.next_framebuffer.take());
|
||||
if let Some(fb) = connector.next_framebuffer.take() {
|
||||
connector.active_framebuffer.set(Some(fb));
|
||||
}
|
||||
if connector.has_damage.get() || connector.cursor_changed.get() {
|
||||
connector.schedule_present();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use {
|
|||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
||||
renderer::RenderResult,
|
||||
state::State,
|
||||
time::now_usec,
|
||||
utils::{
|
||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
|
||||
queue::AsyncQueue, syncqueue::SyncQueue,
|
||||
|
|
@ -818,7 +817,7 @@ impl XBackend {
|
|||
inverted: false,
|
||||
});
|
||||
seat.mouse_event(InputEvent::AxisFrame {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.state.now_usec(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
|
@ -834,7 +833,7 @@ impl XBackend {
|
|||
n => BTN_SIDE + n - 8,
|
||||
};
|
||||
seat.mouse_event(InputEvent::Button {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.state.now_usec(),
|
||||
button,
|
||||
state,
|
||||
});
|
||||
|
|
@ -851,7 +850,7 @@ impl XBackend {
|
|||
let event: XiKeyPress = event.parse()?;
|
||||
if let Some(seat) = self.seats.get(&event.deviceid) {
|
||||
seat.kb_event(InputEvent::Key {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.state.now_usec(),
|
||||
key: event.detail - 8,
|
||||
state,
|
||||
});
|
||||
|
|
@ -885,7 +884,7 @@ impl XBackend {
|
|||
self.mouse_seats.get(&event.deviceid),
|
||||
) {
|
||||
seat.mouse_event(InputEvent::ConnectorPosition {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.state.now_usec(),
|
||||
connector: win.id,
|
||||
x: Fixed::from_1616(event.event_x),
|
||||
y: Fixed::from_1616(event.event_y),
|
||||
|
|
@ -904,7 +903,7 @@ impl XBackend {
|
|||
_ => return Ok(()),
|
||||
};
|
||||
seat.mouse_event(InputEvent::ConnectorPosition {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.state.now_nsec(),
|
||||
connector: win.id,
|
||||
x: Fixed::from_1616(event.event_x),
|
||||
y: Fixed::from_1616(event.event_y),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
mod color;
|
||||
mod damage_tracking;
|
||||
mod duration;
|
||||
mod generate;
|
||||
mod idle;
|
||||
mod input;
|
||||
|
|
@ -12,7 +15,7 @@ mod unlock;
|
|||
|
||||
use {
|
||||
crate::{
|
||||
cli::{input::InputArgs, randr::RandrArgs},
|
||||
cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs},
|
||||
compositor::start_compositor,
|
||||
portal,
|
||||
},
|
||||
|
|
@ -65,6 +68,9 @@ pub enum Cmd {
|
|||
Randr(RandrArgs),
|
||||
/// Inspect/modify input settings.
|
||||
Input(InputArgs),
|
||||
/// Modify damage tracking settings. (Only for debugging.)
|
||||
#[clap(hide = true)]
|
||||
DamageTracking(DamageTrackingArgs),
|
||||
#[cfg(feature = "it")]
|
||||
RunTests,
|
||||
}
|
||||
|
|
@ -241,6 +247,7 @@ pub fn main() {
|
|||
Cmd::Portal => portal::run_freestanding(cli.global),
|
||||
Cmd::Randr(a) => randr::main(cli.global, a),
|
||||
Cmd::Input(a) => input::main(cli.global, a),
|
||||
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
|
||||
#[cfg(feature = "it")]
|
||||
Cmd::RunTests => crate::it::run_tests(),
|
||||
}
|
||||
|
|
|
|||
36
src/cli/color.rs
Normal file
36
src/cli/color.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use {
|
||||
crate::{theme::Color, utils::errorfmt::ErrorFmt},
|
||||
std::ops::Range,
|
||||
};
|
||||
|
||||
pub fn parse_color(string: &str) -> Color {
|
||||
let hex = match string.strip_prefix("#") {
|
||||
Some(s) => s,
|
||||
_ => fatal!("Color must start with #"),
|
||||
};
|
||||
let d = |range: Range<usize>| match u8::from_str_radix(&hex[range.clone()], 16) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
fatal!(
|
||||
"Could not parse color component {}: {}",
|
||||
&hex[range],
|
||||
ErrorFmt(e)
|
||||
)
|
||||
}
|
||||
};
|
||||
let s = |range: Range<usize>| {
|
||||
let v = d(range);
|
||||
(v << 4) | v
|
||||
};
|
||||
let (r, g, b, a) = match hex.len() {
|
||||
3 => (s(0..1), s(1..2), s(2..3), u8::MAX),
|
||||
4 => (s(0..1), s(1..2), s(2..3), s(3..4)),
|
||||
6 => (d(0..2), d(2..4), d(4..6), u8::MAX),
|
||||
8 => (d(0..2), d(2..4), d(4..6), d(6..8)),
|
||||
_ => fatal!(
|
||||
"Unexpected length of color string (should be 3, 4, 6, or 8): {}",
|
||||
hex.len()
|
||||
),
|
||||
};
|
||||
jay_config::theme::Color::new_straight(r, g, b, a).into()
|
||||
}
|
||||
106
src/cli/damage_tracking.rs
Normal file
106
src/cli/damage_tracking.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{color::parse_color, duration::parse_duration, GlobalArgs},
|
||||
tools::tool_client::{with_tool_client, ToolClient},
|
||||
wire::jay_damage_tracking::{SetVisualizerColor, SetVisualizerDecay, SetVisualizerEnabled},
|
||||
},
|
||||
clap::{Args, Subcommand},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct DamageTrackingArgs {
|
||||
#[clap(subcommand)]
|
||||
pub command: DamageTrackingCmd,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum DamageTrackingCmd {
|
||||
/// Visualize damage.
|
||||
Show,
|
||||
/// Hide damage.
|
||||
Hide,
|
||||
/// Set the color used for damage visualization.
|
||||
SetColor(ColorArgs),
|
||||
/// Set the amount of time damage is shown.
|
||||
SetDecay(DecayArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ColorArgs {
|
||||
/// The color to visualize damage.
|
||||
///
|
||||
/// Should be specified in one of the following formats:
|
||||
///
|
||||
/// * `#rgb`
|
||||
/// * `#rgba`
|
||||
/// * `#rrggbb`
|
||||
/// * `#rrggbbaa`
|
||||
pub color: String,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct DecayArgs {
|
||||
/// The interval of inactivity after which to disable the screens.
|
||||
///
|
||||
/// Minutes, seconds, and milliseconds can be specified in any of the following formats:
|
||||
///
|
||||
/// * 1m
|
||||
/// * 1m5s
|
||||
/// * 1m 5s
|
||||
/// * 1min 5sec
|
||||
/// * 1 minute 5 seconds.
|
||||
pub duration: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn main(global: GlobalArgs, damage_tracking_args: DamageTrackingArgs) {
|
||||
with_tool_client(global.log_level.into(), |tc| async move {
|
||||
let damage_tracking = Rc::new(DamageTracking { tc: tc.clone() });
|
||||
damage_tracking.run(damage_tracking_args).await;
|
||||
});
|
||||
}
|
||||
|
||||
struct DamageTracking {
|
||||
tc: Rc<ToolClient>,
|
||||
}
|
||||
|
||||
impl DamageTracking {
|
||||
async fn run(&self, args: DamageTrackingArgs) {
|
||||
let tc = &self.tc;
|
||||
let Some(dt) = tc.jay_damage_tracking().await else {
|
||||
fatal!("Compositor does not support damage tracking");
|
||||
};
|
||||
match args.command {
|
||||
DamageTrackingCmd::Show => {
|
||||
tc.send(SetVisualizerEnabled {
|
||||
self_id: dt,
|
||||
enabled: 1,
|
||||
});
|
||||
}
|
||||
DamageTrackingCmd::Hide => {
|
||||
tc.send(SetVisualizerEnabled {
|
||||
self_id: dt,
|
||||
enabled: 0,
|
||||
});
|
||||
}
|
||||
DamageTrackingCmd::SetColor(c) => {
|
||||
let color = parse_color(&c.color);
|
||||
tc.send(SetVisualizerColor {
|
||||
self_id: dt,
|
||||
r: color.r,
|
||||
g: color.g,
|
||||
b: color.b,
|
||||
a: color.a,
|
||||
});
|
||||
}
|
||||
DamageTrackingCmd::SetDecay(c) => {
|
||||
let duration = parse_duration(&c.duration);
|
||||
tc.send(SetVisualizerDecay {
|
||||
self_id: dt,
|
||||
millis: duration.as_millis() as _,
|
||||
});
|
||||
}
|
||||
}
|
||||
tc.round_trip().await;
|
||||
}
|
||||
}
|
||||
102
src/cli/duration.rs
Normal file
102
src/cli/duration.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
use {
|
||||
crate::utils::errorfmt::ErrorFmt,
|
||||
std::{collections::VecDeque, str::FromStr, time::Duration},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Component {
|
||||
Number(u64),
|
||||
Minutes(String),
|
||||
Seconds(String),
|
||||
Milliseconds(String),
|
||||
}
|
||||
|
||||
pub fn parse_duration(args: &[String]) -> Duration {
|
||||
let comp = parse_components(args);
|
||||
let mut minutes = None;
|
||||
let mut seconds = None;
|
||||
let mut milliseconds = None;
|
||||
let mut pending_num = None;
|
||||
for comp in comp {
|
||||
match comp {
|
||||
Component::Number(_) if pending_num.is_some() => {
|
||||
fatal!("missing number unit after {}", pending_num.unwrap())
|
||||
}
|
||||
Component::Number(n) => pending_num = Some(n),
|
||||
|
||||
Component::Minutes(n) if pending_num.is_none() => {
|
||||
fatal!("`{}` must be preceded by a number", n)
|
||||
}
|
||||
Component::Minutes(_) if minutes.is_some() => {
|
||||
fatal!("minutes specified multiple times")
|
||||
}
|
||||
Component::Minutes(_) => minutes = pending_num.take(),
|
||||
|
||||
Component::Seconds(n) if pending_num.is_none() => {
|
||||
fatal!("`{}` must be preceded by a number", n)
|
||||
}
|
||||
Component::Seconds(_) if seconds.is_some() => {
|
||||
fatal!("seconds specified multiple times")
|
||||
}
|
||||
Component::Seconds(_) => seconds = pending_num.take(),
|
||||
Component::Milliseconds(n) if pending_num.is_none() => {
|
||||
fatal!("`{}` must be preceded by a number", n)
|
||||
}
|
||||
Component::Milliseconds(_) if milliseconds.is_some() => {
|
||||
fatal!("milliseconds specified multiple times")
|
||||
}
|
||||
Component::Milliseconds(_) => milliseconds = pending_num.take(),
|
||||
}
|
||||
}
|
||||
if pending_num.is_some() {
|
||||
fatal!("missing number unit after {}", pending_num.unwrap());
|
||||
}
|
||||
if minutes.is_none() && seconds.is_none() && milliseconds.is_none() {
|
||||
fatal!("duration must be specified");
|
||||
}
|
||||
let mut ms = minutes.unwrap_or(0) as u128 * 60 * 1000
|
||||
+ seconds.unwrap_or(0) as u128 * 1000
|
||||
+ milliseconds.unwrap_or(0) as u128;
|
||||
if ms > u64::MAX as u128 {
|
||||
ms = u64::MAX as u128;
|
||||
}
|
||||
Duration::from_millis(ms as u64)
|
||||
}
|
||||
|
||||
fn parse_components(args: &[String]) -> Vec<Component> {
|
||||
let mut args = VecDeque::from_iter(args.iter().map(|s| s.to_ascii_lowercase()));
|
||||
let mut res = vec![];
|
||||
while let Some(arg) = args.pop_front() {
|
||||
if arg.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut arg = &arg[..];
|
||||
if is_num(arg.as_bytes()[0]) {
|
||||
if let Some(pos) = arg.as_bytes().iter().position(|&a| !is_num(a)) {
|
||||
args.push_front(arg[pos..].to_string());
|
||||
arg = &arg[..pos];
|
||||
}
|
||||
match u64::from_str(arg) {
|
||||
Ok(n) => res.push(Component::Number(n)),
|
||||
Err(e) => fatal!("Could not parse `{}` as a number: {}", arg, ErrorFmt(e)),
|
||||
}
|
||||
} else {
|
||||
if let Some(pos) = arg.as_bytes().iter().position(|&a| is_num(a)) {
|
||||
args.push_front(arg[pos..].to_string());
|
||||
arg = &arg[..pos];
|
||||
}
|
||||
let comp = match arg {
|
||||
"minutes" | "minute" | "min" | "m" => Component::Minutes(arg.to_string()),
|
||||
"seconds" | "second" | "sec" | "s" => Component::Seconds(arg.to_string()),
|
||||
"milliseconds" | "millisecond" | "ms" => Component::Milliseconds(arg.to_string()),
|
||||
_ => fatal!("Could not parse `{}`", arg),
|
||||
};
|
||||
res.push(comp);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn is_num(b: u8) -> bool {
|
||||
matches!(b, b'0'..=b'9')
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{GlobalArgs, IdleArgs, IdleCmd, IdleSetArgs},
|
||||
cli::{duration::parse_duration, GlobalArgs, IdleArgs, IdleCmd, IdleSetArgs},
|
||||
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
||||
utils::{errorfmt::ErrorFmt, stack::Stack},
|
||||
utils::stack::Stack,
|
||||
wire::{jay_compositor, jay_idle, JayIdleId, WlSurfaceId},
|
||||
},
|
||||
std::{cell::Cell, collections::VecDeque, rc::Rc, str::FromStr},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
};
|
||||
|
||||
pub fn main(global: GlobalArgs, args: IdleArgs) {
|
||||
|
|
@ -93,46 +93,11 @@ impl Idle {
|
|||
|
||||
async fn set(self, idle: JayIdleId, args: IdleSetArgs) {
|
||||
let tc = &self.tc;
|
||||
let interval;
|
||||
if args.interval.len() == 1 && args.interval[0] == "disabled" {
|
||||
interval = 0;
|
||||
let interval = if args.interval.len() == 1 && args.interval[0] == "disabled" {
|
||||
0
|
||||
} else {
|
||||
let comp = parse_components(&args.interval);
|
||||
let mut minutes = None;
|
||||
let mut seconds = None;
|
||||
let mut pending_num = None;
|
||||
for comp in comp {
|
||||
match comp {
|
||||
Component::Number(_) if pending_num.is_some() => {
|
||||
fatal!("missing number unit after {}", pending_num.unwrap())
|
||||
}
|
||||
Component::Number(n) => pending_num = Some(n),
|
||||
|
||||
Component::Minutes(n) if pending_num.is_none() => {
|
||||
fatal!("`{}` must be preceded by a number", n)
|
||||
}
|
||||
Component::Minutes(_) if minutes.is_some() => {
|
||||
fatal!("minutes specified multiple times")
|
||||
}
|
||||
Component::Minutes(_) => minutes = pending_num.take(),
|
||||
|
||||
Component::Seconds(n) if pending_num.is_none() => {
|
||||
fatal!("`{}` must be preceded by a number", n)
|
||||
}
|
||||
Component::Seconds(_) if seconds.is_some() => {
|
||||
fatal!("seconds specified multiple times")
|
||||
}
|
||||
Component::Seconds(_) => seconds = pending_num.take(),
|
||||
}
|
||||
}
|
||||
if pending_num.is_some() {
|
||||
fatal!("missing number unit after {}", pending_num.unwrap());
|
||||
}
|
||||
if minutes.is_none() && seconds.is_none() {
|
||||
fatal!("minutes and/or numbers must be specified");
|
||||
}
|
||||
interval = minutes.unwrap_or(0) * 60 + seconds.unwrap_or(0);
|
||||
}
|
||||
parse_duration(&args.interval).as_secs() as u64
|
||||
};
|
||||
tc.send(jay_idle::SetInterval {
|
||||
self_id: idle,
|
||||
interval,
|
||||
|
|
@ -140,47 +105,3 @@ impl Idle {
|
|||
tc.round_trip().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Component {
|
||||
Number(u64),
|
||||
Minutes(String),
|
||||
Seconds(String),
|
||||
}
|
||||
|
||||
fn parse_components(args: &[String]) -> Vec<Component> {
|
||||
let mut args = VecDeque::from_iter(args.iter().map(|s| s.to_ascii_lowercase()));
|
||||
let mut res = vec![];
|
||||
while let Some(arg) = args.pop_front() {
|
||||
if arg.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut arg = &arg[..];
|
||||
if is_num(arg.as_bytes()[0]) {
|
||||
if let Some(pos) = arg.as_bytes().iter().position(|&a| !is_num(a)) {
|
||||
args.push_front(arg[pos..].to_string());
|
||||
arg = &arg[..pos];
|
||||
}
|
||||
match u64::from_str(arg) {
|
||||
Ok(n) => res.push(Component::Number(n)),
|
||||
Err(e) => fatal!("Could not parse `{}` as a number: {}", arg, ErrorFmt(e)),
|
||||
}
|
||||
} else {
|
||||
if let Some(pos) = arg.as_bytes().iter().position(|&a| is_num(a)) {
|
||||
args.push_front(arg[pos..].to_string());
|
||||
arg = &arg[..pos];
|
||||
}
|
||||
let comp = match arg {
|
||||
"minutes" | "minute" | "min" | "m" => Component::Minutes(arg.to_string()),
|
||||
"seconds" | "second" | "sec" | "s" => Component::Seconds(arg.to_string()),
|
||||
_ => fatal!("Could not parse `{}`", arg),
|
||||
};
|
||||
res.push(comp);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn is_num(b: u8) -> bool {
|
||||
matches!(b, b'0'..=b'9')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use {
|
|||
async_engine::Phase,
|
||||
client::{Client, ClientError},
|
||||
object::ObjectId,
|
||||
time::Time,
|
||||
utils::{
|
||||
buffd::{BufFdIn, BufFdOut, MsgParser},
|
||||
errorfmt::ErrorFmt,
|
||||
|
|
@ -11,7 +10,7 @@ use {
|
|||
},
|
||||
},
|
||||
futures_util::{select, FutureExt},
|
||||
std::{collections::VecDeque, mem, rc::Rc},
|
||||
std::{collections::VecDeque, mem, rc::Rc, time::Duration},
|
||||
};
|
||||
|
||||
pub async fn client(data: Rc<Client>) {
|
||||
|
|
@ -112,7 +111,7 @@ async fn send(data: Rc<Client>) {
|
|||
swapchain.commit();
|
||||
mem::swap(&mut swapchain.pending, &mut buffers);
|
||||
}
|
||||
let timeout = Time::in_ms(5000).unwrap();
|
||||
let timeout = data.state.now() + Duration::from_millis(5000);
|
||||
while let Some(mut cur) = buffers.pop_front() {
|
||||
out.flush(&mut cur, timeout).await?;
|
||||
data.swapchain.borrow_mut().free.push(cur);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use {
|
|||
client::{ClientId, Clients},
|
||||
clientmem::{self, ClientMemError},
|
||||
config::ConfigProxy,
|
||||
damage::{visualize_damage, DamageVisualizer},
|
||||
dbus::Dbus,
|
||||
forker,
|
||||
globals::Globals,
|
||||
|
|
@ -244,6 +245,7 @@ fn start_compositor2(
|
|||
tablet_ids: Default::default(),
|
||||
tablet_tool_ids: Default::default(),
|
||||
tablet_pad_ids: Default::default(),
|
||||
damage_visualizer: DamageVisualizer::new(&engine),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -343,6 +345,7 @@ fn start_global_event_handlers(
|
|||
eng.spawn2(Phase::PostLayout, input_popup_positioning(state.clone())),
|
||||
eng.spawn2(Phase::Present, perform_toplevel_screencasts(state.clone())),
|
||||
eng.spawn2(Phase::PostLayout, perform_screencast_realloc(state.clone())),
|
||||
eng.spawn2(Phase::PostLayout, visualize_damage(state.clone())),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -465,9 +468,11 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
screencasts: Default::default(),
|
||||
hardware_cursor_needs_render: Cell::new(false),
|
||||
screencopies: Default::default(),
|
||||
title_visible: Cell::new(false),
|
||||
});
|
||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||
id: state.node_ids.next(),
|
||||
state: state.clone(),
|
||||
is_dummy: true,
|
||||
output: CloneCell::new(dummy_output.clone()),
|
||||
position: Default::default(),
|
||||
|
|
|
|||
|
|
@ -720,8 +720,6 @@ impl ConfigProxyHandler {
|
|||
if let Some(ws) = self.state.workspaces.get(name.as_str()) {
|
||||
ws.may_capture.set(capture);
|
||||
ws.update_has_captures();
|
||||
ws.output.get().schedule_update_render_data();
|
||||
self.state.damage();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -863,7 +861,6 @@ impl ConfigProxyHandler {
|
|||
move_ws_to_output(&link, &output, config);
|
||||
ws.desired_output.set(output.global.output_id.clone());
|
||||
self.state.tree_changed();
|
||||
self.state.damage();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1032,7 +1029,6 @@ impl ConfigProxyHandler {
|
|||
let scale = Scale::from_f64(scale);
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.set_preferred_scale(scale);
|
||||
self.state.damage();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1043,7 +1039,6 @@ impl ConfigProxyHandler {
|
|||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.update_transform(transform);
|
||||
self.state.damage();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1371,6 +1366,7 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
}
|
||||
self.state.root.clone().node_visit(&mut V);
|
||||
self.state.damage(self.state.root.extents.get());
|
||||
}
|
||||
|
||||
fn colors_changed(&self) {
|
||||
|
|
@ -1386,6 +1382,7 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
}
|
||||
self.state.root.clone().node_visit(&mut V);
|
||||
self.state.damage(self.state.root.extents.get());
|
||||
}
|
||||
|
||||
fn get_sized(&self, sized: Resizable) -> Result<ThemeSized, CphError> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
fixed::Fixed,
|
||||
format::ARGB8888,
|
||||
gfx_api::{AcquireSync, GfxContext, GfxError, GfxTexture, ReleaseSync},
|
||||
|
|
@ -283,13 +284,14 @@ impl ServerCursorTemplate {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn instantiate(&self, size: u32) -> Rc<dyn Cursor> {
|
||||
pub fn instantiate(&self, state: &State, size: u32) -> Rc<dyn Cursor> {
|
||||
match &self.var {
|
||||
ServerCursorTemplateVariant::Static(s) => Rc::new(StaticCursor {
|
||||
image: s.for_size(size),
|
||||
}),
|
||||
ServerCursorTemplateVariant::Animated(a) => Rc::new(AnimatedCursor {
|
||||
start: Time::now_unchecked(),
|
||||
start: state.now(),
|
||||
eng: state.eng.clone(),
|
||||
next: NumCell::new(a[0].delay_ns),
|
||||
idx: Cell::new(0),
|
||||
images: a.iter().map(|c| c.for_size(size)).collect(),
|
||||
|
|
@ -424,6 +426,7 @@ impl Cursor for StaticCursor {
|
|||
|
||||
struct AnimatedCursor {
|
||||
start: Time,
|
||||
eng: Rc<AsyncEngine>,
|
||||
next: NumCell<u64>,
|
||||
idx: Cell<usize>,
|
||||
images: Vec<InstantiatedCursorImage>,
|
||||
|
|
@ -463,7 +466,7 @@ impl Cursor for AnimatedCursor {
|
|||
}
|
||||
|
||||
fn tick(&self) {
|
||||
let dist = Time::now_unchecked() - self.start;
|
||||
let dist = self.eng.now() - self.start;
|
||||
if (dist.as_nanos() as u64) < self.next.get() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -478,7 +481,7 @@ impl Cursor for AnimatedCursor {
|
|||
}
|
||||
|
||||
fn time_until_tick(&self) -> Duration {
|
||||
let dist = Time::now_unchecked() - self.start;
|
||||
let dist = self.eng.now() - self.start;
|
||||
let dist = dist.as_nanos() as u64;
|
||||
let nanos = self.next.get().saturating_sub(dist);
|
||||
Duration::from_nanos(nanos)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use {
|
|||
cursor::{Cursor, KnownCursor, DEFAULT_CURSOR_SIZE},
|
||||
fixed::Fixed,
|
||||
rect::Rect,
|
||||
scale::Scale,
|
||||
state::State,
|
||||
tree::OutputNode,
|
||||
utils::{
|
||||
|
|
@ -74,13 +75,26 @@ impl CursorUserGroup {
|
|||
group
|
||||
}
|
||||
|
||||
fn damage_active(&self) {
|
||||
if let Some(active) = self.active.get() {
|
||||
if let Some(cursor) = active.cursor.get() {
|
||||
let (x, y) = active.pos.get();
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
let extents = cursor.extents_at_scale(Scale::default());
|
||||
self.state.damage(extents.move_(x_int, y_int));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deactivate(&self) {
|
||||
if self.hardware_cursor.get() {
|
||||
self.remove_hardware_cursor();
|
||||
} else {
|
||||
self.damage_active();
|
||||
}
|
||||
self.active_id.take();
|
||||
self.active.take();
|
||||
self.state.damage();
|
||||
}
|
||||
|
||||
pub fn latest_output(&self) -> Rc<OutputNode> {
|
||||
|
|
@ -150,6 +164,7 @@ impl CursorUserGroup {
|
|||
if self.hardware_cursor.replace(hardware_cursor) == hardware_cursor {
|
||||
return;
|
||||
}
|
||||
self.damage_active();
|
||||
if hardware_cursor {
|
||||
let prev = self
|
||||
.state
|
||||
|
|
@ -157,6 +172,7 @@ impl CursorUserGroup {
|
|||
.set(Some(self.clone()));
|
||||
if let Some(prev) = prev {
|
||||
prev.hardware_cursor.set(false);
|
||||
prev.damage_active();
|
||||
}
|
||||
match self.active.get() {
|
||||
None => self.remove_hardware_cursor(),
|
||||
|
|
@ -230,9 +246,7 @@ impl CursorUser {
|
|||
self.owner.take();
|
||||
self.group.users.remove(&self.id);
|
||||
if self.group.active_id.get() == Some(self.id) {
|
||||
self.group.active_id.take();
|
||||
self.group.active.take();
|
||||
self.group.state.damage();
|
||||
self.group.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -240,10 +254,15 @@ impl CursorUser {
|
|||
if self.group.active_id.replace(Some(self.id)) == Some(self.id) {
|
||||
return;
|
||||
}
|
||||
if self.software_cursor() {
|
||||
self.group.damage_active();
|
||||
}
|
||||
self.group.latest_output.set(self.output.get());
|
||||
self.group.active.set(Some(self.clone()));
|
||||
self.update_hardware_cursor();
|
||||
self.group.state.damage();
|
||||
if self.software_cursor() {
|
||||
self.group.damage_active();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), allow(dead_code))]
|
||||
|
|
@ -296,7 +315,9 @@ impl CursorUser {
|
|||
KnownCursor::ZoomIn => &cursors.zoom_in,
|
||||
KnownCursor::ZoomOut => &cursors.zoom_out,
|
||||
};
|
||||
self.set_cursor2(Some(tpl.instantiate(self.group.size.get())));
|
||||
self.set_cursor2(Some(
|
||||
tpl.instantiate(&self.group.state, self.group.size.get()),
|
||||
));
|
||||
}
|
||||
|
||||
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||
|
|
@ -339,6 +360,9 @@ impl CursorUser {
|
|||
}
|
||||
}
|
||||
old.handle_unset();
|
||||
if self.software_cursor() {
|
||||
self.group.damage_active();
|
||||
}
|
||||
}
|
||||
if let Some(cursor) = cursor.as_ref() {
|
||||
cursor.clone().handle_set();
|
||||
|
|
@ -346,12 +370,20 @@ impl CursorUser {
|
|||
}
|
||||
self.cursor.set(cursor.clone());
|
||||
self.update_hardware_cursor();
|
||||
if self.software_cursor() {
|
||||
self.group.damage_active();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position(&self) -> (Fixed, Fixed) {
|
||||
self.pos.get()
|
||||
}
|
||||
|
||||
pub fn position_int(&self) -> (i32, i32) {
|
||||
let (x, y) = self.pos.get();
|
||||
(x.round_down(), y.round_down())
|
||||
}
|
||||
|
||||
pub fn set_position(&self, mut x: Fixed, mut y: Fixed) -> (Fixed, Fixed) {
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
|
|
@ -361,6 +393,16 @@ impl CursorUser {
|
|||
x = x.apply_fract(x_tmp);
|
||||
y = y.apply_fract(y_tmp);
|
||||
}
|
||||
if self.software_cursor() {
|
||||
if let Some(cursor) = self.cursor.get() {
|
||||
let (old_x, old_y) = self.pos.get();
|
||||
let old_x_int = old_x.round_down();
|
||||
let old_y_int = old_y.round_down();
|
||||
let extents = cursor.extents_at_scale(Scale::default());
|
||||
self.group.state.damage(extents.move_(old_x_int, old_y_int));
|
||||
self.group.state.damage(extents.move_(x_int, y_int));
|
||||
}
|
||||
}
|
||||
self.pos.set((x, y));
|
||||
self.update_hardware_cursor_(false);
|
||||
(x, y)
|
||||
|
|
@ -374,6 +416,10 @@ impl CursorUser {
|
|||
self.is_active() && self.group.hardware_cursor.get()
|
||||
}
|
||||
|
||||
pub fn software_cursor(&self) -> bool {
|
||||
self.is_active() && !self.group.hardware_cursor.get()
|
||||
}
|
||||
|
||||
fn update_hardware_cursor_(&self, render: bool) {
|
||||
if !self.hardware_cursor() {
|
||||
return;
|
||||
|
|
|
|||
160
src/damage.rs
Normal file
160
src/damage.rs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
rect::{Rect, Region},
|
||||
renderer::renderer_base::RendererBase,
|
||||
state::State,
|
||||
theme::Color,
|
||||
time::Time,
|
||||
utils::{asyncevent::AsyncEvent, errorfmt::ErrorFmt, timer::TimerFd},
|
||||
},
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::VecDeque,
|
||||
rc::Rc,
|
||||
time::Duration,
|
||||
},
|
||||
uapi::c::CLOCK_MONOTONIC,
|
||||
};
|
||||
|
||||
pub async fn visualize_damage(state: Rc<State>) {
|
||||
let timer = match TimerFd::new(CLOCK_MONOTONIC) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
log::error!("Could not create timer fd: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
loop {
|
||||
state.damage_visualizer.entry_added.triggered().await;
|
||||
let duration = Duration::from_millis(50);
|
||||
let res = timer.program(Some(duration), Some(duration));
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not program timer: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
loop {
|
||||
let res = timer.expired(&state.ring).await;
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not wait for timer to expire: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
if state.damage_visualizer.entries.borrow_mut().is_empty() {
|
||||
break;
|
||||
}
|
||||
damage_all(&state);
|
||||
}
|
||||
let res = timer.program(None, None);
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not disable timer: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn damage_all(state: &State) {
|
||||
for connector in state.connectors.lock().values() {
|
||||
if connector.connected.get() {
|
||||
connector.connector.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DamageVisualizer {
|
||||
eng: Rc<AsyncEngine>,
|
||||
entries: RefCell<VecDeque<Damage>>,
|
||||
entry_added: AsyncEvent,
|
||||
enabled: Cell<bool>,
|
||||
decay: Cell<Duration>,
|
||||
color: Cell<Color>,
|
||||
}
|
||||
|
||||
const MAX_RECTS: usize = 100_000;
|
||||
|
||||
struct Damage {
|
||||
time: Time,
|
||||
rect: Rect,
|
||||
}
|
||||
|
||||
impl DamageVisualizer {
|
||||
pub fn new(eng: &Rc<AsyncEngine>) -> Self {
|
||||
Self {
|
||||
eng: eng.clone(),
|
||||
entries: Default::default(),
|
||||
entry_added: Default::default(),
|
||||
enabled: Default::default(),
|
||||
decay: Cell::new(Duration::from_secs(2)),
|
||||
color: Cell::new(Color::from_rgba_straight(255, 0, 0, 128)),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn add(&self, rect: Rect) {
|
||||
if !self.enabled.get() {
|
||||
return;
|
||||
}
|
||||
let entries = &mut *self.entries.borrow_mut();
|
||||
if entries.is_empty() {
|
||||
self.entry_added.trigger();
|
||||
}
|
||||
entries.push_back(Damage {
|
||||
time: self.eng.now(),
|
||||
rect,
|
||||
});
|
||||
if entries.len() > MAX_RECTS {
|
||||
entries.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, state: &State, enabled: bool) {
|
||||
self.enabled.set(enabled);
|
||||
if !enabled {
|
||||
self.entries.borrow_mut().clear();
|
||||
damage_all(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_decay(&self, decay: Duration) {
|
||||
let millis = decay.as_millis();
|
||||
if millis == 0 || millis > u64::MAX as u128 {
|
||||
return;
|
||||
}
|
||||
self.decay.set(decay);
|
||||
}
|
||||
|
||||
pub fn set_color(&self, color: Color) {
|
||||
self.color.set(color);
|
||||
}
|
||||
|
||||
pub fn render(&self, cursor_rect: &Rect, renderer: &mut RendererBase<'_>) {
|
||||
if !self.enabled.get() {
|
||||
return;
|
||||
}
|
||||
let now = self.eng.now();
|
||||
let entries = &mut *self.entries.borrow_mut();
|
||||
let decay = self.decay.get();
|
||||
while let Some(first) = entries.front() {
|
||||
if now - first.time >= decay {
|
||||
entries.pop_front();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let base_color = self.color.get();
|
||||
let mut used = Region::empty();
|
||||
let dx = -cursor_rect.x1();
|
||||
let dy = -cursor_rect.y1();
|
||||
let decay_millis = decay.as_millis() as u64 as f32;
|
||||
for entry in entries.iter().rev() {
|
||||
let region = Region::new(entry.rect);
|
||||
let region = region.subtract(&used);
|
||||
if region.is_not_empty() {
|
||||
let age = (now - entry.time).as_millis() as u64 as f32 / decay_millis;
|
||||
let color = base_color * (1.0 - age);
|
||||
renderer.fill_boxes2(region.rects(), &color, dx, dy);
|
||||
used = used.union(®ion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
cursor::Cursor,
|
||||
damage::DamageVisualizer,
|
||||
fixed::Fixed,
|
||||
format::Format,
|
||||
rect::Rect,
|
||||
|
|
@ -72,13 +73,43 @@ impl SampleRect {
|
|||
let y2 = self.y2;
|
||||
match self.buffer_transform {
|
||||
None => [[x2, y1], [x1, y1], [x2, y2], [x1, y2]],
|
||||
Rotate90 => [[y1, x1], [y1, x2], [y2, x1], [y2, x2]],
|
||||
Rotate180 => [[x1, y2], [x2, y2], [x1, y1], [x2, y1]],
|
||||
Rotate270 => [[y2, x2], [y2, x1], [y1, x2], [y1, x1]],
|
||||
Flip => [[x1, y1], [x2, y1], [x1, y2], [x2, y2]],
|
||||
Rotate90 => [
|
||||
[y1, 1.0 - x2],
|
||||
[y1, 1.0 - x1],
|
||||
[y2, 1.0 - x2],
|
||||
[y2, 1.0 - x1],
|
||||
],
|
||||
Rotate180 => [
|
||||
[1.0 - x2, 1.0 - y1],
|
||||
[1.0 - x1, 1.0 - y1],
|
||||
[1.0 - x2, 1.0 - y2],
|
||||
[1.0 - x1, 1.0 - y2],
|
||||
],
|
||||
Rotate270 => [
|
||||
[1.0 - y1, x2],
|
||||
[1.0 - y1, x1],
|
||||
[1.0 - y2, x2],
|
||||
[1.0 - y2, x1],
|
||||
],
|
||||
Flip => [
|
||||
[1.0 - x2, y1],
|
||||
[1.0 - x1, y1],
|
||||
[1.0 - x2, y2],
|
||||
[1.0 - x1, y2],
|
||||
],
|
||||
FlipRotate90 => [[y1, x2], [y1, x1], [y2, x2], [y2, x1]],
|
||||
FlipRotate180 => [[x2, y2], [x1, y2], [x2, y1], [x1, y1]],
|
||||
FlipRotate270 => [[y2, x1], [y2, x2], [y1, x1], [y1, x2]],
|
||||
FlipRotate180 => [
|
||||
[x2, 1.0 - y1],
|
||||
[x1, 1.0 - y1],
|
||||
[x2, 1.0 - y2],
|
||||
[x1, 1.0 - y2],
|
||||
],
|
||||
FlipRotate270 => [
|
||||
[1.0 - y1, 1.0 - x2],
|
||||
[1.0 - y1, 1.0 - x1],
|
||||
[1.0 - y2, 1.0 - x2],
|
||||
[1.0 - y2, 1.0 - x1],
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -329,6 +360,7 @@ impl dyn GfxFramebuffer {
|
|||
render_hardware_cursor: bool,
|
||||
black_background: bool,
|
||||
transform: Transform,
|
||||
visualizer: Option<&DamageVisualizer>,
|
||||
) -> GfxRenderPass {
|
||||
let mut ops = self.take_render_ops();
|
||||
let mut renderer = Renderer {
|
||||
|
|
@ -345,7 +377,7 @@ impl dyn GfxFramebuffer {
|
|||
if let Some(rect) = cursor_rect {
|
||||
let seats = state.globals.lock_seats();
|
||||
for seat in seats.values() {
|
||||
let (x, y) = seat.pointer_cursor().position();
|
||||
let (x, y) = seat.pointer_cursor().position_int();
|
||||
if let Some(im) = seat.input_method() {
|
||||
for (_, popup) in &im.popups {
|
||||
if popup.surface.node_visible() {
|
||||
|
|
@ -359,25 +391,10 @@ impl dyn GfxFramebuffer {
|
|||
}
|
||||
}
|
||||
if let Some(drag) = seat.toplevel_drag() {
|
||||
if let Some(tl) = drag.toplevel.get() {
|
||||
if tl.xdg.surface.buffer.get().is_some() {
|
||||
let (x, y) = rect.translate(
|
||||
x.round_down() - drag.x_off.get(),
|
||||
y.round_down() - drag.y_off.get(),
|
||||
);
|
||||
renderer.render_xdg_surface(&tl.xdg, x, y, None)
|
||||
}
|
||||
}
|
||||
drag.render(&mut renderer, &rect, x, y);
|
||||
}
|
||||
if let Some(dnd_icon) = seat.dnd_icon() {
|
||||
let extents = dnd_icon.extents.get().move_(
|
||||
x.round_down() + dnd_icon.buf_x.get(),
|
||||
y.round_down() + dnd_icon.buf_y.get(),
|
||||
);
|
||||
if extents.intersects(&rect) {
|
||||
let (x, y) = rect.translate(extents.x1(), extents.y1());
|
||||
renderer.render_surface(&dnd_icon, x, y, None);
|
||||
}
|
||||
dnd_icon.render(&mut renderer, &rect, x, y);
|
||||
}
|
||||
if render_cursor {
|
||||
let cursor_user_group = seat.cursor_group();
|
||||
|
|
@ -395,6 +412,11 @@ impl dyn GfxFramebuffer {
|
|||
}
|
||||
}
|
||||
}
|
||||
if let Some(visualizer) = visualizer {
|
||||
if let Some(cursor_rect) = cursor_rect {
|
||||
visualizer.render(&cursor_rect, &mut renderer.base);
|
||||
}
|
||||
}
|
||||
let c = match black_background {
|
||||
true => Color::SOLID_BLACK,
|
||||
false => state.theme.colors.background.get(),
|
||||
|
|
@ -453,6 +475,7 @@ impl dyn GfxFramebuffer {
|
|||
render_hardware_cursor,
|
||||
black_background,
|
||||
transform,
|
||||
None,
|
||||
);
|
||||
self.perform_render_pass(pass)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use {
|
|||
zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global,
|
||||
},
|
||||
jay_compositor::JayCompositorGlobal,
|
||||
jay_damage_tracking::JayDamageTrackingGlobal,
|
||||
org_kde_kwin_server_decoration_manager::OrgKdeKwinServerDecorationManagerGlobal,
|
||||
wl_compositor::WlCompositorGlobal,
|
||||
wl_output::WlOutputGlobal,
|
||||
|
|
@ -195,6 +196,7 @@ impl Globals {
|
|||
add_singleton!(ExtTransientSeatManagerV1Global);
|
||||
add_singleton!(ZwpPointerGesturesV1Global);
|
||||
add_singleton!(ZwpTabletManagerV2Global);
|
||||
add_singleton!(JayDamageTrackingGlobal);
|
||||
}
|
||||
|
||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ pub mod ext_session_lock_manager_v1;
|
|||
pub mod ext_session_lock_v1;
|
||||
pub mod ipc;
|
||||
pub mod jay_compositor;
|
||||
pub mod jay_damage_tracking;
|
||||
pub mod jay_idle;
|
||||
pub mod jay_input;
|
||||
pub mod jay_log_file;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use {
|
|||
ifs::ext_idle_notification_v1::ExtIdleNotificationV1,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
time::now_usec,
|
||||
utils::errorfmt::ErrorFmt,
|
||||
wire::{ext_idle_notifier_v1::*, ExtIdleNotifierV1Id},
|
||||
},
|
||||
|
|
@ -81,7 +80,7 @@ impl ExtIdleNotifierV1RequestHandler for ExtIdleNotifierV1 {
|
|||
|
||||
async fn run(n: Rc<ExtIdleNotificationV1>) {
|
||||
loop {
|
||||
let now = now_usec();
|
||||
let now = n.client.state.now_usec();
|
||||
let elapsed = now.saturating_sub(n.seat.last_input());
|
||||
if elapsed < n.duration_usec {
|
||||
let res = n
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ impl ExtSessionLockManagerV1RequestHandler for ExtSessionLockManagerV1 {
|
|||
state.lock.locked.set(true);
|
||||
state.lock.lock.set(Some(new.clone()));
|
||||
state.tree_changed();
|
||||
state.damage();
|
||||
state.damage(state.root.extents.get());
|
||||
new.send_locked();
|
||||
} else {
|
||||
new.finish();
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ impl ExtSessionLockV1RequestHandler for ExtSessionLockV1 {
|
|||
node.set_lock_surface(Some(new.clone()));
|
||||
let pos = node.global.pos.get();
|
||||
new.change_extents(pos);
|
||||
new.surface.set_output(&node);
|
||||
self.client.state.tree_changed();
|
||||
}
|
||||
}
|
||||
|
|
@ -87,16 +88,7 @@ impl ExtSessionLockV1RequestHandler for ExtSessionLockV1 {
|
|||
return Err(ExtSessionLockV1Error::NeverLocked);
|
||||
}
|
||||
if !self.finished.get() {
|
||||
let state = &self.client.state;
|
||||
state.lock.locked.set(false);
|
||||
state.lock.lock.take();
|
||||
for output in state.root.outputs.lock().values() {
|
||||
if let Some(surface) = output.set_lock_surface(None) {
|
||||
surface.destroy_node();
|
||||
}
|
||||
}
|
||||
state.tree_changed();
|
||||
state.damage();
|
||||
self.client.state.do_unlock();
|
||||
}
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use {
|
|||
IterableIpcVtable, OfferData, Role,
|
||||
},
|
||||
wl_seat::{WlSeatError, WlSeatGlobal},
|
||||
wl_surface::{SurfaceRole, WlSurfaceError},
|
||||
wl_surface::WlSurfaceError,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
|
|
@ -115,8 +115,7 @@ impl WlDataDeviceRequestHandler for WlDataDevice {
|
|||
};
|
||||
let icon = if req.icon.is_some() {
|
||||
let icon = self.client.lookup(req.icon)?;
|
||||
icon.set_role(SurfaceRole::DndIcon)?;
|
||||
Some(icon)
|
||||
Some(icon.into_dnd_icon(&self.seat)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
|||
|
|
@ -216,19 +216,12 @@ impl JayCompositorRequestHandler for JayCompositor {
|
|||
|
||||
fn unlock(&self, _req: Unlock, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let state = &self.client.state;
|
||||
if state.lock.locked.replace(false) {
|
||||
if let Some(lock) = state.lock.lock.take() {
|
||||
if state.lock.locked.get() {
|
||||
if let Some(lock) = state.lock.lock.get() {
|
||||
lock.finish();
|
||||
}
|
||||
for output in state.root.outputs.lock().values() {
|
||||
if let Some(surface) = output.set_lock_surface(None) {
|
||||
surface.destroy_node();
|
||||
}
|
||||
}
|
||||
state.tree_changed();
|
||||
state.damage();
|
||||
state.do_unlock();
|
||||
}
|
||||
self.client.symmetric_delete.set(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
135
src/ifs/jay_damage_tracking.rs
Normal file
135
src/ifs/jay_damage_tracking.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientCaps, ClientError, CAP_JAY_COMPOSITOR},
|
||||
globals::{Global, GlobalName},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
theme::Color,
|
||||
wire::{
|
||||
jay_damage_tracking::{
|
||||
Destroy, JayDamageTrackingRequestHandler, SetVisualizerColor, SetVisualizerDecay,
|
||||
SetVisualizerEnabled,
|
||||
},
|
||||
JayCompositorId,
|
||||
},
|
||||
},
|
||||
std::{rc::Rc, time::Duration},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct JayDamageTrackingGlobal {
|
||||
name: GlobalName,
|
||||
}
|
||||
|
||||
impl JayDamageTrackingGlobal {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: JayCompositorId,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), JayDamageTrackingError> {
|
||||
let obj = Rc::new(JayDamageTracking {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
JayDamageTrackingGlobal,
|
||||
JayDamageTracking,
|
||||
JayDamageTrackingError
|
||||
);
|
||||
|
||||
impl Global for JayDamageTrackingGlobal {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_JAY_COMPOSITOR
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(JayDamageTrackingGlobal);
|
||||
|
||||
pub struct JayDamageTracking {
|
||||
id: JayCompositorId,
|
||||
client: Rc<Client>,
|
||||
tracker: Tracker<Self>,
|
||||
version: Version,
|
||||
}
|
||||
|
||||
impl JayDamageTrackingRequestHandler for JayDamageTracking {
|
||||
type Error = JayDamageTrackingError;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_visualizer_enabled(
|
||||
&self,
|
||||
req: SetVisualizerEnabled,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
let state = &self.client.state;
|
||||
state.damage_visualizer.set_enabled(state, req.enabled != 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_visualizer_color(
|
||||
&self,
|
||||
req: SetVisualizerColor,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.client.state.damage_visualizer.set_color(Color {
|
||||
r: req.r,
|
||||
g: req.g,
|
||||
b: req.b,
|
||||
a: req.a,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_visualizer_decay(
|
||||
&self,
|
||||
req: SetVisualizerDecay,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.client
|
||||
.state
|
||||
.damage_visualizer
|
||||
.set_decay(Duration::from_millis(req.millis));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayDamageTracking;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for JayDamageTracking {}
|
||||
|
||||
simple_add_obj!(JayDamageTracking);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayDamageTrackingError {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(JayDamageTrackingError, ClientError);
|
||||
|
|
@ -327,9 +327,6 @@ impl JayScreencast {
|
|||
Target::Toplevel(tl) => {
|
||||
let data = tl.tl_data();
|
||||
data.jay_screencasts.remove(&(self.client.id, self.id));
|
||||
if data.jay_screencasts.is_empty() {
|
||||
self.client.state.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -418,10 +415,16 @@ impl JayScreencast {
|
|||
|
||||
fn damage(&self) {
|
||||
if let Some(target) = self.target.get() {
|
||||
match target {
|
||||
Target::Output(o) => o.global.connector.connector.damage(),
|
||||
Target::Toplevel(_) => self.client.state.damage(),
|
||||
}
|
||||
let rect = match target {
|
||||
Target::Output(o) => o.global.pos.get(),
|
||||
Target::Toplevel(t) => {
|
||||
if !t.node_visible() {
|
||||
return;
|
||||
}
|
||||
t.node_absolute_position()
|
||||
}
|
||||
};
|
||||
self.client.state.damage(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -535,9 +538,6 @@ impl JayScreencastRequestHandler for JayScreencast {
|
|||
}
|
||||
let t = t.toplevel.clone();
|
||||
let data = t.tl_data();
|
||||
if data.jay_screencasts.is_empty() {
|
||||
data.state.damage();
|
||||
}
|
||||
data.jay_screencasts
|
||||
.set((self.client.id, self.id), slf.clone());
|
||||
new_target = Some(Target::Toplevel(t));
|
||||
|
|
@ -577,6 +577,10 @@ impl JayScreencastRequestHandler for JayScreencast {
|
|||
}
|
||||
}
|
||||
|
||||
if self.running.get() {
|
||||
self.damage();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ impl WlCallback {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn send_done(&self) {
|
||||
pub fn send_done(&self, data: u32) {
|
||||
self.client.event(Done {
|
||||
self_id: self.id,
|
||||
callback_data: 0,
|
||||
callback_data: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl WlDisplayRequestHandler for WlDisplay {
|
|||
let cb = Rc::new(WlCallback::new(req.callback, &self.client));
|
||||
track!(self.client, cb);
|
||||
self.client.add_client_obj(&cb)?;
|
||||
cb.send_done();
|
||||
cb.send_done(0);
|
||||
self.client.remove_obj(&*cb)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,14 +61,13 @@ use {
|
|||
zwp_pointer_gesture_swipe_v1::ZwpPointerGestureSwipeV1,
|
||||
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
wl_surface::{dnd_icon::DndIcon, WlSurface},
|
||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
rect::Rect,
|
||||
state::{DeviceHandlerData, State},
|
||||
time::now_usec,
|
||||
tree::{
|
||||
generic_node_visitor, ContainerNode, ContainerSplit, Direction, FoundNode, Node,
|
||||
OutputNode, ToplevelNode, WorkspaceNode,
|
||||
|
|
@ -245,7 +244,7 @@ impl WlSeatGlobal {
|
|||
changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE),
|
||||
constraint: Default::default(),
|
||||
idle_notifications: Default::default(),
|
||||
last_input_usec: Cell::new(now_usec()),
|
||||
last_input_usec: Cell::new(state.now_usec()),
|
||||
wlr_data_devices: Default::default(),
|
||||
text_inputs: Default::default(),
|
||||
text_input: Default::default(),
|
||||
|
|
@ -562,7 +561,6 @@ impl WlSeatGlobal {
|
|||
if let Some(parent) = tl.tl_data().parent.get() {
|
||||
if let Some(tl) = parent.node_toplevel() {
|
||||
self.focus_node(tl.tl_into_node());
|
||||
self.state.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -723,11 +721,11 @@ impl WlSeatGlobal {
|
|||
self: &Rc<Self>,
|
||||
origin: &Rc<WlSurface>,
|
||||
source: Option<Rc<WlDataSource>>,
|
||||
icon: Option<Rc<WlSurface>>,
|
||||
icon: Option<Rc<DndIcon>>,
|
||||
serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
if let Some(icon) = &icon {
|
||||
icon.set_output(&self.pointer_cursor.output());
|
||||
icon.surface().set_output(&self.pointer_cursor.output());
|
||||
}
|
||||
self.pointer_owner
|
||||
.start_drag(self, origin, source, icon, serial)
|
||||
|
|
@ -819,7 +817,7 @@ impl WlSeatGlobal {
|
|||
self.primary_selection.get()
|
||||
}
|
||||
|
||||
pub fn dnd_icon(&self) -> Option<Rc<WlSurface>> {
|
||||
pub fn dnd_icon(&self) -> Option<Rc<DndIcon>> {
|
||||
self.pointer_owner.dnd_icon()
|
||||
}
|
||||
|
||||
|
|
@ -926,7 +924,7 @@ impl WlSeatGlobal {
|
|||
pub fn set_visible(&self, visible: bool) {
|
||||
self.cursor_user_group.set_visible(visible);
|
||||
if let Some(icon) = self.dnd_icon() {
|
||||
icon.set_visible(visible);
|
||||
icon.surface().set_visible(visible);
|
||||
}
|
||||
if let Some(tl_drag) = self.toplevel_drag() {
|
||||
if let Some(tl) = tl_drag.toplevel.get() {
|
||||
|
|
@ -965,7 +963,7 @@ impl WlSeatGlobal {
|
|||
impl CursorUserOwner for WlSeatGlobal {
|
||||
fn output_changed(&self, output: &Rc<OutputNode>) {
|
||||
if let Some(dnd) = self.pointer_owner.dnd_icon() {
|
||||
dnd.set_output(output);
|
||||
dnd.surface().set_output(output);
|
||||
}
|
||||
if let Some(drag) = self.pointer_owner.toplevel_drag() {
|
||||
if let Some(tl) = drag.toplevel.get() {
|
||||
|
|
|
|||
|
|
@ -410,6 +410,24 @@ impl WlSeatGlobal {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_pointer_cursor_position(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
|
||||
let dnd_icon = self.pointer_owner.dnd_icon();
|
||||
if let Some(dnd_icon) = &dnd_icon {
|
||||
let (x_old, y_old) = self.pointer_cursor.position_int();
|
||||
dnd_icon.damage_at(x_old, y_old);
|
||||
}
|
||||
let (x, y) = self.pointer_cursor.set_position(x, y);
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
if let Some(dnd_icon) = &dnd_icon {
|
||||
dnd_icon.damage_at(x_int, y_int);
|
||||
}
|
||||
if let Some(td) = self.pointer_owner.toplevel_drag() {
|
||||
td.move_(x_int, y_int);
|
||||
}
|
||||
(x, y)
|
||||
}
|
||||
|
||||
fn connector_position_event(
|
||||
self: &Rc<Self>,
|
||||
time_usec: u64,
|
||||
|
|
@ -424,7 +442,7 @@ impl WlSeatGlobal {
|
|||
let pos = output.global.pos.get();
|
||||
x += Fixed::from_int(pos.x1());
|
||||
y += Fixed::from_int(pos.y1());
|
||||
(x, y) = self.pointer_cursor.set_position(x, y);
|
||||
(x, y) = self.set_pointer_cursor_position(x, y);
|
||||
if let Some(c) = self.constraint.get() {
|
||||
if c.ty == ConstraintType::Lock || !c.contains(x.round_down(), y.round_down()) {
|
||||
c.deactivate();
|
||||
|
|
@ -484,7 +502,7 @@ impl WlSeatGlobal {
|
|||
dy_unaccelerated,
|
||||
);
|
||||
});
|
||||
self.pointer_cursor.set_position(x, y);
|
||||
self.set_pointer_cursor_position(x, y);
|
||||
self.cursor_moved(time_usec);
|
||||
}
|
||||
|
||||
|
|
@ -884,7 +902,6 @@ impl WlSeatGlobal {
|
|||
}
|
||||
|
||||
pub(super) fn apply_changes(self: &Rc<Self>) {
|
||||
self.state.damage();
|
||||
self.pointer_owner.apply_changes(self);
|
||||
if self.changes.get().contains(CHANGE_TREE) {
|
||||
self.tablet_apply_changes();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
use {
|
||||
crate::{
|
||||
fixed::Fixed, ifs::wl_seat::WlSeatGlobal, time::now_usec, tree::Node,
|
||||
utils::clonecell::CloneCell,
|
||||
},
|
||||
crate::{fixed::Fixed, ifs::wl_seat::WlSeatGlobal, tree::Node, utils::clonecell::CloneCell},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
|
|
@ -179,7 +176,7 @@ struct SwipeGesture {
|
|||
|
||||
impl GestureOwner for SwipeGesture {
|
||||
fn revert_to_default(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.swipe_end(seat, now_usec(), true);
|
||||
self.swipe_end(seat, seat.state.now_usec(), true);
|
||||
}
|
||||
|
||||
fn swipe_update(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, dx: Fixed, dy: Fixed) {
|
||||
|
|
@ -199,7 +196,7 @@ struct PinchGesture {
|
|||
|
||||
impl GestureOwner for PinchGesture {
|
||||
fn revert_to_default(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.pinch_end(seat, now_usec(), true);
|
||||
self.pinch_end(seat, seat.state.now_usec(), true);
|
||||
}
|
||||
|
||||
fn pinch_update(
|
||||
|
|
@ -228,7 +225,7 @@ struct HoldGesture {
|
|||
|
||||
impl GestureOwner for HoldGesture {
|
||||
fn revert_to_default(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.hold_end(seat, now_usec(), true);
|
||||
self.hold_end(seat, seat.state.now_usec(), true);
|
||||
}
|
||||
|
||||
fn hold_end(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, cancelled: bool) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use {
|
|||
wl_pointer::PendingScroll, Dnd, DroppedDnd, WlSeatError, WlSeatGlobal, BTN_LEFT,
|
||||
BTN_RIGHT, CHANGE_CURSOR_MOVED, CHANGE_TREE,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
wl_surface::{dnd_icon::DndIcon, WlSurface},
|
||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||
},
|
||||
state::DeviceHandlerData,
|
||||
|
|
@ -120,7 +120,7 @@ impl PointerOwnerHolder {
|
|||
seat: &Rc<WlSeatGlobal>,
|
||||
origin: &Rc<WlSurface>,
|
||||
source: Option<Rc<WlDataSource>>,
|
||||
icon: Option<Rc<WlSurface>>,
|
||||
icon: Option<Rc<DndIcon>>,
|
||||
serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
self.owner
|
||||
|
|
@ -144,7 +144,7 @@ impl PointerOwnerHolder {
|
|||
self.owner.get().dnd_target_removed(seat);
|
||||
}
|
||||
|
||||
pub fn dnd_icon(&self) -> Option<Rc<WlSurface>> {
|
||||
pub fn dnd_icon(&self) -> Option<Rc<DndIcon>> {
|
||||
self.owner.get().dnd_icon()
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ trait PointerOwner {
|
|||
seat: &Rc<WlSeatGlobal>,
|
||||
origin: &Rc<WlSurface>,
|
||||
source: Option<Rc<WlDataSource>>,
|
||||
icon: Option<Rc<WlSurface>>,
|
||||
icon: Option<Rc<DndIcon>>,
|
||||
serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
let _ = origin;
|
||||
|
|
@ -232,7 +232,7 @@ trait PointerOwner {
|
|||
fn dnd_target_removed(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.cancel_dnd(seat);
|
||||
}
|
||||
fn dnd_icon(&self) -> Option<Rc<WlSurface>> {
|
||||
fn dnd_icon(&self) -> Option<Rc<DndIcon>> {
|
||||
None
|
||||
}
|
||||
fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
|
||||
|
|
@ -264,7 +264,7 @@ struct DndPointerOwner {
|
|||
button: u32,
|
||||
dnd: Dnd,
|
||||
target: CloneCell<Rc<dyn Node>>,
|
||||
icon: CloneCell<Option<Rc<WlSurface>>>,
|
||||
icon: CloneCell<Option<Rc<DndIcon>>>,
|
||||
pos_x: Cell<Fixed>,
|
||||
pos_y: Cell<Fixed>,
|
||||
}
|
||||
|
|
@ -385,7 +385,6 @@ impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
|
|||
if !T::IS_DEFAULT {
|
||||
seat.pointer_owner.set_default_pointer_owner(seat);
|
||||
seat.trigger_tree_changed();
|
||||
seat.state.damage();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -446,7 +445,7 @@ impl<T: SimplePointerOwnerUsecase> PointerOwner for SimpleGrabPointerOwner<T> {
|
|||
seat: &Rc<WlSeatGlobal>,
|
||||
origin: &Rc<WlSurface>,
|
||||
src: Option<Rc<WlDataSource>>,
|
||||
icon: Option<Rc<WlSurface>>,
|
||||
icon: Option<Rc<DndIcon>>,
|
||||
serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
self.usecase
|
||||
|
|
@ -486,7 +485,7 @@ impl PointerOwner for DndPointerOwner {
|
|||
}
|
||||
}
|
||||
if let Some(icon) = self.icon.get() {
|
||||
icon.set_dnd_icon_seat(seat.id(), None);
|
||||
icon.disable();
|
||||
}
|
||||
seat.pointer_owner.set_default_pointer_owner(seat);
|
||||
seat.tree_changed.trigger();
|
||||
|
|
@ -542,7 +541,7 @@ impl PointerOwner for DndPointerOwner {
|
|||
ipc::detach_seat(&**src, seat);
|
||||
}
|
||||
if let Some(icon) = self.icon.get() {
|
||||
icon.set_dnd_icon_seat(seat.id(), None);
|
||||
icon.disable();
|
||||
}
|
||||
seat.pointer_owner.set_default_pointer_owner(seat);
|
||||
seat.tree_changed.trigger();
|
||||
|
|
@ -558,7 +557,7 @@ impl PointerOwner for DndPointerOwner {
|
|||
seat.state.tree_changed();
|
||||
}
|
||||
|
||||
fn dnd_icon(&self) -> Option<Rc<WlSurface>> {
|
||||
fn dnd_icon(&self) -> Option<Rc<DndIcon>> {
|
||||
self.icon.get()
|
||||
}
|
||||
|
||||
|
|
@ -593,7 +592,7 @@ trait SimplePointerOwnerUsecase: Sized + Clone + 'static {
|
|||
seat: &Rc<WlSeatGlobal>,
|
||||
origin: &Rc<WlSurface>,
|
||||
src: Option<Rc<WlDataSource>>,
|
||||
icon: Option<Rc<WlSurface>>,
|
||||
icon: Option<Rc<DndIcon>>,
|
||||
serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
let _ = grab;
|
||||
|
|
@ -638,7 +637,7 @@ impl SimplePointerOwnerUsecase for DefaultPointerUsecase {
|
|||
seat: &Rc<WlSeatGlobal>,
|
||||
origin: &Rc<WlSurface>,
|
||||
src: Option<Rc<WlDataSource>>,
|
||||
icon: Option<Rc<WlSurface>>,
|
||||
icon: Option<Rc<DndIcon>>,
|
||||
serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
let button = match grab.buttons.iter().next() {
|
||||
|
|
@ -655,7 +654,7 @@ impl SimplePointerOwnerUsecase for DefaultPointerUsecase {
|
|||
return Ok(());
|
||||
}
|
||||
if let Some(icon) = &icon {
|
||||
icon.set_dnd_icon_seat(seat.id, Some(seat));
|
||||
icon.enable();
|
||||
}
|
||||
if let Some(new) = &src {
|
||||
ipc::attach_seat(&**new, seat, ipc::Role::Dnd)?;
|
||||
|
|
@ -763,21 +762,17 @@ impl<S: ToplevelSelector> NodeSelectorUsecase for SelectToplevelUsecase<S> {
|
|||
}
|
||||
|
||||
fn node_focus(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
|
||||
let mut damage = false;
|
||||
let tl = node.clone().node_into_toplevel();
|
||||
if let Some(tl) = &tl {
|
||||
tl.tl_data().render_highlight.fetch_add(1);
|
||||
if !tl.tl_admits_children() {
|
||||
seat.pointer_cursor().set_known(KnownCursor::Pointer);
|
||||
}
|
||||
damage = true;
|
||||
seat.state.damage(tl.node_absolute_position());
|
||||
}
|
||||
if let Some(prev) = self.latest.set(tl) {
|
||||
prev.tl_data().render_highlight.fetch_sub(1);
|
||||
damage = true;
|
||||
}
|
||||
if damage {
|
||||
seat.state.damage();
|
||||
seat.state.damage(prev.node_absolute_position());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -787,7 +782,7 @@ impl<S: ?Sized> Drop for SelectToplevelUsecase<S> {
|
|||
if let Some(prev) = self.latest.take() {
|
||||
prev.tl_data().render_highlight.fetch_sub(1);
|
||||
if let Some(seat) = self.seat.upgrade() {
|
||||
seat.state.damage();
|
||||
seat.state.damage(prev.node_absolute_position());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -812,19 +807,15 @@ impl<S: WorkspaceSelector> NodeSelectorUsecase for SelectWorkspaceUsecase<S> {
|
|||
}
|
||||
|
||||
fn node_focus(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
|
||||
let mut damage = false;
|
||||
let ws = node.clone().node_into_workspace();
|
||||
if let Some(ws) = &ws {
|
||||
ws.render_highlight.fetch_add(1);
|
||||
seat.pointer_cursor().set_known(KnownCursor::Pointer);
|
||||
damage = true;
|
||||
seat.state.damage(ws.position.get());
|
||||
}
|
||||
if let Some(prev) = self.latest.set(ws) {
|
||||
prev.render_highlight.fetch_sub(1);
|
||||
damage = true;
|
||||
}
|
||||
if damage {
|
||||
seat.state.damage();
|
||||
seat.state.damage(prev.position.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -834,7 +825,7 @@ impl<S: ?Sized> Drop for SelectWorkspaceUsecase<S> {
|
|||
if let Some(prev) = self.latest.take() {
|
||||
prev.render_highlight.fetch_sub(1);
|
||||
if let Some(seat) = self.seat.upgrade() {
|
||||
seat.state.damage();
|
||||
seat.state.damage(prev.position.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ use {
|
|||
wl_surface::WlSurface,
|
||||
},
|
||||
object::Version,
|
||||
time::now_usec,
|
||||
tree::{FoundNode, Node},
|
||||
utils::{
|
||||
bindings::PerClientBindings, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
|
|
@ -328,7 +327,7 @@ impl WlSeatGlobal {
|
|||
return;
|
||||
};
|
||||
for tool in tablet.tools.lock().drain_values() {
|
||||
self.tablet_handle_remove_tool(now_usec(), tool.id);
|
||||
self.tablet_handle_remove_tool(tablet.seat.state.now_usec(), tool.id);
|
||||
}
|
||||
for pad in tablet.pads.lock().drain_values() {
|
||||
pad.pad_owner.destroy(&pad);
|
||||
|
|
@ -366,7 +365,7 @@ impl WlSeatGlobal {
|
|||
if self.tablet.tools.is_empty() {
|
||||
return;
|
||||
}
|
||||
let now = now_usec();
|
||||
let now = self.state.now_usec();
|
||||
for tool in self.tablet.tools.lock().values() {
|
||||
tool.tool_owner.apply_changes(tool, now, None);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use {
|
|||
},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
time::{now_usec, usec_to_msec},
|
||||
time::usec_to_msec,
|
||||
utils::{clonecell::CloneCell, hash_map_ext::HashMapExt},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
|
|
@ -192,7 +192,7 @@ impl TabletPad {
|
|||
|
||||
pub fn surface_enter(self: &Rc<Self>, n: &WlSurface) {
|
||||
let mut serial = n.client.pending_serial();
|
||||
let time = usec_to_msec(now_usec());
|
||||
let time = n.client.state.now_msec() as u32;
|
||||
self.for_each_pair(n, |tablet, pad| {
|
||||
pad.send_enter(serial.get(), &tablet, n);
|
||||
for group in &self.groups {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::wl_seat::tablet::{PadButtonState, TabletPad},
|
||||
time::now_usec,
|
||||
tree::Node,
|
||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||
},
|
||||
|
|
@ -42,7 +41,9 @@ impl PadOwnerHolder {
|
|||
}
|
||||
|
||||
pub fn destroy(&self, pad: &Rc<TabletPad>) {
|
||||
self.owner.get().revert_to_default(pad, now_usec());
|
||||
self.owner
|
||||
.get()
|
||||
.revert_to_default(pad, pad.seat.state.now_usec());
|
||||
let prev = pad.node.set(pad.seat.state.root.clone());
|
||||
prev.node_on_tablet_pad_leave(pad);
|
||||
prev.node_seat_state().remove_tablet_pad_focus(pad);
|
||||
|
|
@ -53,7 +54,9 @@ impl PadOwnerHolder {
|
|||
}
|
||||
|
||||
pub fn focus_root(&self, pad: &Rc<TabletPad>) {
|
||||
self.owner.get().revert_to_default(pad, now_usec());
|
||||
self.owner
|
||||
.get()
|
||||
.revert_to_default(pad, pad.seat.state.now_usec());
|
||||
let node = pad.seat.state.root.clone();
|
||||
pad.focus_node(node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use {
|
|||
crate::{
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::tablet::{TabletTool, TabletToolChanges, ToolButtonState},
|
||||
time::now_usec,
|
||||
tree::{FindTreeUsecase, FoundNode, Node},
|
||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||
},
|
||||
|
|
@ -35,14 +34,15 @@ impl ToolOwnerHolder {
|
|||
pub fn destroy(&self, tool: &Rc<TabletTool>) {
|
||||
let root = tool.tablet.seat.state.root.clone();
|
||||
let prev = tool.node.set(root);
|
||||
prev.node_on_tablet_tool_leave(tool, now_usec());
|
||||
prev.node_on_tablet_tool_leave(tool, tool.tablet.seat.state.now_usec());
|
||||
prev.node_seat_state().remove_tablet_tool_focus(tool);
|
||||
}
|
||||
|
||||
pub fn focus_root(&self, tool: &Rc<TabletTool>) {
|
||||
self.owner.set(self.default.clone());
|
||||
let root = tool.tablet.seat.state.root.clone();
|
||||
tool.set_node(root, now_usec());
|
||||
let state = &tool.tablet.seat.state;
|
||||
let root = state.root.clone();
|
||||
tool.set_node(root, state.now_usec());
|
||||
}
|
||||
|
||||
pub fn button(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
pub mod commit_timeline;
|
||||
pub mod cursor;
|
||||
pub mod dnd_icon;
|
||||
pub mod ext_session_lock_surface_v1;
|
||||
pub mod wl_subsurface;
|
||||
pub mod wp_alpha_modifier_surface_v1;
|
||||
|
|
@ -41,6 +42,7 @@ use {
|
|||
wl_surface::{
|
||||
commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError},
|
||||
cursor::CursorSurface,
|
||||
dnd_icon::DndIcon,
|
||||
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
|
||||
wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1,
|
||||
wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||
|
|
@ -61,7 +63,7 @@ use {
|
|||
renderer::Renderer,
|
||||
tree::{
|
||||
ContainerNode, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase,
|
||||
OutputNode, PlaceholderNode, ToplevelNode,
|
||||
OutputNode, OutputNodeId, PlaceholderNode, ToplevelNode,
|
||||
},
|
||||
utils::{
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||
|
|
@ -80,7 +82,7 @@ use {
|
|||
xwayland::XWaylandEvent,
|
||||
},
|
||||
ahash::AHashMap,
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
isnt::std_1::{primitive::IsntSliceExt, vec::IsntVecExt},
|
||||
jay_config::video::Transform,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -255,6 +257,7 @@ pub struct WlSurface {
|
|||
opaque_region: Cell<Option<Rc<Region>>>,
|
||||
buffer_points: RefCell<BufferPoints>,
|
||||
pub buffer_points_norm: RefCell<SampleRect>,
|
||||
damage_matrix: Cell<DamageMatrix>,
|
||||
buffer_transform: Cell<Transform>,
|
||||
buffer_scale: Cell<i32>,
|
||||
src_rect: Cell<Option<[Fixed; 4]>>,
|
||||
|
|
@ -263,6 +266,7 @@ pub struct WlSurface {
|
|||
pub buffer_abs_pos: Cell<Rect>,
|
||||
pub need_extents_update: Cell<bool>,
|
||||
pub buffer: CloneCell<Option<Rc<SurfaceBuffer>>>,
|
||||
buffer_presented: Cell<bool>,
|
||||
pub shm_texture: CloneCell<Option<Rc<dyn GfxTexture>>>,
|
||||
pub buf_x: NumCell<i32>,
|
||||
pub buf_y: NumCell<i32>,
|
||||
|
|
@ -273,7 +277,7 @@ pub struct WlSurface {
|
|||
seat_state: NodeSeatState,
|
||||
toplevel: CloneCell<Option<Rc<dyn ToplevelNode>>>,
|
||||
cursors: SmallMap<CursorUserId, Rc<CursorSurface>, 1>,
|
||||
dnd_icons: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
dnd_icons: SmallMap<SeatId, Rc<DndIcon>, 1>,
|
||||
pub tracker: Tracker<Self>,
|
||||
idle_inhibitors: SmallMap<ZwpIdleInhibitorV1Id, Rc<ZwpIdleInhibitorV1>, 1>,
|
||||
viewporter: CloneCell<Option<Rc<WpViewport>>>,
|
||||
|
|
@ -397,7 +401,8 @@ struct PendingState {
|
|||
input_region: Option<Option<Rc<Region>>>,
|
||||
frame_request: Vec<Rc<WlCallback>>,
|
||||
damage_full: bool,
|
||||
damage: Vec<Rect>,
|
||||
buffer_damage: Vec<Rect>,
|
||||
surface_damage: Vec<Rect>,
|
||||
presentation_feedback: Vec<Rc<WpPresentationFeedback>>,
|
||||
src_rect: Option<Option<[Fixed; 4]>>,
|
||||
dst_size: Option<Option<(i32, i32)>>,
|
||||
|
|
@ -469,14 +474,23 @@ impl PendingState {
|
|||
self.offset = (dx1 + dx2, dy1 + dy2);
|
||||
}
|
||||
self.frame_request.append(&mut next.frame_request);
|
||||
self.damage_full |= mem::take(&mut next.damage_full);
|
||||
if !self.damage_full {
|
||||
if self.damage.len() + next.damage.len() > MAX_DAMAGE {
|
||||
self.damage_full = true;
|
||||
self.damage.clear();
|
||||
if self.buffer_damage.len() + next.buffer_damage.len() > MAX_DAMAGE {
|
||||
self.damage_full();
|
||||
} else {
|
||||
self.damage.append(&mut next.damage);
|
||||
self.buffer_damage.append(&mut next.buffer_damage);
|
||||
}
|
||||
}
|
||||
if !self.damage_full {
|
||||
if self.surface_damage.len() + next.surface_damage.len() > MAX_DAMAGE {
|
||||
self.damage_full();
|
||||
} else {
|
||||
self.surface_damage.append(&mut next.surface_damage);
|
||||
}
|
||||
}
|
||||
next.surface_damage.clear();
|
||||
next.buffer_damage.clear();
|
||||
mem::swap(
|
||||
&mut self.presentation_feedback,
|
||||
&mut next.presentation_feedback,
|
||||
|
|
@ -518,6 +532,16 @@ impl PendingState {
|
|||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn damage_full(&mut self) {
|
||||
self.damage_full = true;
|
||||
self.buffer_damage.clear();
|
||||
self.surface_damage.clear();
|
||||
}
|
||||
|
||||
fn has_damage(&self) -> bool {
|
||||
self.damage_full || self.buffer_damage.is_not_empty() || self.surface_damage.is_not_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -545,6 +569,7 @@ impl WlSurface {
|
|||
opaque_region: Default::default(),
|
||||
buffer_points: Default::default(),
|
||||
buffer_points_norm: Default::default(),
|
||||
damage_matrix: Default::default(),
|
||||
buffer_transform: Cell::new(Transform::None),
|
||||
buffer_scale: Cell::new(1),
|
||||
src_rect: Cell::new(None),
|
||||
|
|
@ -553,6 +578,7 @@ impl WlSurface {
|
|||
buffer_abs_pos: Cell::new(Default::default()),
|
||||
need_extents_update: Default::default(),
|
||||
buffer: Default::default(),
|
||||
buffer_presented: Default::default(),
|
||||
shm_texture: Default::default(),
|
||||
buf_x: Default::default(),
|
||||
buf_y: Default::default(),
|
||||
|
|
@ -645,8 +671,13 @@ impl WlSurface {
|
|||
}
|
||||
|
||||
fn set_absolute_position(&self, x1: i32, y1: i32) {
|
||||
self.buffer_abs_pos
|
||||
.set(self.buffer_abs_pos.get().at_point(x1, y1));
|
||||
let old_pos = self.buffer_abs_pos.get();
|
||||
let new_pos = old_pos.at_point(x1, y1);
|
||||
if self.visible.get() && self.toplevel.is_none() {
|
||||
self.client.state.damage(old_pos);
|
||||
self.client.state.damage(new_pos);
|
||||
}
|
||||
self.buffer_abs_pos.set(new_pos);
|
||||
if let Some(children) = self.children.borrow_mut().deref_mut() {
|
||||
for ss in children.subsurfaces.values() {
|
||||
let pos = ss.position.get();
|
||||
|
|
@ -758,6 +789,17 @@ impl WlSurface {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn into_dnd_icon(
|
||||
self: &Rc<Self>,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
) -> Result<Rc<DndIcon>, WlSurfaceError> {
|
||||
self.set_role(SurfaceRole::DndIcon)?;
|
||||
Ok(Rc::new(DndIcon {
|
||||
surface: self.clone(),
|
||||
seat: seat.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn unset_ext(&self) {
|
||||
self.ext.set(self.client.state.none_surface_ext.clone());
|
||||
}
|
||||
|
|
@ -806,10 +848,39 @@ impl WlSurface {
|
|||
}
|
||||
|
||||
fn unset_dnd_icons(&self) {
|
||||
while let Some((_, seat)) = self.dnd_icons.pop() {
|
||||
seat.remove_dnd_icon()
|
||||
while let Some((_, dnd_icon)) = self.dnd_icons.pop() {
|
||||
dnd_icon.seat.remove_dnd_icon();
|
||||
if self.visible.get() {
|
||||
dnd_icon.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_damage<F>(
|
||||
&self,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
f: F,
|
||||
) -> Result<(), WlSurfaceError>
|
||||
where
|
||||
F: Fn(&mut PendingState) -> &mut Vec<Rect>,
|
||||
{
|
||||
let pending = &mut *self.pending.borrow_mut();
|
||||
if !pending.damage_full {
|
||||
let damage = f(pending);
|
||||
if damage.len() >= MAX_DAMAGE {
|
||||
pending.damage_full();
|
||||
} else {
|
||||
let Some(rect) = Rect::new_sized(x, y, width, height) else {
|
||||
return Err(WlSurfaceError::InvalidRect);
|
||||
};
|
||||
damage.push(rect);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_DAMAGE: usize = 32;
|
||||
|
|
@ -866,11 +937,10 @@ impl WlSurfaceRequestHandler for WlSurface {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn damage(&self, _req: Damage, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let pending = &mut *self.pending.borrow_mut();
|
||||
pending.damage.clear();
|
||||
pending.damage_full = true;
|
||||
Ok(())
|
||||
fn damage(&self, req: Damage, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.do_damage(req.x, req.y, req.width, req.height, |p| {
|
||||
&mut p.surface_damage
|
||||
})
|
||||
}
|
||||
|
||||
fn frame(&self, req: Frame, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
|
|
@ -936,19 +1006,9 @@ impl WlSurfaceRequestHandler for WlSurface {
|
|||
}
|
||||
|
||||
fn damage_buffer(&self, req: DamageBuffer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let pending = &mut *self.pending.borrow_mut();
|
||||
if !pending.damage_full {
|
||||
if pending.damage.len() >= MAX_DAMAGE || self.shm_texture.is_none() {
|
||||
pending.damage.clear();
|
||||
pending.damage_full = true;
|
||||
} else {
|
||||
let Some(rect) = Rect::new_sized(req.x, req.y, req.width, req.height) else {
|
||||
return Err(WlSurfaceError::InvalidRect);
|
||||
};
|
||||
pending.damage.push(rect);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
self.do_damage(req.x, req.y, req.width, req.height, |p| {
|
||||
&mut p.buffer_damage
|
||||
})
|
||||
}
|
||||
|
||||
fn offset(&self, req: Offset, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
|
|
@ -1008,13 +1068,11 @@ impl WlSurface {
|
|||
old_raw_size = Some(buffer.buffer.rect);
|
||||
}
|
||||
if let Some(buffer) = buffer_change {
|
||||
let damage = match pending.damage_full {
|
||||
let damage = match pending.damage_full || pending.surface_damage.is_not_empty() {
|
||||
true => None,
|
||||
false => Some(&pending.damage[..]),
|
||||
false => Some(&pending.buffer_damage[..]),
|
||||
};
|
||||
buffer.update_texture_or_log(self, damage);
|
||||
pending.damage.clear();
|
||||
pending.damage_full = false;
|
||||
let (sync, release_sync) = match pending.explicit_sync {
|
||||
false => (AcquireSync::Implicit, ReleaseSync::Implicit),
|
||||
true => (AcquireSync::Unnecessary, ReleaseSync::Explicit),
|
||||
|
|
@ -1031,6 +1089,9 @@ impl WlSurface {
|
|||
release,
|
||||
};
|
||||
self.buffer.set(Some(Rc::new(surface_buffer)));
|
||||
if pending.has_damage() {
|
||||
self.buffer_presented.set(false);
|
||||
}
|
||||
} else {
|
||||
self.shm_texture.take();
|
||||
self.buf_x.set(0);
|
||||
|
|
@ -1118,6 +1179,18 @@ impl WlSurface {
|
|||
y2,
|
||||
buffer_transform: self.buffer_transform.get(),
|
||||
};
|
||||
let (buffer_width, buffer_height) = buffer.buffer.rect.size();
|
||||
let (dst_width, dst_height) = new_size.unwrap_or_default();
|
||||
let damage_matrix = DamageMatrix::new(
|
||||
self.buffer_transform.get(),
|
||||
self.buffer_scale.get(),
|
||||
buffer_width,
|
||||
buffer_height,
|
||||
self.src_rect.get(),
|
||||
dst_width,
|
||||
dst_height,
|
||||
);
|
||||
self.damage_matrix.set(damage_matrix);
|
||||
}
|
||||
}
|
||||
let (width, height) = new_size.unwrap_or_default();
|
||||
|
|
@ -1174,10 +1247,72 @@ impl WlSurface {
|
|||
}
|
||||
}
|
||||
self.ext.get().after_apply_commit();
|
||||
self.client.state.damage();
|
||||
if self.visible.get() {
|
||||
if self.buffer_presented.get() {
|
||||
let now = self.client.state.now_msec() as _;
|
||||
for fr in self.frame_requests.borrow_mut().drain(..) {
|
||||
fr.send_done(now);
|
||||
let _ = fr.client.remove_obj(&*fr);
|
||||
}
|
||||
} else {
|
||||
self.apply_damage(pending);
|
||||
}
|
||||
}
|
||||
pending.buffer_damage.clear();
|
||||
pending.surface_damage.clear();
|
||||
pending.damage_full = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_damage(&self, pending: &PendingState) {
|
||||
let bounds = self.toplevel.get().map(|tl| tl.node_absolute_position());
|
||||
let pos = self.buffer_abs_pos.get();
|
||||
let apply_damage = |pos: Rect| {
|
||||
if pending.damage_full {
|
||||
let mut damage = pos;
|
||||
if let Some(bounds) = bounds {
|
||||
damage = damage.intersect(bounds);
|
||||
}
|
||||
self.client.state.damage(damage);
|
||||
} else {
|
||||
let matrix = self.damage_matrix.get();
|
||||
if let Some(buffer) = self.buffer.get() {
|
||||
for damage in &pending.buffer_damage {
|
||||
let mut damage =
|
||||
matrix.apply(pos.x1(), pos.y1(), damage.intersect(buffer.buffer.rect));
|
||||
if let Some(bounds) = bounds {
|
||||
damage = damage.intersect(bounds);
|
||||
}
|
||||
self.client.state.damage(damage);
|
||||
}
|
||||
}
|
||||
for damage in &pending.surface_damage {
|
||||
let mut damage = damage.move_(pos.x1(), pos.y1());
|
||||
damage = damage.intersect(bounds.unwrap_or(pos));
|
||||
self.client.state.damage(damage);
|
||||
}
|
||||
}
|
||||
};
|
||||
match self.role.get() {
|
||||
SurfaceRole::Cursor => {
|
||||
for (_, cursor) in &self.cursors {
|
||||
if cursor.needs_damage_tracking() {
|
||||
let (x, y) = cursor.surface_position();
|
||||
apply_damage(pos.at_point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
SurfaceRole::DndIcon => {
|
||||
for (_, dnd_icon) in &self.dnd_icons {
|
||||
let (x, y) = dnd_icon.seat.pointer_cursor().position_int();
|
||||
let (x, y) = dnd_icon.surface_position(x, y);
|
||||
apply_damage(pos.at_point(x, y));
|
||||
}
|
||||
}
|
||||
_ => apply_damage(pos),
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_explicit_sync(&self, pending: &mut PendingState) -> Result<(), WlSurfaceError> {
|
||||
pending.explicit_sync = self.sync_obj_surface.is_some();
|
||||
if !pending.explicit_sync {
|
||||
|
|
@ -1283,6 +1418,12 @@ impl WlSurface {
|
|||
self.seat_state.set_visible(self, visible);
|
||||
}
|
||||
|
||||
pub fn presented(&self, on: OutputNodeId) {
|
||||
if on == self.output.get().id {
|
||||
self.buffer_presented.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detach_node(&self, set_invisible: bool) {
|
||||
for (_, constraint) in &self.constraints {
|
||||
constraint.deactivate();
|
||||
|
|
@ -1309,8 +1450,8 @@ impl WlSurface {
|
|||
}
|
||||
}
|
||||
self.seat_state.destroy_node(self);
|
||||
if self.visible.get() {
|
||||
self.client.state.damage();
|
||||
if self.visible.get() && self.toplevel.is_none() {
|
||||
self.client.state.damage(self.buffer_abs_pos.get());
|
||||
}
|
||||
if set_invisible {
|
||||
self.visible.set(false);
|
||||
|
|
@ -1349,18 +1490,6 @@ impl WlSurface {
|
|||
.consume_pending_child(self, child, &mut consume)
|
||||
}
|
||||
|
||||
pub fn set_dnd_icon_seat(&self, id: SeatId, seat: Option<&Rc<WlSeatGlobal>>) {
|
||||
match seat {
|
||||
None => {
|
||||
self.dnd_icons.remove(&id);
|
||||
}
|
||||
Some(seat) => {
|
||||
self.dnd_icons.insert(id, seat.clone());
|
||||
}
|
||||
}
|
||||
self.set_visible(self.dnd_icons.is_not_empty() && self.client.state.root_visible());
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> Option<f32> {
|
||||
self.alpha.get()
|
||||
}
|
||||
|
|
@ -1700,3 +1829,114 @@ efrom!(WlSurfaceError, ClientError);
|
|||
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
||||
efrom!(WlSurfaceError, CommitTimelineError);
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct DamageMatrix {
|
||||
transform: Transform,
|
||||
mx: f64,
|
||||
my: f64,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
smear: i32,
|
||||
}
|
||||
|
||||
impl Default for DamageMatrix {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transform: Default::default(),
|
||||
mx: 1.0,
|
||||
my: 1.0,
|
||||
dx: 0.0,
|
||||
dy: 0.0,
|
||||
smear: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DamageMatrix {
|
||||
fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
|
||||
let x1 = rect.x1() - self.smear;
|
||||
let x2 = rect.x2() + self.smear;
|
||||
let y1 = rect.y1() - self.smear;
|
||||
let y2 = rect.y2() + self.smear;
|
||||
let [x1, y1, x2, y2] = match self.transform {
|
||||
Transform::None => [x1, y1, x2, y2],
|
||||
Transform::Rotate90 => [-y2, x1, -y1, x2],
|
||||
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
|
||||
Transform::Rotate270 => [y1, -x2, y2, -x1],
|
||||
Transform::Flip => [-x2, y1, -x1, y2],
|
||||
Transform::FlipRotate90 => [y1, x1, y2, x2],
|
||||
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
|
||||
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
|
||||
};
|
||||
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
|
||||
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
|
||||
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
|
||||
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
|
||||
Rect::new(x1, y1, x2, y2).unwrap()
|
||||
}
|
||||
|
||||
fn new(
|
||||
transform: Transform,
|
||||
legacy_scale: i32,
|
||||
buffer_width: i32,
|
||||
buffer_height: i32,
|
||||
viewport: Option<[Fixed; 4]>,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> DamageMatrix {
|
||||
let mut buffer_width = buffer_width as f64;
|
||||
let mut buffer_height = buffer_height as f64;
|
||||
let dst_width = dst_width as f64;
|
||||
let dst_height = dst_height as f64;
|
||||
|
||||
let mut mx = 1.0;
|
||||
let mut my = 1.0;
|
||||
if legacy_scale != 1 {
|
||||
let scale_inv = 1.0 / (legacy_scale as f64);
|
||||
mx = scale_inv;
|
||||
my = scale_inv;
|
||||
buffer_width *= scale_inv;
|
||||
buffer_height *= scale_inv;
|
||||
}
|
||||
let (mut buffer_width, mut buffer_height) =
|
||||
transform.maybe_swap((buffer_width, buffer_height));
|
||||
let (mut dx, mut dy) = match transform {
|
||||
Transform::None => (0.0, 0.0),
|
||||
Transform::Rotate90 => (buffer_width, 0.0),
|
||||
Transform::Rotate180 => (buffer_width, buffer_height),
|
||||
Transform::Rotate270 => (0.0, buffer_height),
|
||||
Transform::Flip => (buffer_width, 0.0),
|
||||
Transform::FlipRotate90 => (0.0, 0.0),
|
||||
Transform::FlipRotate180 => (0.0, buffer_height),
|
||||
Transform::FlipRotate270 => (buffer_width, buffer_height),
|
||||
};
|
||||
if let Some([x, y, w, h]) = viewport {
|
||||
dx -= x.to_f64();
|
||||
dy -= y.to_f64();
|
||||
buffer_width = w.to_f64();
|
||||
buffer_height = h.to_f64();
|
||||
}
|
||||
let mut smear = false;
|
||||
if dst_width != buffer_width {
|
||||
let scale = dst_width / buffer_width;
|
||||
mx *= scale;
|
||||
dx *= scale;
|
||||
smear |= dst_width > buffer_width;
|
||||
}
|
||||
if dst_height != buffer_height {
|
||||
let scale = dst_height / buffer_height;
|
||||
my *= scale;
|
||||
dy *= scale;
|
||||
smear |= dst_height > buffer_height;
|
||||
}
|
||||
DamageMatrix {
|
||||
transform,
|
||||
mx,
|
||||
my,
|
||||
dx,
|
||||
dy,
|
||||
smear: smear as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,16 @@ impl CursorSurface {
|
|||
pub fn update_hardware_cursor(&self) {
|
||||
self.user.update_hardware_cursor();
|
||||
}
|
||||
|
||||
pub fn needs_damage_tracking(&self) -> bool {
|
||||
self.user.software_cursor()
|
||||
}
|
||||
|
||||
pub fn surface_position(&self) -> (i32, i32) {
|
||||
let (x, y) = self.user.position();
|
||||
let (dx, dy) = self.hotspot.get();
|
||||
(x.to_int() - dx, y.to_int() - dy)
|
||||
}
|
||||
}
|
||||
|
||||
impl Cursor for CursorSurface {
|
||||
|
|
@ -86,11 +96,11 @@ impl Cursor for CursorSurface {
|
|||
let extents = self.surface.extents.get();
|
||||
renderer.render_surface(&self.surface, -extents.x1(), -extents.y1(), None);
|
||||
|
||||
struct FrameRequests;
|
||||
struct FrameRequests(u64);
|
||||
impl NodeVisitorBase for FrameRequests {
|
||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||
for fr in node.frame_requests.borrow_mut().drain(..) {
|
||||
fr.send_done();
|
||||
fr.send_done(self.0 as _);
|
||||
let _ = fr.client.remove_obj(fr.deref());
|
||||
}
|
||||
for fr in node.presentation_feedback.borrow_mut().drain(..) {
|
||||
|
|
@ -100,7 +110,7 @@ impl Cursor for CursorSurface {
|
|||
node.node_visit_children(self);
|
||||
}
|
||||
}
|
||||
FrameRequests.visit_surface(&self.surface);
|
||||
FrameRequests(self.surface.client.state.now_msec()).visit_surface(&self.surface);
|
||||
}
|
||||
|
||||
fn extents_at_scale(&self, scale: Scale) -> Rect {
|
||||
|
|
|
|||
69
src/ifs/wl_surface/dnd_icon.rs
Normal file
69
src/ifs/wl_surface/dnd_icon.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::{wl_seat::WlSeatGlobal, wl_surface::WlSurface},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct DndIcon {
|
||||
pub(super) surface: Rc<WlSurface>,
|
||||
pub(super) seat: Rc<WlSeatGlobal>,
|
||||
}
|
||||
|
||||
impl DndIcon {
|
||||
pub fn surface(&self) -> &Rc<WlSurface> {
|
||||
&self.surface
|
||||
}
|
||||
|
||||
fn update_visible(&self) {
|
||||
let was_visible = self.surface.visible.get();
|
||||
let is_visible =
|
||||
self.surface.dnd_icons.is_not_empty() && self.surface.client.state.root_visible();
|
||||
self.surface.set_visible(is_visible);
|
||||
if was_visible != is_visible {
|
||||
self.damage();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable(self: &Rc<Self>) {
|
||||
self.surface.dnd_icons.insert(self.seat.id(), self.clone());
|
||||
self.update_visible();
|
||||
}
|
||||
|
||||
pub fn disable(self: &Rc<Self>) {
|
||||
self.surface.dnd_icons.remove(&self.seat.id());
|
||||
self.update_visible();
|
||||
}
|
||||
|
||||
pub fn surface_position(&self, seat_x: i32, seat_y: i32) -> (i32, i32) {
|
||||
(
|
||||
seat_x + self.surface.buf_x.get(),
|
||||
seat_y + self.surface.buf_y.get(),
|
||||
)
|
||||
}
|
||||
|
||||
fn extents(&self, x: i32, y: i32) -> Rect {
|
||||
let (x, y) = self.surface_position(x, y);
|
||||
self.surface.extents.get().move_(x, y)
|
||||
}
|
||||
|
||||
pub fn damage(&self) {
|
||||
let (x, y) = self.seat.pointer_cursor().position_int();
|
||||
self.damage_at(x, y);
|
||||
}
|
||||
|
||||
pub fn damage_at(&self, x: i32, y: i32) {
|
||||
let extents = self.extents(x, y);
|
||||
self.surface.client.state.damage(extents);
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer<'_>, cursor_rect: &Rect, x: i32, y: i32) {
|
||||
let extents = self.extents(x, y);
|
||||
if extents.intersects(&cursor_rect) {
|
||||
let (x, y) = cursor_rect.translate(extents.x1(), extents.y1());
|
||||
renderer.render_surface(&self.surface, x, y, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ impl ExtSessionLockSurfaceV1 {
|
|||
|
||||
pub fn change_extents(&self, rect: Rect) {
|
||||
self.send_configure(rect.width(), rect.height());
|
||||
self.surface.set_absolute_position(rect.x1(), rect.x2());
|
||||
self.surface.set_absolute_position(rect.x1(), rect.y1());
|
||||
}
|
||||
|
||||
fn send_configure(&self, width: i32, height: i32) {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,9 @@ impl WlSubsurface {
|
|||
if let Some((x, y)) = pending.position.take() {
|
||||
self.position
|
||||
.set(self.surface.buffer_abs_pos.get().at_point(x, y));
|
||||
let (parent_x, parent_y) = self.parent.buffer_abs_pos.get().position();
|
||||
self.surface
|
||||
.set_absolute_position(parent_x + x, parent_y + y);
|
||||
self.parent.need_extents_update.set(true);
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -172,6 +175,8 @@ impl WlSubsurface {
|
|||
self.surface.set_toplevel(self.parent.toplevel.get());
|
||||
self.surface.ext.set(self.clone());
|
||||
update_children_attach(self)?;
|
||||
let (x, y) = self.parent.buffer_abs_pos.get().position();
|
||||
self.surface.set_absolute_position(x, y);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -347,6 +352,9 @@ impl SurfaceExt for WlSubsurface {
|
|||
if self.had_buffer.replace(has_buffer) != has_buffer {
|
||||
if has_buffer {
|
||||
if self.parent.visible.get() {
|
||||
let (x, y) = self.surface.buffer_abs_pos.get().position();
|
||||
let extents = self.surface.extents.get();
|
||||
self.surface.client.state.damage(extents.move_(x, y));
|
||||
self.surface.set_visible(true);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -252,6 +252,7 @@ impl Xwindow {
|
|||
|
||||
pub fn map_status_changed(self: &Rc<Self>) {
|
||||
let map_change = self.map_change();
|
||||
let override_redirect = self.data.info.override_redirect.get();
|
||||
match map_change {
|
||||
Change::None => return,
|
||||
Change::Unmap => {
|
||||
|
|
@ -261,7 +262,7 @@ impl Xwindow {
|
|||
.set(self.data.info.extents.take());
|
||||
self.tl_destroy();
|
||||
}
|
||||
Change::Map if self.data.info.override_redirect.get() => {
|
||||
Change::Map if override_redirect => {
|
||||
self.clone()
|
||||
.tl_change_extents(&self.data.info.pending_extents.get());
|
||||
*self.display_link.borrow_mut() =
|
||||
|
|
@ -284,12 +285,17 @@ impl Xwindow {
|
|||
match map_change {
|
||||
Change::Unmap => self.tl_set_visible(false),
|
||||
Change::Map => {
|
||||
self.tl_set_visible(true);
|
||||
if override_redirect {
|
||||
self.tl_set_visible(true);
|
||||
}
|
||||
self.toplevel_data.broadcast(self.clone());
|
||||
}
|
||||
Change::None => {}
|
||||
}
|
||||
self.data.state.tree_changed();
|
||||
if override_redirect {
|
||||
self.data.state.damage(self.data.info.pending_extents.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -414,7 +420,10 @@ impl ToplevelNodeBase for Xwindow {
|
|||
// log::info!("xwin {} change_extents {:?}", self.data.window_id, rect);
|
||||
let old = self.data.info.extents.replace(*rect);
|
||||
if old != *rect {
|
||||
if !self.data.info.override_redirect.get() {
|
||||
if self.data.info.override_redirect.get() {
|
||||
self.data.state.damage(old);
|
||||
self.data.state.damage(*rect);
|
||||
} else {
|
||||
self.data
|
||||
.state
|
||||
.xwayland
|
||||
|
|
|
|||
|
|
@ -167,6 +167,10 @@ pub trait XdgSurfaceExt: Debug {
|
|||
fn extents_changed(&self) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn geometry_changed(&self) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl XdgSurface {
|
||||
|
|
@ -189,16 +193,20 @@ impl XdgSurface {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_surface_position(&self) {
|
||||
let (mut x1, mut y1) = self.absolute_desired_extents.get().position();
|
||||
if let Some(geo) = self.geometry.get() {
|
||||
x1 -= geo.x1();
|
||||
y1 -= geo.y1();
|
||||
}
|
||||
self.surface.set_absolute_position(x1, y1);
|
||||
self.update_popup_positions();
|
||||
}
|
||||
|
||||
fn set_absolute_desired_extents(&self, ext: &Rect) {
|
||||
let prev = self.absolute_desired_extents.replace(*ext);
|
||||
if ext.position() != prev.position() {
|
||||
let (mut x1, mut y1) = (ext.x1(), ext.y1());
|
||||
if let Some(geo) = self.geometry.get() {
|
||||
x1 -= geo.x1();
|
||||
y1 -= geo.y1();
|
||||
}
|
||||
self.surface.set_absolute_position(x1, y1);
|
||||
self.update_popup_positions();
|
||||
self.update_surface_position();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +262,12 @@ impl XdgSurface {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn damage(&self) {
|
||||
let (x, y) = self.surface.buffer_abs_pos.get().position();
|
||||
let extents = self.surface.extents.get();
|
||||
self.surface.client.state.damage(extents.move_(x, y));
|
||||
}
|
||||
|
||||
pub fn geometry(&self) -> Option<Rect> {
|
||||
self.geometry.get()
|
||||
}
|
||||
|
|
@ -489,8 +503,14 @@ impl SurfaceExt for XdgSurface {
|
|||
}
|
||||
if let Some(pending) = &mut pending.xdg_surface {
|
||||
if let Some(geometry) = pending.geometry.take() {
|
||||
self.geometry.set(Some(geometry));
|
||||
self.update_extents();
|
||||
let prev = self.geometry.replace(Some(geometry));
|
||||
if prev != Some(geometry) {
|
||||
self.update_extents();
|
||||
self.update_surface_position();
|
||||
if let Some(ext) = self.ext.get() {
|
||||
ext.geometry_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -152,8 +152,10 @@ impl XdgToplevel {
|
|||
}
|
||||
|
||||
pub fn send_current_configure(&self) {
|
||||
let rect = self.xdg.absolute_desired_extents.get();
|
||||
self.send_configure_checked(rect.width(), rect.height());
|
||||
if self.drag.is_none() {
|
||||
let rect = self.xdg.absolute_desired_extents.get();
|
||||
self.send_configure_checked(rect.width(), rect.height());
|
||||
}
|
||||
self.xdg.do_send_configure();
|
||||
}
|
||||
|
||||
|
|
@ -404,8 +406,15 @@ impl XdgToplevel {
|
|||
self.xdg.set_output(&seat.get_output());
|
||||
}
|
||||
self.toplevel_data.broadcast(self.clone());
|
||||
self.tl_set_visible(self.state.root_visible());
|
||||
self.xdg.damage();
|
||||
}
|
||||
self.extents_changed();
|
||||
} else {
|
||||
if self.is_mapped.replace(false) {
|
||||
self.tl_set_visible(false);
|
||||
self.xdg.damage();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -670,6 +679,14 @@ impl XdgSurfaceExt for XdgToplevel {
|
|||
self.toplevel_data.pos.set(self.xdg.extents.get());
|
||||
self.tl_extents_changed();
|
||||
}
|
||||
|
||||
fn geometry_changed(&self) {
|
||||
self.xdg
|
||||
.surface
|
||||
.client
|
||||
.state
|
||||
.damage(self.node_absolute_position());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -588,6 +588,11 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
|
|||
}
|
||||
if self.mapped.get() != was_mapped {
|
||||
output.update_visible();
|
||||
if self.mapped.get() {
|
||||
let (x, y) = self.surface.buffer_abs_pos.get().position();
|
||||
let extents = self.surface.extents.get().move_(x, y);
|
||||
self.client.state.damage(extents);
|
||||
}
|
||||
}
|
||||
if self.mapped.get() {
|
||||
match self.keyboard_interactivity.get() {
|
||||
|
|
|
|||
|
|
@ -44,17 +44,24 @@ pub async fn input_popup_positioning(state: Rc<State>) {
|
|||
}
|
||||
|
||||
impl ZwpInputPopupSurfaceV2 {
|
||||
fn damage(&self) {
|
||||
let (x, y) = self.surface.buffer_abs_pos.get().position();
|
||||
let extents = self.surface.extents.get();
|
||||
self.client.state.damage(extents.move_(x, y));
|
||||
}
|
||||
|
||||
pub fn update_visible(self: &Rc<Self>) {
|
||||
let was_visible = self.surface.visible.get();
|
||||
let is_visible = self.surface.buffer.is_some()
|
||||
&& self.input_method.connection.is_some()
|
||||
&& self.client.state.root_visible();
|
||||
self.surface.set_visible(is_visible);
|
||||
if was_visible || is_visible {
|
||||
self.client.state.damage();
|
||||
}
|
||||
if !was_visible && is_visible {
|
||||
self.schedule_positioning();
|
||||
if was_visible != is_visible {
|
||||
if is_visible {
|
||||
self.schedule_positioning();
|
||||
} else {
|
||||
self.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +139,9 @@ impl ZwpInputPopupSurfaceV2 {
|
|||
}
|
||||
|
||||
fn detach(&self) {
|
||||
if self.surface.visible.get() {
|
||||
self.damage();
|
||||
}
|
||||
self.surface.destroy_node();
|
||||
self.surface.unset_ext();
|
||||
self.input_method.popups.remove(&self.id);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ use {
|
|||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
tree::{Node, ToplevelNode},
|
||||
utils::clonecell::CloneCell,
|
||||
wire::{xdg_toplevel_drag_v1::*, XdgToplevelDragV1Id},
|
||||
},
|
||||
|
|
@ -49,6 +52,33 @@ impl XdgToplevelDragV1 {
|
|||
tl.drag.take();
|
||||
}
|
||||
}
|
||||
|
||||
fn move2(&self, x: i32, y: i32, damage_initial: bool) {
|
||||
if let Some(tl) = self.toplevel.get() {
|
||||
if damage_initial && tl.node_visible() {
|
||||
tl.xdg.damage();
|
||||
}
|
||||
let extents = tl.xdg.absolute_desired_extents.get();
|
||||
let extents = extents.at_point(x - self.x_off.get(), y - self.y_off.get());
|
||||
tl.clone().tl_change_extents(&extents);
|
||||
if tl.node_visible() {
|
||||
tl.xdg.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_(&self, x: i32, y: i32) {
|
||||
self.move2(x, y, true);
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer<'_>, cursor_rect: &Rect, x: i32, y: i32) {
|
||||
if let Some(tl) = self.toplevel.get() {
|
||||
if tl.xdg.surface.buffer.get().is_some() {
|
||||
let (x, y) = cursor_rect.translate(x - self.x_off.get(), y - self.y_off.get());
|
||||
renderer.render_xdg_surface(&tl.xdg, x, y, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XdgToplevelDragV1RequestHandler for XdgToplevelDragV1 {
|
||||
|
|
@ -96,6 +126,10 @@ impl XdgToplevelDragV1 {
|
|||
};
|
||||
tl.prepare_toplevel_drag();
|
||||
self.client.state.tree_changed();
|
||||
if let Some(seat) = self.source.data.seat.get() {
|
||||
let (x, y) = seat.pointer_cursor().position_int();
|
||||
self.move2(x, y, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish_drag(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ use {
|
|||
test_error::TestResult, test_gfx_api::TestGfxCtx, test_utils::test_expected_event::TEEH,
|
||||
},
|
||||
state::State,
|
||||
time::now_usec,
|
||||
utils::{
|
||||
clonecell::CloneCell, copyhashmap::CopyHashMap, on_change::OnChange, oserror::OsError,
|
||||
syncqueue::SyncQueue,
|
||||
|
|
@ -75,6 +74,7 @@ impl TestBackend {
|
|||
chm
|
||||
},
|
||||
name: Rc::new("default-mouse".to_string()),
|
||||
state: state.clone(),
|
||||
},
|
||||
transform_matrix: Cell::new([[1.0, 0.0], [0.0, 1.0]]),
|
||||
accel_speed: Cell::new(1.0),
|
||||
|
|
@ -93,6 +93,7 @@ impl TestBackend {
|
|||
chm
|
||||
},
|
||||
name: Rc::new("default-keyboard".to_string()),
|
||||
state: state.clone(),
|
||||
},
|
||||
});
|
||||
let mode = Mode {
|
||||
|
|
@ -273,7 +274,7 @@ pub struct TestMouseClick {
|
|||
impl Drop for TestMouseClick {
|
||||
fn drop(&mut self) {
|
||||
self.mouse.common.event(InputEvent::Button {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.mouse.common.state.now_usec(),
|
||||
button: self.button,
|
||||
state: KeyState::Released,
|
||||
});
|
||||
|
|
@ -291,7 +292,7 @@ pub struct TestBackendMouse {
|
|||
impl TestBackendMouse {
|
||||
pub fn rel(&self, dx: f64, dy: f64) {
|
||||
self.common.event(InputEvent::Motion {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.common.state.now_usec(),
|
||||
dx: Fixed::from_f64(dx * self.accel_speed.get()),
|
||||
dy: Fixed::from_f64(dy * self.accel_speed.get()),
|
||||
dx_unaccelerated: Fixed::from_f64(dx),
|
||||
|
|
@ -301,7 +302,7 @@ impl TestBackendMouse {
|
|||
|
||||
pub fn abs(&self, connector: &TestConnector, x: f64, y: f64) {
|
||||
self.common.event(InputEvent::ConnectorPosition {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.common.state.now_usec(),
|
||||
connector: connector.id,
|
||||
x: Fixed::from_f64(x),
|
||||
y: Fixed::from_f64(y),
|
||||
|
|
@ -310,7 +311,7 @@ impl TestBackendMouse {
|
|||
|
||||
pub fn click(self: &Rc<Self>, button: u32) -> TestMouseClick {
|
||||
self.common.event(InputEvent::Button {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.common.state.now_usec(),
|
||||
button,
|
||||
state: KeyState::Pressed,
|
||||
});
|
||||
|
|
@ -330,7 +331,7 @@ impl TestBackendMouse {
|
|||
inverted: false,
|
||||
});
|
||||
self.common.event(InputEvent::AxisFrame {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.common.state.now_usec(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -348,7 +349,7 @@ impl TestBackendMouse {
|
|||
inverted,
|
||||
});
|
||||
self.common.event(InputEvent::AxisFrame {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.common.state.now_usec(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -365,7 +366,7 @@ pub struct PressedKey {
|
|||
impl Drop for PressedKey {
|
||||
fn drop(&mut self) {
|
||||
self.kb.common.event(InputEvent::Key {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.kb.common.state.now_usec(),
|
||||
key: self.key,
|
||||
state: KeyState::Released,
|
||||
});
|
||||
|
|
@ -375,7 +376,7 @@ impl Drop for PressedKey {
|
|||
impl TestBackendKb {
|
||||
pub fn press(self: &Rc<Self>, key: u32) -> PressedKey {
|
||||
self.common.event(InputEvent::Key {
|
||||
time_usec: now_usec(),
|
||||
time_usec: self.common.state.now_usec(),
|
||||
key,
|
||||
state: KeyState::Pressed,
|
||||
});
|
||||
|
|
@ -421,6 +422,7 @@ pub struct TestInputDeviceCommon {
|
|||
pub on_change: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||
pub capabilities: CopyHashMap<InputDeviceCapability, ()>,
|
||||
pub name: Rc<String>,
|
||||
pub state: Rc<State>,
|
||||
}
|
||||
|
||||
impl TestInputDeviceCommon {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use {
|
|||
backend::KeyState,
|
||||
ifs::wl_seat::wl_keyboard,
|
||||
it::{test_error::TestError, test_object::TestObject, test_transport::TestTransport},
|
||||
time::now_usec,
|
||||
wire::{zwp_virtual_keyboard_v1::*, ZwpVirtualKeyboardV1Id},
|
||||
},
|
||||
std::{cell::Cell, io::Write, rc::Rc},
|
||||
|
|
@ -50,7 +49,7 @@ impl TestVirtualKeyboard {
|
|||
};
|
||||
self.tran.send(Key {
|
||||
self_id: self.id,
|
||||
time: (now_usec() / 1000) as u32,
|
||||
time: self.tran.run.state.now_msec() as u32,
|
||||
key,
|
||||
state,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -294,6 +294,11 @@ macro_rules! tree_id {
|
|||
pub fn raw(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn none() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for $id {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ mod compositor;
|
|||
mod config;
|
||||
mod cursor;
|
||||
mod cursor_user;
|
||||
mod damage;
|
||||
mod dbus;
|
||||
mod drm_feedback;
|
||||
mod edid;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,10 @@ impl Region {
|
|||
self.extents
|
||||
}
|
||||
|
||||
pub fn rects(&self) -> &[Rect] {
|
||||
unsafe { mem::transmute::<&[RectRaw], &[Rect]>(&self.rects[..]) }
|
||||
}
|
||||
|
||||
pub fn contains(&self, x: i32, y: i32) -> bool {
|
||||
if !self.extents.contains(x, y) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ use {
|
|||
state::State,
|
||||
theme::Color,
|
||||
tree::{
|
||||
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
||||
ToplevelNodeBase, WorkspaceNode,
|
||||
ContainerNode, DisplayNode, FloatNode, OutputNode, OutputNodeId, PlaceholderNode,
|
||||
ToplevelData, ToplevelNodeBase, WorkspaceNode,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
|
|
@ -31,16 +31,26 @@ use {
|
|||
|
||||
pub mod renderer_base;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RenderResult {
|
||||
pub frame_requests: Vec<Rc<WlCallback>>,
|
||||
pub presentation_feedbacks: Vec<Rc<WpPresentationFeedback>>,
|
||||
pub output_id: OutputNodeId,
|
||||
}
|
||||
|
||||
impl Default for RenderResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
frame_requests: Default::default(),
|
||||
presentation_feedbacks: Default::default(),
|
||||
output_id: OutputNodeId::none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderResult {
|
||||
pub fn dispatch_frame_requests(&mut self) {
|
||||
pub fn dispatch_frame_requests(&mut self, now: u64) {
|
||||
for fr in self.frame_requests.drain(..) {
|
||||
fr.send_done();
|
||||
fr.send_done(now as _);
|
||||
let _ = fr.client.remove_obj(&*fr);
|
||||
}
|
||||
}
|
||||
|
|
@ -441,6 +451,7 @@ impl Renderer<'_> {
|
|||
let mut fbs = surface.presentation_feedback.borrow_mut();
|
||||
result.presentation_feedbacks.extend(fbs.drain(..));
|
||||
}
|
||||
surface.presented(result.output_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -481,7 +492,7 @@ impl Renderer<'_> {
|
|||
if let Some(alpha) = alpha {
|
||||
color = color * alpha;
|
||||
}
|
||||
self.base.fill_boxes(&[rect], &color);
|
||||
self.base.fill_scaled_boxes(&[rect], &color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
48
src/state.rs
48
src/state.rs
|
|
@ -14,6 +14,7 @@ use {
|
|||
config::ConfigProxy,
|
||||
cursor::{Cursor, ServerCursors},
|
||||
cursor_user::{CursorUserGroup, CursorUserGroupId, CursorUserGroupIds, CursorUserIds},
|
||||
damage::DamageVisualizer,
|
||||
dbus::Dbus,
|
||||
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
||||
fixed::Fixed,
|
||||
|
|
@ -60,6 +61,7 @@ use {
|
|||
scale::Scale,
|
||||
security_context_acceptor::SecurityContextAcceptors,
|
||||
theme::{Color, Theme},
|
||||
time::Time,
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, Direction, DisplayNode, FloatNode, Node, NodeIds,
|
||||
NodeVisitorBase, OutputNode, PlaceholderNode, ToplevelNode, ToplevelNodeBase,
|
||||
|
|
@ -198,6 +200,7 @@ pub struct State {
|
|||
pub tablet_ids: TabletIds,
|
||||
pub tablet_tool_ids: TabletToolIds,
|
||||
pub tablet_pad_ids: TabletPadIds,
|
||||
pub damage_visualizer: DamageVisualizer,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -659,7 +662,6 @@ impl State {
|
|||
ws.flush_jay_workspaces();
|
||||
output.schedule_update_render_data();
|
||||
self.tree_changed();
|
||||
self.damage();
|
||||
// let seats = self.globals.seats.lock();
|
||||
// for seat in seats.values() {
|
||||
// seat.workspace_changed(&output);
|
||||
|
|
@ -686,7 +688,6 @@ impl State {
|
|||
for output in outputs.values() {
|
||||
output.set_status(&status);
|
||||
}
|
||||
self.damage();
|
||||
}
|
||||
|
||||
pub fn input_occurred(&self) {
|
||||
|
|
@ -728,14 +729,30 @@ impl State {
|
|||
serial as _
|
||||
}
|
||||
|
||||
pub fn damage(&self) {
|
||||
for connector in self.connectors.lock().values() {
|
||||
if connector.connected.get() {
|
||||
connector.connector.damage();
|
||||
pub fn damage(&self, rect: Rect) {
|
||||
if rect.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.damage_visualizer.add(rect);
|
||||
for output in self.root.outputs.lock().values() {
|
||||
if output.global.pos.get().intersects(&rect) {
|
||||
output.global.connector.connector.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_unlock(&self) {
|
||||
self.lock.locked.set(false);
|
||||
self.lock.lock.take();
|
||||
for output in self.root.outputs.lock().values() {
|
||||
if let Some(surface) = output.set_lock_surface(None) {
|
||||
surface.destroy_node();
|
||||
}
|
||||
}
|
||||
self.tree_changed();
|
||||
self.damage(self.root.extents.get());
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.lock.lock.take();
|
||||
self.xwayland.handler.borrow_mut().take();
|
||||
|
|
@ -843,7 +860,7 @@ impl State {
|
|||
render_hw_cursor,
|
||||
)?;
|
||||
output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None);
|
||||
rr.dispatch_frame_requests();
|
||||
rr.dispatch_frame_requests(self.now_msec());
|
||||
Ok(sync_file)
|
||||
}
|
||||
|
||||
|
|
@ -1049,6 +1066,23 @@ impl State {
|
|||
}
|
||||
(self.dummy_output.get().unwrap(), 0, 0)
|
||||
}
|
||||
|
||||
pub fn now(&self) -> Time {
|
||||
self.eng.now()
|
||||
}
|
||||
|
||||
pub fn now_nsec(&self) -> u64 {
|
||||
self.eng.now().nsec()
|
||||
}
|
||||
|
||||
pub fn now_usec(&self) -> u64 {
|
||||
self.eng.now().usec()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn now_msec(&self) -> u64 {
|
||||
self.eng.now().msec()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -172,7 +172,9 @@ impl ConnectorHandler {
|
|||
update_render_data_scheduled: Cell::new(false),
|
||||
hardware_cursor_needs_render: Cell::new(false),
|
||||
screencopies: Default::default(),
|
||||
title_visible: Default::default(),
|
||||
});
|
||||
on.update_visible();
|
||||
on.update_rects();
|
||||
self.state
|
||||
.add_output_scale(on.global.persistent.scale.get());
|
||||
|
|
@ -295,7 +297,7 @@ impl ConnectorHandler {
|
|||
.remove_output_scale(on.global.persistent.scale.get());
|
||||
let _ = self.state.remove_global(&*global);
|
||||
self.state.tree_changed();
|
||||
self.state.damage();
|
||||
self.state.damage(self.state.root.extents.get());
|
||||
}
|
||||
|
||||
async fn handle_non_desktop_connected(&self, monitor_info: MonitorInfo) {
|
||||
|
|
|
|||
35
src/time.rs
35
src/time.rs
|
|
@ -5,16 +5,9 @@ use {
|
|||
ops::{Add, Sub},
|
||||
time::Duration,
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TimeError {
|
||||
#[error("clock_gettime failed: {0}")]
|
||||
ClockGettime(crate::utils::oserror::OsError),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Time(pub c::timespec);
|
||||
|
||||
|
|
@ -28,20 +21,6 @@ impl Debug for Time {
|
|||
}
|
||||
|
||||
impl Time {
|
||||
pub fn now() -> Result<Time, TimeError> {
|
||||
let mut time = uapi::pod_zeroed();
|
||||
if let Err(e) = uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut time) {
|
||||
return Err(TimeError::ClockGettime(e.into()));
|
||||
}
|
||||
Ok(Self(time))
|
||||
}
|
||||
|
||||
pub fn in_ms(ms: u64) -> Result<Time, TimeError> {
|
||||
let now = Self::now()?;
|
||||
Ok(now + Duration::from_millis(ms))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn now_unchecked() -> Time {
|
||||
let mut time = uapi::pod_zeroed();
|
||||
let _ = uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut time);
|
||||
|
|
@ -73,6 +52,12 @@ impl Time {
|
|||
let nsec = self.0.tv_nsec as u64 / 1_000;
|
||||
sec + nsec
|
||||
}
|
||||
|
||||
pub fn msec(self) -> u64 {
|
||||
let sec = self.0.tv_sec as u64 * 1_000;
|
||||
let nsec = self.0.tv_nsec as u64 / 1_000_000;
|
||||
sec + nsec
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Time {}
|
||||
|
|
@ -124,14 +109,6 @@ impl Add<Duration> for Time {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn now_nsec() -> u64 {
|
||||
Time::now_unchecked().nsec()
|
||||
}
|
||||
|
||||
pub fn now_usec() -> u64 {
|
||||
Time::now_unchecked().usec()
|
||||
}
|
||||
|
||||
pub fn usec_to_msec(usec: u64) -> u32 {
|
||||
(usec / 1000) as u32
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ use {
|
|||
},
|
||||
wheel::{Wheel, WheelError},
|
||||
wire::{
|
||||
wl_callback, wl_display, wl_registry, JayCompositor, JayCompositorId, WlCallbackId,
|
||||
WlRegistryId,
|
||||
wl_callback, wl_display, wl_registry, JayCompositor, JayCompositorId,
|
||||
JayDamageTracking, JayDamageTrackingId, WlCallbackId, WlRegistryId,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
|
|
@ -92,6 +92,7 @@ pub struct ToolClient {
|
|||
outgoing: Cell<Option<SpawnedFuture<()>>>,
|
||||
singletons: CloneCell<Option<Rc<Singletons>>>,
|
||||
jay_compositor: Cell<Option<JayCompositorId>>,
|
||||
jay_damage_tracking: Cell<Option<Option<JayDamageTrackingId>>>,
|
||||
}
|
||||
|
||||
pub fn with_tool_client<T, F>(level: Level, f: F)
|
||||
|
|
@ -188,6 +189,7 @@ impl ToolClient {
|
|||
outgoing: Default::default(),
|
||||
singletons: Default::default(),
|
||||
jay_compositor: Default::default(),
|
||||
jay_damage_tracking: Default::default(),
|
||||
});
|
||||
wl_display::Error::handle(&slf, WL_DISPLAY_ID, (), |_, val| {
|
||||
fatal!("The compositor returned a fatal error: {}", val.message);
|
||||
|
|
@ -285,6 +287,7 @@ impl ToolClient {
|
|||
#[derive(Default)]
|
||||
struct S {
|
||||
jay_compositor: Cell<Option<u32>>,
|
||||
jay_damage_tracking: Cell<Option<u32>>,
|
||||
}
|
||||
let s = Rc::new(S::default());
|
||||
let registry: WlRegistryId = self.id();
|
||||
|
|
@ -295,6 +298,8 @@ impl ToolClient {
|
|||
wl_registry::Global::handle(self, registry, s.clone(), |s, g| {
|
||||
if g.interface == JayCompositor.name() {
|
||||
s.jay_compositor.set(Some(g.name));
|
||||
} else if g.interface == JayDamageTracking.name() {
|
||||
s.jay_damage_tracking.set(Some(g.name));
|
||||
}
|
||||
});
|
||||
self.round_trip().await;
|
||||
|
|
@ -309,6 +314,7 @@ impl ToolClient {
|
|||
let res = Rc::new(Singletons {
|
||||
registry,
|
||||
jay_compositor: get!(jay_compositor, JayCompositor),
|
||||
jay_damage_tracking: s.jay_damage_tracking.get(),
|
||||
});
|
||||
self.singletons.set(Some(res.clone()));
|
||||
res
|
||||
|
|
@ -330,11 +336,33 @@ impl ToolClient {
|
|||
self.jay_compositor.set(Some(id));
|
||||
id
|
||||
}
|
||||
|
||||
pub async fn jay_damage_tracking(self: &Rc<Self>) -> Option<JayDamageTrackingId> {
|
||||
if let Some(id) = self.jay_damage_tracking.get() {
|
||||
return id;
|
||||
}
|
||||
let s = self.singletons().await;
|
||||
let Some(name) = s.jay_damage_tracking else {
|
||||
self.jay_damage_tracking.set(Some(None));
|
||||
return None;
|
||||
};
|
||||
let id: JayDamageTrackingId = self.id();
|
||||
self.send(wl_registry::Bind {
|
||||
self_id: s.registry,
|
||||
name,
|
||||
interface: JayDamageTracking.name(),
|
||||
version: 1,
|
||||
id: id.into(),
|
||||
});
|
||||
self.jay_damage_tracking.set(Some(Some(id)));
|
||||
Some(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Singletons {
|
||||
registry: WlRegistryId,
|
||||
pub jay_compositor: u32,
|
||||
pub jay_damage_tracking: Option<u32>,
|
||||
}
|
||||
|
||||
pub const NONE_FUTURE: Option<Pending<()>> = None;
|
||||
|
|
|
|||
|
|
@ -355,9 +355,24 @@ impl ContainerNode {
|
|||
self.schedule_compute_render_data();
|
||||
}
|
||||
|
||||
fn damage(&self) {
|
||||
self.state.damage(
|
||||
Rect::new_sized(
|
||||
self.abs_x1.get(),
|
||||
self.abs_y1.get(),
|
||||
self.width.get(),
|
||||
self.height.get(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
fn schedule_layout(self: &Rc<Self>) {
|
||||
if !self.layout_scheduled.replace(true) {
|
||||
self.state.pending_container_layout.push(self.clone());
|
||||
if self.toplevel_data.visible.get() {
|
||||
self.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -677,8 +692,13 @@ impl ContainerNode {
|
|||
let split = self.split.get();
|
||||
let have_active = self.children.iter().any(|c| c.active.get());
|
||||
let scales = self.state.scales.lock();
|
||||
let abs_x = self.abs_x1.get();
|
||||
let abs_y = self.abs_y1.get();
|
||||
for (i, child) in self.children.iter().enumerate() {
|
||||
let rect = child.title_rect.get();
|
||||
if self.toplevel_data.visible.get() {
|
||||
self.state.damage(rect.move_(abs_x, abs_y));
|
||||
}
|
||||
if i > 0 {
|
||||
let rect = if mono {
|
||||
Rect::new_sized(rect.x1() - bw, 0, bw, th)
|
||||
|
|
@ -1471,6 +1491,7 @@ impl ContainingNode for ContainerNode {
|
|||
if let Some(body) = body {
|
||||
let body = body.move_(self.abs_x1.get(), self.abs_y1.get());
|
||||
new.clone().tl_change_extents(&body);
|
||||
self.state.damage(body);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ impl DisplayNode {
|
|||
for seat in state.globals.seats.lock().values() {
|
||||
seat.set_visible(visible);
|
||||
}
|
||||
if visible {
|
||||
state.damage(self.extents.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,9 @@ impl FloatNode {
|
|||
child.tl_set_visible(floater.visible.get());
|
||||
child.tl_restack_popups();
|
||||
floater.schedule_layout();
|
||||
if floater.visible.get() {
|
||||
state.damage(position);
|
||||
}
|
||||
floater
|
||||
}
|
||||
|
||||
|
|
@ -199,11 +202,12 @@ impl FloatNode {
|
|||
_ => return,
|
||||
};
|
||||
let scales = self.state.scales.lock();
|
||||
let tr = Rect::new_sized(pos.x1() + bw, pos.y1() + bw, pos.width() - 2 * bw, th).unwrap();
|
||||
for (scale, _) in scales.iter() {
|
||||
let old_tex = self.title_textures.remove(scale);
|
||||
let mut th = th;
|
||||
let mut th = tr.height();
|
||||
let mut scalef = None;
|
||||
let mut width = pos.width() - 2 * bw;
|
||||
let mut width = tr.width();
|
||||
if *scale != 1 {
|
||||
let scale = scale.to_f64();
|
||||
th = (th as f64 * scale).round() as _;
|
||||
|
|
@ -222,6 +226,9 @@ impl FloatNode {
|
|||
};
|
||||
self.title_textures.set(*scale, texture);
|
||||
}
|
||||
if self.visible.get() {
|
||||
self.state.damage(tr);
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_move(
|
||||
|
|
@ -307,8 +314,12 @@ impl FloatNode {
|
|||
y2 = y2.max(y1 + 2 * bw + th + 1);
|
||||
}
|
||||
}
|
||||
self.position.set(Rect::new(x1, y1, x2, y2).unwrap());
|
||||
self.state.damage();
|
||||
let new_pos = Rect::new(x1, y1, x2, y2).unwrap();
|
||||
self.position.set(new_pos);
|
||||
if self.visible.get() {
|
||||
self.state.damage(pos);
|
||||
self.state.damage(new_pos);
|
||||
}
|
||||
self.schedule_layout();
|
||||
return;
|
||||
}
|
||||
|
|
@ -684,6 +695,9 @@ impl ContainingNode for FloatNode {
|
|||
self.pull_child_properties();
|
||||
new.tl_set_visible(self.visible.get());
|
||||
self.schedule_layout();
|
||||
if self.visible.get() {
|
||||
self.state.damage(self.position.get());
|
||||
}
|
||||
}
|
||||
|
||||
fn cnode_remove_child2(self: Rc<Self>, _child: &dyn Node, _preserve_focus: bool) {
|
||||
|
|
@ -691,6 +705,9 @@ impl ContainingNode for FloatNode {
|
|||
self.child.set(None);
|
||||
self.display_link.borrow_mut().take();
|
||||
self.workspace_link.set(None);
|
||||
if self.visible.get() {
|
||||
self.state.damage(self.position.get());
|
||||
}
|
||||
}
|
||||
|
||||
fn cnode_accepts_child(&self, _node: &dyn Node) -> bool {
|
||||
|
|
@ -716,8 +733,10 @@ impl ContainingNode for FloatNode {
|
|||
let (x, y) = (x - bw, y - th - bw - 1);
|
||||
let pos = self.position.get();
|
||||
if pos.position() != (x, y) {
|
||||
self.position.set(pos.at_point(x, y));
|
||||
self.state.damage();
|
||||
let new_pos = pos.at_point(x, y);
|
||||
self.position.set(new_pos);
|
||||
self.state.damage(pos);
|
||||
self.state.damage(new_pos);
|
||||
self.schedule_layout();
|
||||
}
|
||||
}
|
||||
|
|
@ -753,7 +772,10 @@ impl ContainingNode for FloatNode {
|
|||
let new_pos = Rect::new(x1, y1, x2, y2).unwrap();
|
||||
if new_pos != pos {
|
||||
self.position.set(new_pos);
|
||||
self.state.damage();
|
||||
if self.visible.get() {
|
||||
self.state.damage(pos);
|
||||
self.state.damage(new_pos);
|
||||
}
|
||||
self.schedule_layout();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ use {
|
|||
scale::Scale,
|
||||
state::State,
|
||||
text::{self, TextTexture},
|
||||
time::Time,
|
||||
tree::{
|
||||
walker::NodeVisitor, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node,
|
||||
NodeId, StackedNode, WorkspaceNode,
|
||||
|
|
@ -77,6 +76,7 @@ pub struct OutputNode {
|
|||
pub update_render_data_scheduled: Cell<bool>,
|
||||
pub screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc<JayScreencast>>,
|
||||
pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc<ZwlrScreencopyFrameV1>>,
|
||||
pub title_visible: Cell<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
|
|
@ -115,6 +115,9 @@ impl OutputNode {
|
|||
if let Some(c) = self.workspace.get() {
|
||||
c.change_extents(&self.workspace_rect.get());
|
||||
}
|
||||
if self.node_visible() {
|
||||
self.state.damage(self.global.pos.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +167,7 @@ impl OutputNode {
|
|||
if self.screencopies.is_empty() {
|
||||
return;
|
||||
}
|
||||
let now = Time::now().unwrap();
|
||||
let now = self.state.now();
|
||||
for capture in self.screencopies.lock().drain_values() {
|
||||
let wl_buffer = match capture.buffer.take() {
|
||||
Some(b) => b,
|
||||
|
|
@ -308,7 +311,8 @@ impl OutputNode {
|
|||
texture_height = (th as f64 * scale).round() as _;
|
||||
}
|
||||
let active_id = self.workspace.get().map(|w| w.id);
|
||||
let output_width = self.non_exclusive_rect.get().width();
|
||||
let non_exclusive_rect = self.non_exclusive_rect.get();
|
||||
let output_width = non_exclusive_rect.width();
|
||||
rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap();
|
||||
for ws in self.workspaces.iter() {
|
||||
let old_tex = ws.title_texture.take();
|
||||
|
|
@ -415,7 +419,16 @@ impl OutputNode {
|
|||
tex: title,
|
||||
});
|
||||
}
|
||||
self.state.damage();
|
||||
if self.title_visible.get() {
|
||||
let title_rect = Rect::new_sized(
|
||||
non_exclusive_rect.x1(),
|
||||
non_exclusive_rect.y1(),
|
||||
non_exclusive_rect.width(),
|
||||
th,
|
||||
)
|
||||
.unwrap();
|
||||
self.state.damage(title_rect);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_workspace(self: &Rc<Self>) -> Rc<WorkspaceNode> {
|
||||
|
|
@ -461,12 +474,16 @@ impl OutputNode {
|
|||
for seat in seats {
|
||||
ws.clone().node_do_focus(&seat, Direction::Unspecified);
|
||||
}
|
||||
if self.node_visible() {
|
||||
self.state.damage(self.global.pos.get());
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn create_workspace(self: &Rc<Self>, name: &str) -> Rc<WorkspaceNode> {
|
||||
let ws = Rc::new(WorkspaceNode {
|
||||
id: self.state.node_ids.next(),
|
||||
state: self.state.clone(),
|
||||
is_dummy: false,
|
||||
output: CloneCell::new(self.clone()),
|
||||
position: Cell::new(Default::default()),
|
||||
|
|
@ -583,6 +600,11 @@ impl OutputNode {
|
|||
}
|
||||
|
||||
fn change_extents_(self: &Rc<Self>, rect: &Rect) {
|
||||
if self.node_visible() {
|
||||
let old_pos = self.global.pos.get();
|
||||
self.state.damage(old_pos);
|
||||
self.state.damage(*rect);
|
||||
}
|
||||
self.global.persistent.pos.set((rect.x1(), rect.y1()));
|
||||
self.global.pos.set(*rect);
|
||||
self.state.root.update_extents();
|
||||
|
|
@ -703,6 +725,13 @@ impl OutputNode {
|
|||
prev
|
||||
}
|
||||
|
||||
pub fn fullscreen_changed(&self) {
|
||||
self.update_visible();
|
||||
if self.node_visible() {
|
||||
self.state.damage(self.global.pos.get());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_visible(&self) {
|
||||
let mut visible = self.state.root_visible();
|
||||
if self.state.lock.locked.get() {
|
||||
|
|
@ -723,6 +752,7 @@ impl OutputNode {
|
|||
have_fullscreen = ws.fullscreen.is_some();
|
||||
}
|
||||
let lower_visible = visible && !have_fullscreen;
|
||||
self.title_visible.set(lower_visible);
|
||||
set_layer_visible!(self.layers[0], lower_visible);
|
||||
set_layer_visible!(self.layers[1], lower_visible);
|
||||
if let Some(ws) = self.workspace.get() {
|
||||
|
|
@ -823,7 +853,7 @@ impl Node for OutputNode {
|
|||
}
|
||||
|
||||
fn node_visible(&self) -> bool {
|
||||
true
|
||||
self.state.root_visible()
|
||||
}
|
||||
|
||||
fn node_absolute_position(&self) -> Rect {
|
||||
|
|
|
|||
|
|
@ -435,7 +435,6 @@ impl ToplevelData {
|
|||
.tl_into_node()
|
||||
.node_do_focus(&seat, Direction::Unspecified);
|
||||
}
|
||||
state.damage();
|
||||
}
|
||||
|
||||
pub fn unset_fullscreen(&self, state: &Rc<State>, node: Rc<dyn ToplevelNode>) {
|
||||
|
|
@ -480,7 +479,6 @@ impl ToplevelData {
|
|||
fd.placeholder
|
||||
.node_seat_state()
|
||||
.destroy_node(fd.placeholder.deref());
|
||||
state.damage();
|
||||
}
|
||||
|
||||
pub fn set_visible(&self, node: &dyn Node, visible: bool) {
|
||||
|
|
@ -496,7 +494,6 @@ impl ToplevelData {
|
|||
if let Some(parent) = self.parent.get() {
|
||||
parent.cnode_child_attention_request_changed(node, false);
|
||||
}
|
||||
self.state.damage();
|
||||
}
|
||||
|
||||
pub fn request_attention(&self, node: &dyn Node) {
|
||||
|
|
@ -510,6 +507,5 @@ impl ToplevelData {
|
|||
if let Some(parent) = self.parent.get() {
|
||||
parent.cnode_child_attention_request_changed(node, true);
|
||||
}
|
||||
self.state.damage();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use {
|
|||
},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
state::State,
|
||||
text::TextTexture,
|
||||
tree::{
|
||||
container::ContainerNode, walker::NodeVisitor, ContainingNode, Direction,
|
||||
|
|
@ -38,6 +39,7 @@ tree_id!(WorkspaceNodeId);
|
|||
|
||||
pub struct WorkspaceNode {
|
||||
pub id: WorkspaceNodeId,
|
||||
pub state: Rc<State>,
|
||||
pub is_dummy: bool,
|
||||
pub output: CloneCell<Rc<OutputNode>>,
|
||||
pub position: Cell<Rect>,
|
||||
|
|
@ -85,6 +87,7 @@ impl WorkspaceNode {
|
|||
}
|
||||
if self.has_capture.replace(has_capture) != has_capture {
|
||||
output.schedule_update_render_data();
|
||||
output.state.damage(output.global.pos.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +120,7 @@ impl WorkspaceNode {
|
|||
container.tl_set_parent(self.clone());
|
||||
container.tl_set_visible(self.container_visible());
|
||||
self.container.set(Some(container.clone()));
|
||||
self.state.damage(self.position.get());
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
|
|
@ -168,7 +172,7 @@ impl WorkspaceNode {
|
|||
}
|
||||
self.pull_child_properties(&**node);
|
||||
if self.visible.get() {
|
||||
self.output.get().update_visible();
|
||||
self.output.get().fullscreen_changed();
|
||||
} else {
|
||||
node.tl_set_visible(false);
|
||||
}
|
||||
|
|
@ -183,7 +187,7 @@ impl WorkspaceNode {
|
|||
if let Some(node) = self.fullscreen.take() {
|
||||
self.discard_child_properties(&*node);
|
||||
if self.visible.get() {
|
||||
self.output.get().update_visible();
|
||||
self.output.get().fullscreen_changed();
|
||||
}
|
||||
if let Some(surface) = node.tl_scanout_surface() {
|
||||
if let Some(fb) = surface.client.state.drm_feedback.get() {
|
||||
|
|
@ -324,6 +328,7 @@ impl ContainingNode for WorkspaceNode {
|
|||
if container.node_id() == child.node_id() {
|
||||
self.discard_child_properties(&*container);
|
||||
self.container.set(None);
|
||||
self.state.damage(self.position.get());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -387,4 +392,10 @@ pub fn move_ws_to_output(
|
|||
if !source.is_dummy {
|
||||
source.schedule_update_render_data();
|
||||
}
|
||||
if source.node_visible() {
|
||||
target.state.damage(source.global.pos.get());
|
||||
}
|
||||
if target.node_visible() {
|
||||
target.state.damage(target.global.pos.get());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18
src/wheel.rs
18
src/wheel.rs
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
io_uring::{IoUring, IoUringError},
|
||||
time::{Time, TimeError},
|
||||
time::Time,
|
||||
utils::{
|
||||
buf::TypedBuf, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, hash_map_ext::HashMapExt,
|
||||
numcell::NumCell, oserror::OsError, stack::Stack,
|
||||
|
|
@ -28,8 +28,6 @@ pub enum WheelError {
|
|||
CreateFailed(#[source] OsError),
|
||||
#[error("Could not set the timerfd")]
|
||||
SetFailed(#[source] OsError),
|
||||
#[error("Cannot determine the time")]
|
||||
TimeError(#[from] TimeError),
|
||||
#[error("The timer wheel is already destroyed")]
|
||||
Destroyed,
|
||||
#[error("Could not read from the timerfd")]
|
||||
|
|
@ -99,6 +97,7 @@ impl Future for WheelTimeoutFuture {
|
|||
pub struct WheelData {
|
||||
destroyed: Cell<bool>,
|
||||
ring: Rc<IoUring>,
|
||||
eng: Rc<AsyncEngine>,
|
||||
fd: Rc<OwnedFd>,
|
||||
next_id: NumCell<u64>,
|
||||
start: Time,
|
||||
|
|
@ -118,9 +117,10 @@ impl Wheel {
|
|||
let data = Rc::new(WheelData {
|
||||
destroyed: Cell::new(false),
|
||||
ring: ring.clone(),
|
||||
eng: eng.clone(),
|
||||
fd,
|
||||
next_id: NumCell::new(1),
|
||||
start: Time::now()?,
|
||||
start: eng.now(),
|
||||
current_expiration: Default::default(),
|
||||
dispatchers: Default::default(),
|
||||
expirations: Default::default(),
|
||||
|
|
@ -161,13 +161,7 @@ impl Wheel {
|
|||
};
|
||||
}
|
||||
let future = self.future();
|
||||
let now = match Time::now() {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
future.data.expired.set(Some(Err(WheelError::TimeError(e))));
|
||||
return future;
|
||||
}
|
||||
};
|
||||
let now = self.data.eng.now();
|
||||
let expiration = (now + Duration::from_millis(ms)).round_to_ms();
|
||||
let current = self.data.current_expiration.get();
|
||||
if current.is_none() || expiration - self.data.start < current.unwrap() - self.data.start {
|
||||
|
|
@ -224,7 +218,7 @@ impl WheelData {
|
|||
if let Err(e) = self.ring.read(&self.fd, n.buf()).await {
|
||||
return Err(WheelError::Read(e));
|
||||
}
|
||||
let now = Time::now()?;
|
||||
let now = self.eng.now();
|
||||
let dist = now - self.start;
|
||||
{
|
||||
let mut expirations = self.expirations.borrow_mut();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ use {
|
|||
io_uring::{IoUring, IoUringError},
|
||||
rect::Rect,
|
||||
state::State,
|
||||
time::Time,
|
||||
tree::{Node, ToplevelNode},
|
||||
utils::{
|
||||
bitflags::BitflagsExt, buf::Buf, cell_ext::CellExt, clonecell::CloneCell,
|
||||
|
|
@ -71,6 +70,7 @@ use {
|
|||
mem::{self},
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
time::Duration,
|
||||
},
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
|
@ -2514,7 +2514,7 @@ struct XToWaylandTransfer {
|
|||
|
||||
impl XToWaylandTransfer {
|
||||
async fn run(mut self) {
|
||||
let timeout = Time::in_ms(5000).unwrap();
|
||||
let timeout = self.state.now() + Duration::from_millis(5000);
|
||||
let mut pos = 0;
|
||||
while pos < self.data.len() {
|
||||
let res = self
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ impl Parser for ColorParser {
|
|||
3 => (s(0..1)?, s(1..2)?, s(2..3)?, u8::MAX),
|
||||
4 => (s(0..1)?, s(1..2)?, s(2..3)?, s(3..4)?),
|
||||
6 => (d(0..2)?, d(2..4)?, d(4..6)?, u8::MAX),
|
||||
8 => (d(0..2)?, d(2..4)?, d(4..6)?, d(4..8)?),
|
||||
8 => (d(0..2)?, d(2..4)?, d(4..6)?, d(6..8)?),
|
||||
_ => return Err(ColorParserError::Length.spanned(span)),
|
||||
};
|
||||
Ok(Color::new_straight(r, g, b, a))
|
||||
|
|
|
|||
18
wire/jay_damage_tracking.txt
Normal file
18
wire/jay_damage_tracking.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
request destroy {
|
||||
|
||||
}
|
||||
|
||||
request set_visualizer_enabled {
|
||||
enabled: u32,
|
||||
}
|
||||
|
||||
request set_visualizer_color {
|
||||
r: pod(f32),
|
||||
g: pod(f32),
|
||||
b: pod(f32),
|
||||
a: pod(f32),
|
||||
}
|
||||
|
||||
request set_visualizer_decay {
|
||||
millis: pod(u64),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue