1
0
Fork 0
forked from wry/wry

autocommit 2022-04-27 20:37:49 CEST

This commit is contained in:
Julian Orth 2022-04-27 20:37:49 +02:00
parent 57899b3f35
commit 324eb835bb
24 changed files with 478 additions and 68 deletions

View file

@ -33,6 +33,10 @@ pub trait Backend {
fn is_freestanding(&self) -> bool { fn is_freestanding(&self) -> bool {
false false
} }
fn supports_presentation_feedback(&self) -> bool {
false
}
} }
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]

View file

@ -187,6 +187,10 @@ impl Backend for MetalBackend {
fn is_freestanding(&self) -> bool { fn is_freestanding(&self) -> bool {
true true
} }
fn supports_presentation_feedback(&self) -> bool {
true
}
} }
fn dup_async_fd(state: &Rc<State>, fd: c::c_int) -> Result<AsyncFd, MetalError> { fn dup_async_fd(state: &Rc<State>, fd: c::c_int) -> Result<AsyncFd, MetalError> {

View file

@ -1,17 +1,19 @@
use { use {
crate::{ crate::{
async_engine::{AsyncFd, SpawnedFuture}, async_engine::{AsyncFd, Phase, SpawnedFuture},
backend::{ backend::{
BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, MonitorInfo, BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, MonitorInfo,
}, },
backends::metal::{DrmId, MetalBackend, MetalError}, backends::metal::{DrmId, MetalBackend, MetalError},
edid::Descriptor, edid::Descriptor,
format::{Format, XRGB8888}, format::{Format, XRGB8888},
render::{Framebuffer, RenderContext}, ifs::wp_presentation_feedback::ExecutedPresentation,
render::{Framebuffer, RenderContext, RenderResult},
state::State, state::State,
utils::{ utils::{
bitflags::BitflagsExt, clonecell::CloneCell, debug_fn::debug_fn, errorfmt::ErrorFmt, asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell,
numcell::NumCell, oserror::OsError, syncqueue::SyncQueue, debug_fn::debug_fn, errorfmt::ErrorFmt, numcell::NumCell, oserror::OsError,
syncqueue::SyncQueue,
}, },
video::{ video::{
drm::{ drm::{
@ -28,15 +30,13 @@ use {
ahash::{AHashMap, AHashSet}, ahash::{AHashMap, AHashSet},
bstr::{BString, ByteSlice}, bstr::{BString, ByteSlice},
std::{ std::{
cell::Cell, cell::{Cell, RefCell},
ffi::CString, ffi::CString,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
rc::Rc, rc::Rc,
}, },
uapi::c, uapi::c,
}; };
use crate::async_engine::Phase;
use crate::utils::asyncevent::AsyncEvent;
pub struct PendingDrmDevice { pub struct PendingDrmDevice {
pub id: DrmId, pub id: DrmId,
@ -91,6 +91,7 @@ pub struct MetalConnector {
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>, pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
pub modes: Vec<DrmModeInfo>, pub modes: Vec<DrmModeInfo>,
pub mode: CloneCell<Option<Rc<DrmModeInfo>>>, pub mode: CloneCell<Option<Rc<DrmModeInfo>>>,
pub refresh: Cell<u32>,
pub monitor_manufacturer: String, pub monitor_manufacturer: String,
pub monitor_name: String, pub monitor_name: String,
@ -120,6 +121,8 @@ pub struct MetalConnector {
pub on_change: OnChange, pub on_change: OnChange,
pub present_trigger: AsyncEvent, pub present_trigger: AsyncEvent,
pub render_result: RefCell<RenderResult>,
} }
pub struct ConnectorFutures { pub struct ConnectorFutures {
@ -179,9 +182,17 @@ impl MetalConnector {
}; };
let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()]; let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()];
if let Some(node) = self.state.root.outputs.get(&self.connector_id) { if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
buffer let mut rr = self.render_result.borrow_mut();
.egl buffer.egl.render(
.render(&*node, &self.state, Some(node.global.pos.get())); &*node,
&self.state,
Some(node.global.pos.get()),
true,
&mut rr,
);
for fr in rr.frame_requests.drain(..) {
fr.client.dispatch_frame_requests.push(fr.clone());
}
} }
let mut changes = self.master.change(); let mut changes = self.master.change();
changes.change_object(plane.id, |c| { changes.change_object(plane.id, |c| {
@ -285,7 +296,13 @@ fn get_connectors(
state: &Rc<State>, state: &Rc<State>,
dev: &Rc<MetalDrmDeviceStatic>, dev: &Rc<MetalDrmDeviceStatic>,
ids: &[DrmConnector], ids: &[DrmConnector],
) -> Result<(AHashMap<DrmConnector, Rc<MetalConnector>>, Vec<ConnectorFutures>), DrmError> { ) -> Result<
(
AHashMap<DrmConnector, Rc<MetalConnector>>,
Vec<ConnectorFutures>,
),
DrmError,
> {
let mut connectors = AHashMap::new(); let mut connectors = AHashMap::new();
let mut futures = vec![]; let mut futures = vec![];
for connector in ids { for connector in ids {
@ -385,13 +402,19 @@ fn create_connector(
serial_number = edid.base_block.id_serial_number.to_string(); serial_number = edid.base_block.id_serial_number.to_string();
} }
} }
let mode = info.modes.first().cloned().map(Rc::new);
let refresh = mode
.as_ref()
.map(|m| 1_000_000_000_000u64 / (m.refresh_rate_millihz() as u64))
.unwrap_or(0) as u32;
let slf = Rc::new(MetalConnector { let slf = Rc::new(MetalConnector {
id: connector, id: connector,
master: dev.master.clone(), master: dev.master.clone(),
state: state.clone(), state: state.clone(),
connector_id: state.connector_ids.next(), connector_id: state.connector_ids.next(),
crtcs, crtcs,
mode: CloneCell::new(info.modes.first().cloned().map(Rc::new)), mode: CloneCell::new(mode),
refresh: Cell::new(refresh),
monitor_manufacturer: manufacturer, monitor_manufacturer: manufacturer,
monitor_name: name, monitor_name: name,
monitor_serial_number: serial_number, monitor_serial_number: serial_number,
@ -411,7 +434,8 @@ fn create_connector(
crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)), crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)),
crtc: Default::default(), crtc: Default::default(),
on_change: Default::default(), on_change: Default::default(),
present_trigger: Default::default() present_trigger: Default::default(),
render_result: RefCell::new(Default::default()),
}); });
let futures = ConnectorFutures { let futures = ConnectorFutures {
present: state.eng.spawn2(Phase::Present, slf.clone().present_loop()), present: state.eng.spawn2(Phase::Present, slf.clone().present_loop()),
@ -655,7 +679,11 @@ impl MetalBackend {
let (connectors, futures) = get_connectors(&self.state, &dev, &resources.connectors)?; let (connectors, futures) = get_connectors(&self.state, &dev, &resources.connectors)?;
let slf = Rc::new(MetalDrmDevice { dev, connectors, futures }); let slf = Rc::new(MetalDrmDevice {
dev,
connectors,
futures,
});
self.init_drm_device(&slf)?; self.init_drm_device(&slf)?;
@ -782,9 +810,9 @@ impl MetalBackend {
self: &Rc<Self>, self: &Rc<Self>,
dev: &Rc<MetalDrmDevice>, dev: &Rc<MetalDrmDevice>,
crtc_id: DrmCrtc, crtc_id: DrmCrtc,
_tv_sec: u32, tv_sec: u32,
_tv_usec: u32, tv_usec: u32,
_sequence: u32, sequence: u32,
) { ) {
let crtc = match dev.dev.crtcs.get(&crtc_id) { let crtc = match dev.dev.crtcs.get(&crtc_id) {
Some(c) => c, Some(c) => c,
@ -798,6 +826,27 @@ impl MetalBackend {
if connector.has_damage.get() { if connector.has_damage.get() {
connector.schedule_present(); connector.schedule_present();
} }
{
let global = self.state.outputs.get(&connector.connector_id);
let mut rr = connector.render_result.borrow_mut();
for fb in rr.presentation_feedbacks.drain(..) {
match &global {
Some(g) => {
fb.client
.dispatch_presentation_feedback
.push(ExecutedPresentation {
feedback: fb.clone(),
output: g.node.global.clone(),
tv_sec: tv_sec as _,
tv_nsec: tv_usec * 1000,
seq: sequence as _,
refresh: connector.refresh.get(),
})
}
_ => fb.client.discard_presentation_feedback.push(fb.clone()),
}
}
}
} }
fn reset_planes(&self, dev: &MetalDrmDevice, changes: &mut Change) { fn reset_planes(&self, dev: &MetalDrmDevice, changes: &mut Change) {

View file

@ -9,7 +9,7 @@ use {
fixed::Fixed, fixed::Fixed,
format::XRGB8888, format::XRGB8888,
ifs::wl_seat::PX_PER_SCROLL, ifs::wl_seat::PX_PER_SCROLL,
render::{Framebuffer, RenderContext, RenderError}, render::{Framebuffer, RenderContext, RenderError, RenderResult},
state::State, state::State,
utils::{ utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
@ -53,6 +53,7 @@ use {
collections::VecDeque, collections::VecDeque,
error::Error, error::Error,
future::pending, future::pending,
ops::DerefMut,
rc::Rc, rc::Rc,
}, },
thiserror::Error, thiserror::Error,
@ -222,6 +223,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
root, root,
scheduled_present: Default::default(), scheduled_present: Default::default(),
grab_requests: Default::default(), grab_requests: Default::default(),
render_result: Default::default(),
}); });
data.add_output().await?; data.add_output().await?;
@ -250,6 +252,7 @@ pub struct XBackend {
root: u32, root: u32,
scheduled_present: AsyncQueue<Rc<XOutput>>, scheduled_present: AsyncQueue<Rc<XOutput>>,
grab_requests: AsyncQueue<(Rc<XSeat>, bool)>, grab_requests: AsyncQueue<(Rc<XSeat>, bool)>,
render_result: RefCell<RenderResult>,
} }
impl XBackend { impl XBackend {
@ -673,8 +676,18 @@ impl XBackend {
image.last_serial.set(serial); image.last_serial.set(serial);
if let Some(node) = self.state.root.outputs.get(&output.id) { if let Some(node) = self.state.root.outputs.get(&output.id) {
let mut rr = self.render_result.borrow_mut();
let fb = image.fb.get(); let fb = image.fb.get();
fb.render(&*node, &self.state, Some(node.global.pos.get())); fb.render(
&*node,
&self.state,
Some(node.global.pos.get()),
true,
rr.deref_mut(),
);
for fr in rr.frame_requests.drain(..) {
fr.client.dispatch_frame_requests.push(fr.clone());
}
} }
let pp = PresentPixmap { let pp = PresentPixmap {

View file

@ -3,7 +3,12 @@ use {
crate::{ crate::{
async_engine::{AsyncFd, SpawnedFuture}, async_engine::{AsyncFd, SpawnedFuture},
client::{error::LookupError, objects::Objects}, client::{error::LookupError, objects::Objects},
ifs::{wl_callback::WlCallback, wl_display::WlDisplay, wl_registry::WlRegistry}, ifs::{
wl_callback::WlCallback,
wl_display::WlDisplay,
wl_registry::WlRegistry,
wp_presentation_feedback::{ExecutedPresentation, WpPresentationFeedback},
},
leaks::Tracker, leaks::Tracker,
object::{Interface, Object, ObjectId, WL_DISPLAY_ID}, object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
state::State, state::State,
@ -132,6 +137,8 @@ impl Clients {
flush_request: Default::default(), flush_request: Default::default(),
shutdown: Default::default(), shutdown: Default::default(),
dispatch_frame_requests: AsyncQueue::new(), dispatch_frame_requests: AsyncQueue::new(),
discard_presentation_feedback: Default::default(),
dispatch_presentation_feedback: Default::default(),
tracker: Default::default(), tracker: Default::default(),
is_xwayland, is_xwayland,
secure, secure,
@ -239,6 +246,8 @@ pub struct Client {
flush_request: AsyncEvent, flush_request: AsyncEvent,
shutdown: AsyncEvent, shutdown: AsyncEvent,
pub dispatch_frame_requests: AsyncQueue<Rc<WlCallback>>, pub dispatch_frame_requests: AsyncQueue<Rc<WlCallback>>,
pub discard_presentation_feedback: AsyncQueue<Rc<WpPresentationFeedback>>,
pub dispatch_presentation_feedback: AsyncQueue<ExecutedPresentation>,
pub tracker: Tracker<Client>, pub tracker: Tracker<Client>,
pub is_xwayland: bool, pub is_xwayland: bool,
pub secure: bool, pub secure: bool,

View file

@ -2,6 +2,7 @@ use {
crate::{ crate::{
async_engine::Phase, async_engine::Phase,
client::{Client, ClientError}, client::{Client, ClientError},
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
object::ObjectId, object::ObjectId,
utils::{ utils::{
buffd::{BufFdIn, BufFdOut, MsgParser}, buffd::{BufFdIn, BufFdOut, MsgParser},
@ -16,6 +17,8 @@ use {
pub async fn client(data: Rc<Client>) { pub async fn client(data: Rc<Client>) {
let mut recv = data.state.eng.spawn(receive(data.clone())).fuse(); let mut recv = data.state.eng.spawn(receive(data.clone())).fuse();
let mut dispatch_fr = data.state.eng.spawn(dispatch_fr(data.clone())).fuse(); let mut dispatch_fr = data.state.eng.spawn(dispatch_fr(data.clone())).fuse();
let discard_fb = data.state.eng.spawn(discard_fb(data.clone())).fuse();
let dispatch_fb = data.state.eng.spawn(dispatch_fb(data.clone())).fuse();
let mut shutdown = data.shutdown.triggered().fuse(); let mut shutdown = data.shutdown.triggered().fuse();
let _send = data.state.eng.spawn2(Phase::PostLayout, send(data.clone())); let _send = data.state.eng.spawn2(Phase::PostLayout, send(data.clone()));
select! { select! {
@ -25,6 +28,8 @@ pub async fn client(data: Rc<Client>) {
} }
drop(recv); drop(recv);
drop(dispatch_fr); drop(dispatch_fr);
drop(discard_fb);
drop(dispatch_fb);
data.flush_request.trigger(); data.flush_request.trigger();
match data.state.eng.timeout(5000) { match data.state.eng.timeout(5000) {
Ok(timeout) => { Ok(timeout) => {
@ -56,6 +61,38 @@ async fn dispatch_fr(data: Rc<Client>) {
} }
} }
async fn discard_fb(data: Rc<Client>) {
loop {
data.discard_presentation_feedback.non_empty().await;
while let Some(fr) = data.discard_presentation_feedback.try_pop() {
fr.send_discarded();
let _ = data.remove_obj(&*fr);
}
}
}
async fn dispatch_fb(data: Rc<Client>) {
loop {
data.dispatch_presentation_feedback.non_empty().await;
while let Some(fr) = data.dispatch_presentation_feedback.try_pop() {
let bindings = fr.output.bindings.borrow_mut();
if let Some(bindings) = bindings.get(&data.id) {
for binding in bindings.values() {
fr.feedback.send_sync_output(binding);
}
}
fr.feedback.send_presented(
fr.tv_sec,
fr.tv_nsec,
fr.refresh,
fr.seq,
KIND_VSYNC | KIND_HW_COMPLETION,
);
let _ = data.remove_obj(&*fr.feedback);
}
}
}
async fn receive(data: Rc<Client>) { async fn receive(data: Rc<Client>) {
let display = data.display().unwrap(); let display = data.display().unwrap();
let recv = async { let recv = async {

View file

@ -16,6 +16,7 @@ use {
wl_seat::WlSeatGlobal, wl_seat::WlSeatGlobal,
wl_shm::WlShmGlobal, wl_shm::WlShmGlobal,
wl_subcompositor::WlSubcompositorGlobal, wl_subcompositor::WlSubcompositorGlobal,
wp_presentation::WpPresentationGlobal,
xdg_wm_base::XdgWmBaseGlobal, xdg_wm_base::XdgWmBaseGlobal,
zwlr_layer_shell_v1::ZwlrLayerShellV1Global, zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1Global, zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1Global,
@ -135,6 +136,10 @@ impl Globals {
if backend.supports_idle() { if backend.supports_idle() {
add_singleton!(ZwpIdleInhibitManagerV1Global); add_singleton!(ZwpIdleInhibitManagerV1Global);
} }
if backend.supports_presentation_feedback() {
add_singleton!(WpPresentationGlobal);
}
} }
pub fn name(&self) -> GlobalName { pub fn name(&self) -> GlobalName {

View file

@ -18,6 +18,8 @@ pub mod wl_shm;
pub mod wl_shm_pool; pub mod wl_shm_pool;
pub mod wl_subcompositor; pub mod wl_subcompositor;
pub mod wl_surface; pub mod wl_surface;
pub mod wp_presentation;
pub mod wp_presentation_feedback;
pub mod xdg_positioner; pub mod xdg_positioner;
pub mod xdg_wm_base; pub mod xdg_wm_base;
pub mod zwlr_layer_shell_v1; pub mod zwlr_layer_shell_v1;

View file

@ -10,8 +10,8 @@ use {
}; };
pub struct WlCallback { pub struct WlCallback {
client: Rc<Client>, pub client: Rc<Client>,
id: WlCallbackId, pub id: WlCallbackId,
pub tracker: Tracker<Self>, pub tracker: Tracker<Self>,
} }

View file

@ -18,6 +18,7 @@ use {
cursor::CursorSurface, wl_subsurface::WlSubsurface, xdg_surface::XdgSurfaceError, cursor::CursorSurface, wl_subsurface::WlSubsurface, xdg_surface::XdgSurfaceError,
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error,
}, },
wp_presentation_feedback::WpPresentationFeedback,
}, },
leaks::Tracker, leaks::Tracker,
object::Object, object::Object,
@ -97,6 +98,7 @@ pub struct WlSurface {
pub children: RefCell<Option<Box<ParentData>>>, pub children: RefCell<Option<Box<ParentData>>>,
ext: CloneCell<Rc<dyn SurfaceExt>>, ext: CloneCell<Rc<dyn SurfaceExt>>,
pub frame_requests: RefCell<Vec<Rc<WlCallback>>>, pub frame_requests: RefCell<Vec<Rc<WlCallback>>>,
pub presentation_feedback: RefCell<Vec<Rc<WpPresentationFeedback>>>,
seat_state: NodeSeatState, seat_state: NodeSeatState,
toplevel: CloneCell<Option<Rc<dyn ToplevelNode>>>, toplevel: CloneCell<Option<Rc<dyn ToplevelNode>>>,
cursors: SmallMap<SeatId, Rc<CursorSurface>, 1>, cursors: SmallMap<SeatId, Rc<CursorSurface>, 1>,
@ -181,6 +183,7 @@ struct PendingState {
input_region: Cell<Option<Option<Rc<Region>>>>, input_region: Cell<Option<Option<Rc<Region>>>>,
frame_request: RefCell<Vec<Rc<WlCallback>>>, frame_request: RefCell<Vec<Rc<WlCallback>>>,
damage: Cell<bool>, damage: Cell<bool>,
presentation_feedback: RefCell<Vec<Rc<WpPresentationFeedback>>>,
} }
#[derive(Default)] #[derive(Default)]
@ -215,6 +218,7 @@ impl WlSurface {
children: Default::default(), children: Default::default(),
ext: CloneCell::new(client.state.none_surface_ext.clone()), ext: CloneCell::new(client.state.none_surface_ext.clone()),
frame_requests: Default::default(), frame_requests: Default::default(),
presentation_feedback: Default::default(),
seat_state: Default::default(), seat_state: Default::default(),
toplevel: Default::default(), toplevel: Default::default(),
cursors: Default::default(), cursors: Default::default(),
@ -236,6 +240,13 @@ impl WlSurface {
} }
} }
pub fn add_presentation_feedback(&self, fb: &Rc<WpPresentationFeedback>) {
self.pending
.presentation_feedback
.borrow_mut()
.push(fb.clone());
}
pub fn is_cursor(&self) -> bool { pub fn is_cursor(&self) -> bool {
self.role.get() == SurfaceRole::Cursor self.role.get() == SurfaceRole::Cursor
} }
@ -497,6 +508,15 @@ impl WlSurface {
let mut pfr = self.pending.frame_request.borrow_mut(); let mut pfr = self.pending.frame_request.borrow_mut();
self.frame_requests.borrow_mut().extend(pfr.drain(..)); self.frame_requests.borrow_mut().extend(pfr.drain(..));
} }
{
let mut fbs = self.presentation_feedback.borrow_mut();
for fb in fbs.drain(..) {
fb.send_discarded();
let _ = self.client.remove_obj(&*fb);
}
let mut pfbs = self.pending.presentation_feedback.borrow_mut();
mem::swap(fbs.deref_mut(), pfbs.deref_mut());
}
{ {
if let Some(region) = self.pending.input_region.take() { if let Some(region) = self.pending.input_region.take() {
self.input_region.set(region); self.input_region.set(region);

View file

@ -328,21 +328,6 @@ impl XdgToplevel {
Ok(()) Ok(())
} }
fn notify_parent(&self) {
let parent = match self.toplevel_data.parent.get() {
Some(p) => p,
_ => return,
};
let extents = self.xdg.extents.get();
parent.clone().node_child_active_changed(
self,
self.toplevel_data.active_children.get() > 0,
1,
);
parent.node_child_size_changed(self, extents.width(), extents.height());
parent.node_child_title_changed(self, self.toplevel_data.title.borrow_mut().deref());
}
fn map_floating(self: &Rc<Self>, workspace: &Rc<WorkspaceNode>) { fn map_floating(self: &Rc<Self>, workspace: &Rc<WorkspaceNode>) {
let (width, height) = self.toplevel_data.float_size(workspace); let (width, height) = self.toplevel_data.float_size(workspace);
self.state self.state
@ -460,9 +445,6 @@ impl ToplevelNode for XdgToplevel {
} }
fn tl_set_active(&self, active: bool) { fn tl_set_active(&self, active: bool) {
if let Some(parent) = self.toplevel_data.parent.get() {
parent.node_child_active_changed(self, active, 1);
}
let changed = { let changed = {
let mut states = self.states.borrow_mut(); let mut states = self.states.borrow_mut();
match active { match active {
@ -587,10 +569,8 @@ impl XdgSurfaceExt for XdgToplevel {
} }
fn extents_changed(&self) { fn extents_changed(&self) {
self.notify_parent(); self.toplevel_data.pos.set(self.xdg.extents.get());
if self.toplevel_data.parent.get().is_some() { self.tl_extents_changed();
self.state.tree_changed();
}
} }
} }

View file

@ -307,7 +307,8 @@ impl SurfaceExt for Xwindow {
} }
fn extents_changed(&self) { fn extents_changed(&self) {
self.tl_notify_parent(); self.toplevel_data.pos.set(self.surface.extents.get());
self.tl_extents_changed();
} }
} }
@ -391,12 +392,6 @@ impl ToplevelNode for Xwindow {
&& self.data.info.input_model.get() != XInputModel::None && self.data.info.input_model.get() != XInputModel::None
} }
fn tl_set_active(&self, active: bool) {
if let Some(pn) = self.toplevel_data.parent.get() {
pn.node_child_active_changed(self, active, 1);
}
}
fn tl_on_activate(&self) { fn tl_on_activate(&self) {
self.data self.data
.state .state

View file

@ -307,6 +307,7 @@ impl ZwlrLayerSurfaceV1 {
self.mapped.set(false); self.mapped.set(false);
self.surface.destroy_node(); self.surface.destroy_node();
self.seat_state.destroy_node(self); self.seat_state.destroy_node(self);
self.client.state.tree_changed();
} }
} }

116
src/ifs/wp_presentation.rs Normal file
View file

@ -0,0 +1,116 @@
pub use crate::wire::{wp_presentation::*, WpPresentationId};
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName},
ifs::wp_presentation_feedback::WpPresentationFeedback,
leaks::Tracker,
object::Object,
utils::buffd::{MsgParser, MsgParserError},
},
std::rc::Rc,
thiserror::Error,
uapi::c,
};
pub struct WpPresentationGlobal {
pub name: GlobalName,
}
impl WpPresentationGlobal {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: WpPresentationId,
client: &Rc<Client>,
_version: u32,
) -> Result<(), WpPresentationError> {
let obj = Rc::new(WpPresentation {
id,
client: client.clone(),
tracker: Default::default(),
});
track!(client, obj);
client.add_client_obj(&obj)?;
obj.send_clock_id();
Ok(())
}
}
global_base!(WpPresentationGlobal, WpPresentation, WpPresentationError);
impl Global for WpPresentationGlobal {
fn singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
1
}
}
simple_add_global!(WpPresentationGlobal);
pub struct WpPresentation {
pub id: WpPresentationId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
}
impl WpPresentation {
fn send_clock_id(&self) {
self.client.event(ClockId {
self_id: self.id,
clk_id: c::CLOCK_MONOTONIC as _,
});
}
pub fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpPresentationError> {
let _req: Destroy = self.client.parse(self, parser)?;
self.client.remove_obj(self)?;
Ok(())
}
pub fn feedback(&self, parser: MsgParser<'_, '_>) -> Result<(), WpPresentationError> {
let req: Feedback = self.client.parse(self, parser)?;
let surface = self.client.lookup(req.surface)?;
let fb = Rc::new(WpPresentationFeedback {
id: req.callback,
client: self.client.clone(),
surface: surface.clone(),
tracker: Default::default(),
});
track!(self.client, fb);
self.client.add_client_obj(&fb)?;
surface.add_presentation_feedback(&fb);
Ok(())
}
}
object_base2! {
WpPresentation;
DESTROY => destroy,
FEEDBACK => feedback,
}
impl Object for WpPresentation {
fn num_requests(&self) -> u32 {
FEEDBACK + 1
}
}
simple_add_obj!(WpPresentation);
#[derive(Debug, Error)]
pub enum WpPresentationError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(WpPresentationError, MsgParserError);
efrom!(WpPresentationError, ClientError);

View file

@ -0,0 +1,78 @@
use {
crate::{
client::Client,
ifs::{
wl_output::{WlOutput, WlOutputGlobal},
wl_surface::WlSurface,
},
leaks::Tracker,
object::Object,
wire::{wp_presentation_feedback::*, WpPresentationFeedbackId},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpPresentationFeedback {
pub id: WpPresentationFeedbackId,
pub client: Rc<Client>,
pub surface: Rc<WlSurface>,
pub tracker: Tracker<Self>,
}
pub struct ExecutedPresentation {
pub feedback: Rc<WpPresentationFeedback>,
pub output: Rc<WlOutputGlobal>,
pub tv_sec: u64,
pub tv_nsec: u32,
pub seq: u64,
pub refresh: u32,
}
pub const KIND_VSYNC: u32 = 0x1;
#[allow(dead_code)]
pub const KIND_HW_CLOCK: u32 = 0x2;
pub const KIND_HW_COMPLETION: u32 = 0x4;
#[allow(dead_code)]
pub const KIND_ZERO_COPY: u32 = 0x8;
impl WpPresentationFeedback {
pub fn send_sync_output(&self, output: &WlOutput) {
self.client.event(SyncOutput {
self_id: self.id,
output: output.id,
});
}
pub fn send_presented(&self, tv_sec: u64, tv_nsec: u32, refresh: u32, seq: u64, flags: u32) {
self.client.event(Presented {
self_id: self.id,
tv_sec_hi: (tv_sec >> 32) as u32,
tv_sec_lo: tv_sec as u32,
tv_nsec,
refresh,
seq_hi: (seq >> 32) as u32,
seq_lo: seq as u32,
flags,
});
}
pub fn send_discarded(&self) {
self.client.event(Discarded { self_id: self.id });
}
}
object_base2! {
WpPresentationFeedback;
}
impl Object for WpPresentationFeedback {
fn num_requests(&self) -> u32 {
0
}
}
simple_add_obj!(WpPresentationFeedback);
#[derive(Debug, Error)]
pub enum WpPresentationFeedbackError {}

View file

@ -11,6 +11,7 @@ use {
}, },
renderer::{context::RenderContext, renderer::Renderer}, renderer::{context::RenderContext, renderer::Renderer},
sys::{glBlendFunc, glFlush, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, sys::{glBlendFunc, glFlush, GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
RenderResult,
}, },
state::State, state::State,
tree::Node, tree::Node,
@ -45,7 +46,14 @@ impl Framebuffer {
}); });
} }
pub fn render(&self, node: &dyn Node, state: &State, cursor_rect: Option<Rect>) { pub fn render(
&self,
node: &dyn Node,
state: &State,
cursor_rect: Option<Rect>,
on_output: bool,
result: &mut RenderResult,
) {
let _ = self.ctx.ctx.with_current(|| { let _ = self.ctx.ctx.with_current(|| {
let c = state.theme.background_color.get(); let c = state.theme.background_color.get();
unsafe { unsafe {
@ -59,6 +67,8 @@ impl Framebuffer {
ctx: &self.ctx, ctx: &self.ctx,
fb: &self.gl, fb: &self.gl,
state, state,
on_output,
result,
}; };
node.node_render(&mut renderer, 0, 0); node.node_render(&mut renderer, 0, 0);
if let Some(rect) = cursor_rect { if let Some(rect) = cursor_rect {

View file

@ -3,9 +3,11 @@ use {
format::{Format, ARGB8888}, format::{Format, ARGB8888},
ifs::{ ifs::{
wl_buffer::WlBuffer, wl_buffer::WlBuffer,
wl_callback::WlCallback,
wl_surface::{ wl_surface::{
xdg_surface::XdgSurface, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, WlSurface, xdg_surface::XdgSurface, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, WlSurface,
}, },
wp_presentation_feedback::WpPresentationFeedback,
}, },
rect::Rect, rect::Rect,
render::{ render::{
@ -31,13 +33,32 @@ use {
}, },
utils::rc_eq::rc_eq, utils::rc_eq::rc_eq,
}, },
std::{ops::Deref, rc::Rc, slice}, std::{
fmt::{Debug, Formatter},
ops::Deref,
rc::Rc,
slice,
},
}; };
#[derive(Default)]
pub struct RenderResult {
pub frame_requests: Vec<Rc<WlCallback>>,
pub presentation_feedbacks: Vec<Rc<WpPresentationFeedback>>,
}
impl Debug for RenderResult {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RenderResult").finish_non_exhaustive()
}
}
pub struct Renderer<'a> { pub struct Renderer<'a> {
pub(super) ctx: &'a Rc<RenderContext>, pub(super) ctx: &'a Rc<RenderContext>,
pub(super) fb: &'a GlFrameBuffer, pub(super) fb: &'a GlFrameBuffer,
pub(super) state: &'a State, pub(super) state: &'a State,
pub(super) on_output: bool,
pub(super) result: &'a mut RenderResult,
} }
impl Renderer<'_> { impl Renderer<'_> {
@ -271,9 +292,15 @@ impl Renderer<'_> {
} else { } else {
self.render_buffer(&buffer, x, y); self.render_buffer(&buffer, x, y);
} }
let mut fr = surface.frame_requests.borrow_mut(); if self.on_output {
for cb in fr.drain(..) { {
surface.client.dispatch_frame_requests.push(cb); let mut fr = surface.frame_requests.borrow_mut();
self.result.frame_requests.extend(fr.drain(..));
}
{
let mut fbs = surface.presentation_feedback.borrow_mut();
self.result.presentation_feedbacks.extend(fbs.drain(..));
}
} }
} }

View file

@ -53,7 +53,13 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR,
)?; )?;
let fb = ctx.dmabuf_fb(bo.dmabuf())?; let fb = ctx.dmabuf_fb(bo.dmabuf())?;
fb.render(state.root.deref(), state, Some(state.root.extents.get())); fb.render(
state.root.deref(),
state,
Some(state.root.extents.get()),
false,
&mut Default::default(),
);
let drm = ctx.gbm.drm.dup_render()?.fd().clone(); let drm = ctx.gbm.drm.dup_render()?.fd().clone();
Ok(Screenshot { drm, bo }) Ok(Screenshot { drm, bo })
} }

View file

@ -1,4 +1,3 @@
use std::fmt::{Debug, Formatter};
use { use {
crate::{ crate::{
async_engine::{AsyncEngine, SpawnedFuture}, async_engine::{AsyncEngine, SpawnedFuture},
@ -42,6 +41,7 @@ use {
jay_config::Direction, jay_config::Direction,
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
fmt::{Debug, Formatter},
num::Wrapping, num::Wrapping,
rc::Rc, rc::Rc,
sync::Arc, sync::Arc,

View file

@ -351,6 +351,7 @@ impl ContainerNode {
} else { } else {
self.perform_split_layout(); self.perform_split_layout();
} }
self.state.tree_changed();
self.schedule_compute_render_data(); self.schedule_compute_render_data();
} }

View file

@ -38,13 +38,20 @@ pub trait ToplevelNode: Node {
} }
fn tl_surface_active_changed(&self, active: bool) { fn tl_surface_active_changed(&self, active: bool) {
let data = self.tl_data();
if active { if active {
if self.tl_data().active_children.fetch_add(1) == 0 { if data.active_children.fetch_add(1) == 0 {
self.tl_set_active(true); self.tl_set_active(true);
if let Some(parent) = data.parent.get() {
parent.node_child_active_changed(self.tl_as_node(), true, 1);
}
} }
} else { } else {
if self.tl_data().active_children.fetch_sub(1) == 1 { if data.active_children.fetch_sub(1) == 1 {
self.tl_set_active(false); self.tl_set_active(false);
if let Some(parent) = data.parent.get() {
parent.node_child_active_changed(self.tl_as_node(), false, 1);
}
} }
} }
} }
@ -83,7 +90,9 @@ pub trait ToplevelNode: Node {
let data = self.tl_data(); let data = self.tl_data();
data.parent.set(Some(parent.clone())); data.parent.set(Some(parent.clone()));
data.is_floating.set(parent.node_is_float()); data.is_floating.set(parent.node_is_float());
self.tl_notify_parent(); self.tl_extents_changed();
self.tl_title_changed();
self.tl_active_changed();
self.tl_after_parent_set(parent); self.tl_after_parent_set(parent);
} }
@ -91,14 +100,13 @@ pub trait ToplevelNode: Node {
let _ = parent; let _ = parent;
} }
fn tl_notify_parent(&self) { fn tl_active_changed(&self) {
let data = self.tl_data(); let data = self.tl_data();
let parent = match data.parent.get() { let parent = match data.parent.get() {
Some(p) => p, Some(p) => p,
_ => return, _ => return,
}; };
let node = self.tl_as_node(); let node = self.tl_as_node();
let pos = data.pos.get();
let depth = if data.active.get() { let depth = if data.active.get() {
1 1
} else if data.active_children.get() > 0 { } else if data.active_children.get() > 0 {
@ -109,10 +117,18 @@ pub trait ToplevelNode: Node {
if depth > 0 { if depth > 0 {
parent.clone().node_child_active_changed(node, true, depth); parent.clone().node_child_active_changed(node, true, depth);
} }
}
fn tl_extents_changed(&self) {
let data = self.tl_data();
let parent = match data.parent.get() {
Some(p) => p,
_ => return,
};
let node = self.tl_as_node();
let pos = data.pos.get();
parent.node_child_size_changed(node, pos.width(), pos.height()); parent.node_child_size_changed(node, pos.width(), pos.height());
parent data.state.tree_changed();
.clone()
.node_child_title_changed(node, data.title.borrow_mut().deref());
} }
fn tl_set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) { fn tl_set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
@ -253,6 +269,7 @@ impl ToplevelData {
placeholder, placeholder,
workspace: ws.clone(), workspace: ws.clone(),
}); });
drop(data);
self.is_fullscreen.set(true); self.is_fullscreen.set(true);
ws.fullscreen.set(Some(node.clone())); ws.fullscreen.set(Some(node.clone()));
node.tl_set_parent(ws.clone()); node.tl_set_parent(ws.clone());

View file

@ -1,8 +1,8 @@
use std::fmt::{Debug, Formatter};
use { use {
crate::utils::numcell::NumCell, crate::utils::numcell::NumCell,
std::{ std::{
cell::Cell, cell::Cell,
fmt::{Debug, Formatter},
future::Future, future::Future,
pin::Pin, pin::Pin,
task::{Context, Poll, Waker}, task::{Context, Poll, Waker},
@ -17,7 +17,9 @@ pub struct AsyncEvent {
impl Debug for AsyncEvent { impl Debug for AsyncEvent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncEvent").field("triggers", &self.triggers.get()).finish_non_exhaustive() f.debug_struct("AsyncEvent")
.field("triggers", &self.triggers.get())
.finish_non_exhaustive()
} }
} }

15
wire/wp_presentation.txt Normal file
View file

@ -0,0 +1,15 @@
# requests
msg destroy = 0 {
}
msg feedback = 1 {
surface: id(wl_surface),
callback: id(wp_presentation_feedback),
}
# events
msg clock_id = 0 {
clk_id: u32,
}

View file

@ -0,0 +1,19 @@
# events
msg sync_output = 0 {
output: id(wl_output),
}
msg presented = 1 {
tv_sec_hi : u32,
tv_sec_lo : u32,
tv_nsec : u32,
refresh : u32,
seq_hi : u32,
seq_lo : u32,
flags : u32,
}
msg discarded = 2 {
}