all: implement hardware cursors
This commit is contained in:
parent
6cc97ee56e
commit
3b8935cf55
23 changed files with 614 additions and 91 deletions
|
|
@ -363,6 +363,13 @@ impl Client {
|
|||
self.send(&ClientMessage::SetCursorSize { seat, size })
|
||||
}
|
||||
|
||||
pub fn set_use_hardware_cursor(&self, seat: Seat, use_hardware_cursor: bool) {
|
||||
self.send(&ClientMessage::SetUseHardwareCursor {
|
||||
seat,
|
||||
use_hardware_cursor,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_size(&self, sized: Resizable, size: i32) {
|
||||
self.send(&ClientMessage::SetSize { sized, size })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,6 +300,10 @@ pub enum ClientMessage<'a> {
|
|||
device: InputDevice,
|
||||
enabled: bool,
|
||||
},
|
||||
SetUseHardwareCursor {
|
||||
seat: Seat,
|
||||
use_hardware_cursor: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Debug)]
|
||||
|
|
|
|||
|
|
@ -128,6 +128,16 @@ impl Seat {
|
|||
Self(raw)
|
||||
}
|
||||
|
||||
/// Sets whether this seat's cursor uses the hardware cursor if available.
|
||||
///
|
||||
/// Only one seat at a time can use the hardware cursor. Setting this to `true` for a
|
||||
/// seat automatically unsets it for all other seats.
|
||||
///
|
||||
/// By default, the first created seat uses the hardware cursor.
|
||||
pub fn use_hardware_cursor(self, use_hardware_cursor: bool) {
|
||||
get!().set_use_hardware_cursor(self, use_hardware_cursor);
|
||||
}
|
||||
|
||||
/// Sets the size of the cursor theme.
|
||||
///
|
||||
/// Default: 16.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use {
|
|||
async_engine::SpawnedFuture,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
||||
render::Framebuffer,
|
||||
video::drm::ConnectorType,
|
||||
},
|
||||
std::{
|
||||
|
|
@ -85,11 +86,21 @@ pub trait Connector {
|
|||
#[derive(Debug)]
|
||||
pub enum ConnectorEvent {
|
||||
Connected(MonitorInfo),
|
||||
HardwareCursor(Option<Rc<dyn HardwareCursor>>),
|
||||
Disconnected,
|
||||
Removed,
|
||||
ModeChanged(Mode),
|
||||
}
|
||||
|
||||
pub trait HardwareCursor: Debug {
|
||||
fn set_enabled(&self, enabled: bool);
|
||||
fn get_buffer(&self) -> Rc<Framebuffer>;
|
||||
fn set_position(&self, x: i32, y: i32);
|
||||
fn swap_buffer(&self);
|
||||
fn commit(&self);
|
||||
fn max_size(&self) -> (i32, i32);
|
||||
}
|
||||
|
||||
pub type TransformMatrix = [[f64; 2]; 2];
|
||||
|
||||
pub trait InputDevice {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ use {
|
|||
async_engine::{Phase, SpawnedFuture},
|
||||
backend::{
|
||||
BackendDrmDevice, BackendEvent, Connector, ConnectorEvent, ConnectorId,
|
||||
ConnectorKernelId, DrmDeviceId, MonitorInfo,
|
||||
ConnectorKernelId, DrmDeviceId, HardwareCursor, MonitorInfo,
|
||||
},
|
||||
backends::metal::{MetalBackend, MetalError},
|
||||
edid::Descriptor,
|
||||
format::{Format, XRGB8888},
|
||||
format::{Format, ARGB8888, XRGB8888},
|
||||
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
||||
render::{Framebuffer, RenderContext, RenderResult, ResetStatus, Texture},
|
||||
state::State,
|
||||
|
|
@ -66,6 +66,8 @@ pub struct MetalDrmDevice {
|
|||
pub max_width: u32,
|
||||
pub min_height: u32,
|
||||
pub max_height: u32,
|
||||
pub cursor_width: u64,
|
||||
pub cursor_height: u64,
|
||||
pub gbm: GbmDevice,
|
||||
pub handle_events: HandleEvents,
|
||||
}
|
||||
|
|
@ -153,12 +155,14 @@ pub struct MetalConnector {
|
|||
|
||||
pub can_present: Cell<bool>,
|
||||
pub has_damage: Cell<bool>,
|
||||
pub cursor_changed: Cell<bool>,
|
||||
|
||||
pub display: RefCell<ConnectorDisplayData>,
|
||||
|
||||
pub connect_sent: Cell<bool>,
|
||||
|
||||
pub primary_plane: CloneCell<Option<Rc<MetalPlane>>>,
|
||||
pub cursor_plane: CloneCell<Option<Rc<MetalPlane>>>,
|
||||
|
||||
pub crtc: CloneCell<Option<Rc<MetalCrtc>>>,
|
||||
|
||||
|
|
@ -167,6 +171,77 @@ pub struct MetalConnector {
|
|||
pub present_trigger: AsyncEvent,
|
||||
|
||||
pub render_result: RefCell<RenderResult>,
|
||||
|
||||
pub cursor_generation: NumCell<u64>,
|
||||
pub cursor_x: Cell<i32>,
|
||||
pub cursor_y: Cell<i32>,
|
||||
pub cursor_enabled: Cell<bool>,
|
||||
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
|
||||
pub cursor_front_buffer: NumCell<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MetalHardwareCursor {
|
||||
pub generation: u64,
|
||||
pub connector: Rc<MetalConnector>,
|
||||
pub cursor_swap_buffer: Cell<bool>,
|
||||
pub cursor_enabled_pending: Cell<bool>,
|
||||
pub cursor_x_pending: Cell<i32>,
|
||||
pub cursor_y_pending: Cell<i32>,
|
||||
pub cursor_buffers: Rc<[RenderBuffer; 2]>,
|
||||
pub have_changes: Cell<bool>,
|
||||
}
|
||||
|
||||
impl HardwareCursor for MetalHardwareCursor {
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
if self.cursor_enabled_pending.replace(enabled) != enabled {
|
||||
self.have_changes.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_buffer(&self) -> Rc<Framebuffer> {
|
||||
let buffer = (self.connector.cursor_front_buffer.get() + 1) % 2;
|
||||
self.cursor_buffers[buffer].fb.clone()
|
||||
}
|
||||
|
||||
fn set_position(&self, x: i32, y: i32) {
|
||||
self.cursor_x_pending.set(x);
|
||||
self.cursor_y_pending.set(y);
|
||||
self.have_changes.set(true);
|
||||
}
|
||||
|
||||
fn swap_buffer(&self) {
|
||||
self.cursor_swap_buffer.set(true);
|
||||
self.have_changes.set(true);
|
||||
}
|
||||
|
||||
fn commit(&self) {
|
||||
if self.generation != self.connector.cursor_generation.get() {
|
||||
return;
|
||||
}
|
||||
if !self.have_changes.take() {
|
||||
return;
|
||||
}
|
||||
self.connector
|
||||
.cursor_enabled
|
||||
.set(self.cursor_enabled_pending.get());
|
||||
self.connector.cursor_x.set(self.cursor_x_pending.get());
|
||||
self.connector.cursor_y.set(self.cursor_y_pending.get());
|
||||
if self.cursor_swap_buffer.take() {
|
||||
self.connector.cursor_front_buffer.fetch_add(1);
|
||||
}
|
||||
self.connector.cursor_changed.set(true);
|
||||
if self.connector.can_present.get() {
|
||||
self.connector.schedule_present();
|
||||
}
|
||||
}
|
||||
|
||||
fn max_size(&self) -> (i32, i32) {
|
||||
(
|
||||
self.connector.dev.cursor_width as _,
|
||||
self.connector.dev.cursor_height as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConnectorFutures {
|
||||
|
|
@ -201,6 +276,27 @@ impl MetalConnector {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_hardware_cursor(self: &Rc<Self>) {
|
||||
if !self.connect_sent.get() {
|
||||
return;
|
||||
}
|
||||
let generation = self.cursor_generation.fetch_add(1) + 1;
|
||||
let hc = match self.cursor_buffers.get() {
|
||||
Some(cp) => Some(Rc::new(MetalHardwareCursor {
|
||||
generation,
|
||||
connector: self.clone(),
|
||||
cursor_swap_buffer: Cell::new(false),
|
||||
cursor_enabled_pending: Cell::new(self.cursor_enabled.get()),
|
||||
cursor_x_pending: Cell::new(self.cursor_x.get()),
|
||||
cursor_y_pending: Cell::new(self.cursor_y.get()),
|
||||
cursor_buffers: cp.clone(),
|
||||
have_changes: Cell::new(false),
|
||||
}) as _),
|
||||
_ => None,
|
||||
};
|
||||
self.send_event(ConnectorEvent::HardwareCursor(hc));
|
||||
}
|
||||
|
||||
fn connected(&self) -> bool {
|
||||
let dd = self.display.borrow_mut();
|
||||
dd.connection == ConnectorStatus::Connected && self.primary_plane.get().is_some()
|
||||
|
|
@ -218,14 +314,11 @@ impl MetalConnector {
|
|||
}
|
||||
|
||||
pub fn present(&self) {
|
||||
if !self.backend.check_render_context() {
|
||||
return;
|
||||
}
|
||||
let crtc = match self.crtc.get() {
|
||||
Some(crtc) => crtc,
|
||||
_ => return,
|
||||
};
|
||||
if !self.has_damage.get() || !self.can_present.get() {
|
||||
if (!self.has_damage.get() && !self.cursor_changed.get()) || !self.can_present.get() {
|
||||
return;
|
||||
}
|
||||
if !crtc.active.value.get() {
|
||||
|
|
@ -239,27 +332,58 @@ impl MetalConnector {
|
|||
Some(b) => b,
|
||||
_ => return,
|
||||
};
|
||||
let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()];
|
||||
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
||||
let mut rr = self.render_result.borrow_mut();
|
||||
buffer.fb.render(
|
||||
&*node,
|
||||
&self.state,
|
||||
Some(node.global.pos.get()),
|
||||
true,
|
||||
&mut rr,
|
||||
node.preferred_scale.get(),
|
||||
);
|
||||
for fr in rr.frame_requests.drain(..) {
|
||||
fr.send_done();
|
||||
let _ = fr.client.remove_obj(&*fr);
|
||||
}
|
||||
node.global.perform_screencopies(&buffer.fb, &buffer.tex);
|
||||
}
|
||||
let cursor = self.cursor_plane.get();
|
||||
let mut changes = self.master.change();
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
||||
});
|
||||
if self.has_damage.get() {
|
||||
if !self.backend.check_render_context() {
|
||||
return;
|
||||
}
|
||||
let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()];
|
||||
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
||||
let mut rr = self.render_result.borrow_mut();
|
||||
buffer.fb.render(
|
||||
&*node,
|
||||
&self.state,
|
||||
Some(node.global.pos.get()),
|
||||
true,
|
||||
&mut rr,
|
||||
node.preferred_scale.get(),
|
||||
!self.cursor_enabled.get(),
|
||||
);
|
||||
for fr in rr.frame_requests.drain(..) {
|
||||
fr.send_done();
|
||||
let _ = fr.client.remove_obj(&*fr);
|
||||
}
|
||||
node.global.perform_screencopies(&buffer.fb, &buffer.tex);
|
||||
}
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
||||
});
|
||||
}
|
||||
if self.cursor_changed.get() && cursor.is_some() {
|
||||
let plane = cursor.unwrap();
|
||||
if self.cursor_enabled.get() {
|
||||
let buffers = self.cursor_buffers.get().unwrap();
|
||||
let buffer = &buffers[self.cursor_front_buffer.get() % buffers.len()];
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
||||
c.change(plane.crtc_id.id, crtc.id.0 as _);
|
||||
c.change(plane.crtc_x.id, self.cursor_x.get() as _);
|
||||
c.change(plane.crtc_y.id, self.cursor_y.get() as _);
|
||||
c.change(plane.crtc_w.id, buffer.tex.width() as _);
|
||||
c.change(plane.crtc_h.id, buffer.tex.height() as _);
|
||||
c.change(plane.src_x.id, 0);
|
||||
c.change(plane.src_y.id, 0);
|
||||
c.change(plane.src_w.id, (buffer.tex.width() as u64) << 16);
|
||||
c.change(plane.src_h.id, (buffer.tex.height() as u64) << 16);
|
||||
});
|
||||
} else {
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, 0);
|
||||
c.change(plane.crtc_id.id, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) {
|
||||
match e {
|
||||
DrmError::Atomic(OsError(c::EACCES)) => {
|
||||
|
|
@ -270,6 +394,7 @@ impl MetalConnector {
|
|||
}
|
||||
self.can_present.set(false);
|
||||
self.has_damage.set(false);
|
||||
self.cursor_changed.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -346,6 +471,8 @@ pub struct MetalPlane {
|
|||
pub possible_crtcs: u32,
|
||||
pub formats: AHashMap<u32, &'static Format>,
|
||||
|
||||
pub assigned: Cell<bool>,
|
||||
|
||||
pub crtc_id: MutableProperty<DrmCrtc>,
|
||||
pub crtc_x: MutableProperty<i32>,
|
||||
pub crtc_y: MutableProperty<i32>,
|
||||
|
|
@ -404,12 +531,20 @@ fn create_connector(
|
|||
can_present: Cell::new(true),
|
||||
has_damage: Cell::new(true),
|
||||
primary_plane: Default::default(),
|
||||
cursor_plane: Default::default(),
|
||||
crtc: Default::default(),
|
||||
on_change: Default::default(),
|
||||
present_trigger: Default::default(),
|
||||
render_result: RefCell::new(Default::default()),
|
||||
cursor_generation: Default::default(),
|
||||
cursor_x: Cell::new(0),
|
||||
cursor_y: Cell::new(0),
|
||||
cursor_enabled: Cell::new(false),
|
||||
cursor_buffers: Default::default(),
|
||||
display: RefCell::new(display),
|
||||
connect_sent: Cell::new(false),
|
||||
cursor_changed: Cell::new(false),
|
||||
cursor_front_buffer: Default::default(),
|
||||
});
|
||||
let futures = ConnectorFutures {
|
||||
present: backend
|
||||
|
|
@ -629,6 +764,7 @@ fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, D
|
|||
src_w: props.get("SRC_W")?.map(|v| v as u32),
|
||||
src_h: props.get("SRC_H")?.map(|v| v as u32),
|
||||
in_fence_fd: props.get("IN_FENCE_FD")?.id,
|
||||
assigned: Cell::new(false),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -856,7 +992,7 @@ impl MetalBackend {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn send_connected(&self, connector: &MetalConnector, dd: &ConnectorDisplayData) {
|
||||
fn send_connected(&self, connector: &Rc<MetalConnector>, dd: &ConnectorDisplayData) {
|
||||
let mut prev_mode = None;
|
||||
let mut modes = vec![];
|
||||
for mode in dd.modes.iter().map(|m| m.to_backend()) {
|
||||
|
|
@ -874,6 +1010,7 @@ impl MetalBackend {
|
|||
height_mm: dd.mm_height as _,
|
||||
}));
|
||||
connector.connect_sent.set(true);
|
||||
connector.send_hardware_cursor();
|
||||
}
|
||||
|
||||
pub fn create_drm_device(
|
||||
|
|
@ -886,6 +1023,14 @@ impl MetalBackend {
|
|||
}
|
||||
let resources = master.get_resources()?;
|
||||
|
||||
let (cursor_width, cursor_height) = match master.get_cursor_size() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::warn!("Can't determine size of cursor planes: {}", ErrorFmt(e));
|
||||
(64, 64)
|
||||
}
|
||||
};
|
||||
|
||||
let mut planes = AHashMap::new();
|
||||
for plane in master.get_planes()? {
|
||||
match create_plane(plane, master) {
|
||||
|
|
@ -933,6 +1078,8 @@ impl MetalBackend {
|
|||
max_width: resources.max_width,
|
||||
min_height: resources.min_height,
|
||||
max_height: resources.max_height,
|
||||
cursor_width,
|
||||
cursor_height,
|
||||
gbm,
|
||||
handle_events: HandleEvents {
|
||||
handle_events: Cell::new(None),
|
||||
|
|
@ -1019,6 +1166,7 @@ impl MetalBackend {
|
|||
for connector in dev.connectors.lock().values() {
|
||||
connector.can_present.set(true);
|
||||
connector.has_damage.set(true);
|
||||
connector.cursor_changed.set(true);
|
||||
}
|
||||
if dev.unprocessed_change.get() {
|
||||
return self.handle_drm_change_(dev, false);
|
||||
|
|
@ -1083,7 +1231,7 @@ impl MetalBackend {
|
|||
_ => return,
|
||||
};
|
||||
connector.can_present.set(true);
|
||||
if connector.has_damage.get() {
|
||||
if connector.has_damage.get() || connector.cursor_changed.get() {
|
||||
connector.schedule_present();
|
||||
}
|
||||
let dd = connector.display.borrow_mut();
|
||||
|
|
@ -1123,6 +1271,7 @@ impl MetalBackend {
|
|||
continue;
|
||||
}
|
||||
plane.crtc_id.value.set(DrmCrtc::NONE);
|
||||
plane.assigned.set(false);
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.crtc_id.id, 0);
|
||||
c.change(plane.fb_id, 0);
|
||||
|
|
@ -1135,19 +1284,15 @@ impl MetalBackend {
|
|||
&self,
|
||||
dev: &MetalDrmDeviceData,
|
||||
changes: &mut Change,
|
||||
preserve: &mut Preserve,
|
||||
preserve: &Preserve,
|
||||
) {
|
||||
for connector in dev.connectors.lock().values() {
|
||||
if preserve.connectors.contains(&connector.id) {
|
||||
if let Some(pp) = connector.primary_plane.get() {
|
||||
preserve.planes.insert(pp.id);
|
||||
}
|
||||
if let Some(crtc) = connector.crtc.get() {
|
||||
preserve.crtcs.insert(crtc.id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
connector.primary_plane.set(None);
|
||||
connector.cursor_plane.set(None);
|
||||
connector.cursor_enabled.set(false);
|
||||
connector.crtc.set(None);
|
||||
let dd = connector.display.borrow_mut();
|
||||
dd.crtc_id.value.set(DrmCrtc::NONE);
|
||||
|
|
@ -1220,11 +1365,31 @@ impl MetalBackend {
|
|||
fail!(c.id);
|
||||
}
|
||||
}
|
||||
if let Some(plane) = c.cursor_plane.get() {
|
||||
let crtc_id = plane.crtc_id.value.get();
|
||||
if crtc_id.is_some() && crtc_id != crtc.id {
|
||||
log::warn!("Cannot preserve connector whose cursor plane is attached to a different crtc");
|
||||
fail!(c.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for c in remove_connectors {
|
||||
preserve.connectors.remove(&c);
|
||||
}
|
||||
for connector in dev.connectors.lock().values() {
|
||||
if preserve.connectors.contains(&connector.id) {
|
||||
if let Some(pp) = connector.primary_plane.get() {
|
||||
preserve.planes.insert(pp.id);
|
||||
}
|
||||
if let Some(pp) = connector.cursor_plane.get() {
|
||||
preserve.planes.insert(pp.id);
|
||||
}
|
||||
if let Some(crtc) = connector.crtc.get() {
|
||||
preserve.crtcs.insert(crtc.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_drm_device(
|
||||
|
|
@ -1256,7 +1421,7 @@ impl MetalBackend {
|
|||
for connector in dev.connectors.lock().values() {
|
||||
if !preserve.connectors.contains(&connector.id) {
|
||||
if let Err(e) =
|
||||
self.assign_connector_plane(connector, &mut changes, &ctx, &mut old_buffers)
|
||||
self.assign_connector_planes(connector, &mut changes, &ctx, &mut old_buffers)
|
||||
{
|
||||
log::error!("Could not assign a plane: {}", ErrorFmt(e));
|
||||
}
|
||||
|
|
@ -1265,6 +1430,12 @@ impl MetalBackend {
|
|||
if let Err(e) = changes.commit(flags, 0) {
|
||||
return Err(MetalError::Modeset(e));
|
||||
}
|
||||
for connector in dev.connectors.lock().values() {
|
||||
if preserve.connectors.contains(&connector.id) {
|
||||
continue;
|
||||
}
|
||||
connector.send_hardware_cursor();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1355,8 +1526,9 @@ impl MetalBackend {
|
|||
width: i32,
|
||||
height: i32,
|
||||
ctx: &MetalRenderContext,
|
||||
cursor: bool,
|
||||
) -> Result<[RenderBuffer; 2], MetalError> {
|
||||
let create = || self.create_scanout_buffer(dev, format, width, height, ctx);
|
||||
let create = || self.create_scanout_buffer(dev, format, width, height, ctx, cursor);
|
||||
Ok([create()?, create()?])
|
||||
}
|
||||
|
||||
|
|
@ -1367,9 +1539,10 @@ impl MetalBackend {
|
|||
width: i32,
|
||||
height: i32,
|
||||
ctx: &MetalRenderContext,
|
||||
cursor: bool,
|
||||
) -> Result<RenderBuffer, MetalError> {
|
||||
let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
|
||||
if ctx.dev.id != dev.id {
|
||||
if cursor || ctx.dev.id != dev.id {
|
||||
usage |= GBM_BO_USE_LINEAR;
|
||||
};
|
||||
let bo = dev.gbm.create_bo(width, height, format, usage);
|
||||
|
|
@ -1439,7 +1612,7 @@ impl MetalBackend {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_connector_plane(
|
||||
fn assign_connector_planes(
|
||||
&self,
|
||||
connector: &Rc<MetalConnector>,
|
||||
changes: &mut Change,
|
||||
|
|
@ -1461,7 +1634,7 @@ impl MetalBackend {
|
|||
let primary_plane = 'primary_plane: {
|
||||
for plane in crtc.possible_planes.values() {
|
||||
if plane.ty == PlaneType::Primary
|
||||
&& plane.crtc_id.value.get().is_none()
|
||||
&& !plane.assigned.get()
|
||||
&& plane.formats.contains_key(&XRGB8888.drm)
|
||||
{
|
||||
break 'primary_plane plane.clone();
|
||||
|
|
@ -1469,17 +1642,51 @@ impl MetalBackend {
|
|||
}
|
||||
return Err(MetalError::NoPrimaryPlaneForConnector);
|
||||
};
|
||||
let format = ModifiedFormat {
|
||||
format: XRGB8888,
|
||||
modifier: INVALID_MODIFIER,
|
||||
};
|
||||
let buffers = Rc::new(self.create_scanout_buffers(
|
||||
&connector.dev,
|
||||
&format,
|
||||
&ModifiedFormat {
|
||||
format: XRGB8888,
|
||||
modifier: INVALID_MODIFIER,
|
||||
},
|
||||
mode.hdisplay as _,
|
||||
mode.vdisplay as _,
|
||||
ctx,
|
||||
false,
|
||||
)?);
|
||||
let mut cursor_plane = None;
|
||||
for plane in crtc.possible_planes.values() {
|
||||
if plane.ty == PlaneType::Cursor
|
||||
&& !plane.assigned.get()
|
||||
&& plane.formats.contains_key(&ARGB8888.drm)
|
||||
{
|
||||
cursor_plane = Some(plane.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
let mut cursor_buffers = None;
|
||||
if cursor_plane.is_some() {
|
||||
let res = self.create_scanout_buffers(
|
||||
&connector.dev,
|
||||
&ModifiedFormat {
|
||||
format: ARGB8888,
|
||||
modifier: INVALID_MODIFIER,
|
||||
},
|
||||
connector.dev.cursor_width as _,
|
||||
connector.dev.cursor_height as _,
|
||||
ctx,
|
||||
true,
|
||||
);
|
||||
match res {
|
||||
Ok(r) => cursor_buffers = Some(Rc::new(r)),
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Could not allocate buffers for the cursor plane: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
cursor_plane = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
changes.change_object(primary_plane.id, |c| {
|
||||
c.change(primary_plane.fb_id, buffers[0].drm.id().0 as _);
|
||||
c.change(primary_plane.crtc_id.id, crtc.id.0 as _);
|
||||
|
|
@ -1492,6 +1699,7 @@ impl MetalBackend {
|
|||
c.change(primary_plane.src_w.id, (mode.hdisplay as u64) << 16);
|
||||
c.change(primary_plane.src_h.id, (mode.vdisplay as u64) << 16);
|
||||
});
|
||||
primary_plane.assigned.set(true);
|
||||
primary_plane.crtc_id.value.set(crtc.id);
|
||||
primary_plane.crtc_x.value.set(0);
|
||||
primary_plane.crtc_y.value.set(0);
|
||||
|
|
@ -1505,6 +1713,14 @@ impl MetalBackend {
|
|||
old_buffers.push(old);
|
||||
}
|
||||
connector.primary_plane.set(Some(primary_plane.clone()));
|
||||
if let Some(cp) = &cursor_plane {
|
||||
cp.assigned.set(true);
|
||||
}
|
||||
if let Some(old) = connector.cursor_buffers.set(cursor_buffers) {
|
||||
old_buffers.push(old);
|
||||
}
|
||||
connector.cursor_plane.set(cursor_plane);
|
||||
connector.cursor_enabled.set(false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1516,6 +1732,7 @@ impl MetalBackend {
|
|||
dd.mode.as_ref().unwrap(),
|
||||
);
|
||||
connector.has_damage.set(true);
|
||||
connector.cursor_changed.set(true);
|
||||
connector.schedule_present();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -700,6 +700,7 @@ impl XBackend {
|
|||
true,
|
||||
rr.deref_mut(),
|
||||
node.preferred_scale.get(),
|
||||
true,
|
||||
);
|
||||
for fr in rr.frame_requests.drain(..) {
|
||||
fr.send_done();
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ fn start_compositor2(
|
|||
},
|
||||
scales,
|
||||
cursor_sizes: Default::default(),
|
||||
hardware_tick_cursor: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -274,6 +275,7 @@ fn start_global_event_handlers(
|
|||
|
||||
res.push(eng.spawn(tasks::handle_backend_events(state.clone())));
|
||||
res.push(eng.spawn(tasks::handle_slow_clients(state.clone())));
|
||||
res.push(eng.spawn(tasks::handle_hardware_cursor_tick(state.clone())));
|
||||
res.push(eng.spawn2(Phase::Layout, container_layout(state.clone())));
|
||||
res.push(eng.spawn2(Phase::PostLayout, container_render_data(state.clone())));
|
||||
res.push(eng.spawn2(Phase::Layout, float_layout(state.clone())));
|
||||
|
|
@ -383,6 +385,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
pointer_positions: Default::default(),
|
||||
lock_surface: Default::default(),
|
||||
preferred_scale: Cell::new(Fixed::from_int(1)),
|
||||
hardware_cursor: Default::default(),
|
||||
});
|
||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||
id: state.node_ids.next(),
|
||||
|
|
|
|||
|
|
@ -605,6 +605,24 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_use_hardware_cursor(
|
||||
&self,
|
||||
seat: Seat,
|
||||
use_hardware_cursor: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
if use_hardware_cursor {
|
||||
for other in self.state.globals.seats.lock().values() {
|
||||
if other.id() != seat.id() {
|
||||
other.set_hardware_cursor(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
seat.set_hardware_cursor(use_hardware_cursor);
|
||||
self.state.refresh_hardware_cursors();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_size(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
let pos = connector.node.global.pos.get();
|
||||
|
|
@ -1171,6 +1189,12 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetDragLockEnabled { device, enabled } => self
|
||||
.handle_set_drag_lock_enabled(device, enabled)
|
||||
.wrn("set_drag_lock_enabled")?,
|
||||
ClientMessage::SetUseHardwareCursor {
|
||||
seat,
|
||||
use_hardware_cursor,
|
||||
} => self
|
||||
.handle_set_use_hardware_cursor(seat, use_hardware_cursor)
|
||||
.wrn("set_use_hardware_cursor")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use {
|
|||
rect::Rect,
|
||||
render::{RenderContext, RenderError, Renderer, Texture},
|
||||
state::State,
|
||||
time::Time,
|
||||
tree::OutputNode,
|
||||
utils::{errorfmt::ErrorFmt, numcell::NumCell, smallmap::SmallMapMut},
|
||||
},
|
||||
|
|
@ -22,9 +23,9 @@ use {
|
|||
mem::MaybeUninit,
|
||||
rc::Rc,
|
||||
slice, str,
|
||||
time::Duration,
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
const XCURSOR_MAGIC: u32 = 0x72756358;
|
||||
|
|
@ -38,11 +39,19 @@ const HEADER_SIZE: u32 = 16;
|
|||
|
||||
pub trait Cursor {
|
||||
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed);
|
||||
fn render_hardware_cursor(&self, renderer: &mut Renderer);
|
||||
fn extents_at_scale(&self, scale: Fixed) -> Rect;
|
||||
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||
let _ = output;
|
||||
}
|
||||
fn handle_unset(&self) {}
|
||||
fn tick(&self) {}
|
||||
fn needs_tick(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn time_until_tick(&self) -> Duration {
|
||||
Duration::new(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerCursors {
|
||||
|
|
@ -183,19 +192,12 @@ impl ServerCursorTemplate {
|
|||
ServerCursorTemplateVariant::Static(s) => Rc::new(StaticCursor {
|
||||
image: s.for_size(size),
|
||||
}),
|
||||
ServerCursorTemplateVariant::Animated(a) => {
|
||||
let mut start = c::timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
};
|
||||
uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut start).unwrap();
|
||||
Rc::new(AnimatedCursor {
|
||||
start,
|
||||
next: NumCell::new(a[0].delay_ns),
|
||||
idx: Cell::new(0),
|
||||
images: a.iter().map(|c| c.for_size(size)).collect(),
|
||||
})
|
||||
}
|
||||
ServerCursorTemplateVariant::Animated(a) => Rc::new(AnimatedCursor {
|
||||
start: Time::now_unchecked(),
|
||||
next: NumCell::new(a[0].delay_ns),
|
||||
idx: Cell::new(0),
|
||||
images: a.iter().map(|c| c.for_size(size)).collect(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -237,7 +239,7 @@ impl CursorImage {
|
|||
sizes: SmallMapMut<(Fixed, u32), Rc<CursorImageScaled>, 2>,
|
||||
) -> Result<Self, CursorError> {
|
||||
Ok(Self {
|
||||
delay_ns: delay_ms * 1_000_000,
|
||||
delay_ns: delay_ms.max(1) * 1_000_000,
|
||||
sizes,
|
||||
})
|
||||
}
|
||||
|
|
@ -291,10 +293,23 @@ impl Cursor for StaticCursor {
|
|||
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed) {
|
||||
render_img(&self.image, renderer, x, y);
|
||||
}
|
||||
|
||||
fn render_hardware_cursor(&self, renderer: &mut Renderer) {
|
||||
if let Some(img) = self.image.scales.get(&renderer.scale()) {
|
||||
renderer.render_texture(&img.tex, 0, 0, ARGB8888, None, None, renderer.scale());
|
||||
}
|
||||
}
|
||||
|
||||
fn extents_at_scale(&self, scale: Fixed) -> Rect {
|
||||
match self.image.scales.get(&scale) {
|
||||
None => Rect::new_empty(0, 0),
|
||||
Some(i) => i.extents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AnimatedCursor {
|
||||
start: c::timespec,
|
||||
start: Time,
|
||||
next: NumCell<u64>,
|
||||
idx: Cell<usize>,
|
||||
images: Vec<InstantiatedCursorImage>,
|
||||
|
|
@ -306,15 +321,24 @@ impl Cursor for AnimatedCursor {
|
|||
render_img(img, renderer, x, y);
|
||||
}
|
||||
|
||||
fn render_hardware_cursor(&self, renderer: &mut Renderer) {
|
||||
let img = &self.images[self.idx.get()];
|
||||
if let Some(img) = img.scales.get(&renderer.scale()) {
|
||||
renderer.render_texture(&img.tex, 0, 0, ARGB8888, None, None, renderer.scale());
|
||||
}
|
||||
}
|
||||
|
||||
fn extents_at_scale(&self, scale: Fixed) -> Rect {
|
||||
let img = &self.images[self.idx.get()];
|
||||
match img.scales.get(&scale) {
|
||||
None => Rect::new_empty(0, 0),
|
||||
Some(i) => i.extents,
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&self) {
|
||||
let mut now = c::timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
};
|
||||
uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut now).unwrap();
|
||||
let dist = (now.tv_sec.wrapping_sub(self.start.tv_sec)) as i64 * 1_000_000_000
|
||||
+ now.tv_nsec.wrapping_sub(self.start.tv_nsec) as i64;
|
||||
if (dist as u64) < self.next.get() {
|
||||
let dist = Time::now_unchecked() - self.start;
|
||||
if (dist.as_nanos() as u64) < self.next.get() {
|
||||
return;
|
||||
}
|
||||
let idx = (self.idx.get() + 1) % self.images.len();
|
||||
|
|
@ -322,6 +346,17 @@ impl Cursor for AnimatedCursor {
|
|||
let image = &self.images[idx];
|
||||
self.next.fetch_add(image.delay_ns);
|
||||
}
|
||||
|
||||
fn needs_tick(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn time_until_tick(&self) -> Duration {
|
||||
let dist = Time::now_unchecked() - self.start;
|
||||
let dist = dist.as_nanos() as u64;
|
||||
let nanos = self.next.get().saturating_sub(dist);
|
||||
Duration::from_nanos(nanos)
|
||||
}
|
||||
}
|
||||
|
||||
struct OpenCursorResult {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ use {
|
|||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
rect::Rect,
|
||||
state::State,
|
||||
tree::{
|
||||
generic_node_visitor, ContainerNode, ContainerSplit, Direction, FloatNode, FoundNode,
|
||||
|
|
@ -150,6 +151,7 @@ pub struct WlSeatGlobal {
|
|||
desired_known_cursor: Cell<Option<KnownCursor>>,
|
||||
changes: NumCell<u32>,
|
||||
cursor_size: Cell<u32>,
|
||||
hardware_cursor: Cell<bool>,
|
||||
}
|
||||
|
||||
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
||||
|
|
@ -202,6 +204,7 @@ impl WlSeatGlobal {
|
|||
desired_known_cursor: Cell::new(None),
|
||||
changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE),
|
||||
cursor_size: Cell::new(DEFAULT_CURSOR_SIZE),
|
||||
hardware_cursor: Cell::new(state.globals.seats.len() == 0),
|
||||
});
|
||||
state.add_cursor_size(DEFAULT_CURSOR_SIZE);
|
||||
let seat = slf.clone();
|
||||
|
|
@ -218,6 +221,78 @@ impl WlSeatGlobal {
|
|||
slf
|
||||
}
|
||||
|
||||
pub fn set_hardware_cursor(&self, hardware_cursor: bool) {
|
||||
self.hardware_cursor.set(hardware_cursor);
|
||||
}
|
||||
|
||||
pub fn hardware_cursor(&self) -> bool {
|
||||
self.hardware_cursor.get()
|
||||
}
|
||||
|
||||
fn update_hardware_cursor_position(&self) {
|
||||
self.update_hardware_cursor_(false);
|
||||
}
|
||||
|
||||
pub fn update_hardware_cursor(&self) {
|
||||
self.update_hardware_cursor_(true);
|
||||
}
|
||||
|
||||
fn update_hardware_cursor_(&self, render: bool) {
|
||||
if !self.hardware_cursor.get() {
|
||||
return;
|
||||
}
|
||||
let cursor = match self.get_cursor() {
|
||||
Some(c) => c,
|
||||
_ => {
|
||||
self.state.disable_hardware_cursors();
|
||||
return;
|
||||
}
|
||||
};
|
||||
if render {
|
||||
cursor.tick();
|
||||
}
|
||||
let (x, y) = self.get_position();
|
||||
for output in self.state.root.outputs.lock().values() {
|
||||
if let Some(hc) = output.hardware_cursor.get() {
|
||||
let scale = output.preferred_scale.get();
|
||||
let extents = cursor.extents_at_scale(scale);
|
||||
if render {
|
||||
let (max_width, max_height) = hc.max_size();
|
||||
if extents.width() > max_width || extents.height() > max_height {
|
||||
hc.set_enabled(false);
|
||||
hc.commit();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let opos = output.global.pos.get();
|
||||
let (x_rel, y_rel);
|
||||
if scale == 1 {
|
||||
x_rel = x.round_down() - opos.x1();
|
||||
y_rel = y.round_down() - opos.y1();
|
||||
} else {
|
||||
let scalef = scale.to_f64();
|
||||
x_rel = ((x - Fixed::from_int(opos.x1())).to_f64() * scalef).round() as i32;
|
||||
y_rel = ((y - Fixed::from_int(opos.y1())).to_f64() * scalef).round() as i32;
|
||||
}
|
||||
let mode = output.global.mode.get();
|
||||
if extents
|
||||
.intersects(&Rect::new_sized(-x_rel, -y_rel, mode.width, mode.height).unwrap())
|
||||
{
|
||||
if render {
|
||||
let buffer = hc.get_buffer();
|
||||
buffer.render_hardware_cursor(cursor.deref(), &self.state, scale);
|
||||
hc.swap_buffer();
|
||||
}
|
||||
hc.set_enabled(true);
|
||||
hc.set_position(x_rel + extents.x1(), y_rel + extents.y1());
|
||||
} else {
|
||||
hc.set_enabled(false);
|
||||
}
|
||||
hc.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_size(&self, size: u32) {
|
||||
let old = self.cursor_size.replace(size);
|
||||
if size != old {
|
||||
|
|
@ -361,6 +436,7 @@ impl WlSeatGlobal {
|
|||
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
self.pos.set((Fixed::from_int(x), Fixed::from_int(y)));
|
||||
self.update_hardware_cursor_position();
|
||||
self.trigger_tree_changed();
|
||||
let output = 'set_output: {
|
||||
let outputs = self.state.outputs.lock();
|
||||
|
|
@ -681,7 +757,9 @@ impl WlSeatGlobal {
|
|||
if let Some(cursor) = cursor.as_ref() {
|
||||
cursor.set_output(&self.output.get());
|
||||
}
|
||||
self.cursor.set(cursor);
|
||||
self.cursor.set(cursor.clone());
|
||||
self.state.hardware_tick_cursor.push(cursor);
|
||||
self.update_hardware_cursor();
|
||||
}
|
||||
|
||||
pub fn dnd_icon(&self) -> Option<Rc<WlSurface>> {
|
||||
|
|
|
|||
|
|
@ -480,6 +480,7 @@ impl WlSeatGlobal {
|
|||
fn set_new_position(self: &Rc<Self>, time_usec: u64, x: Fixed, y: Fixed) {
|
||||
self.pos_time_usec.set(time_usec);
|
||||
self.pos.set((x, y));
|
||||
self.update_hardware_cursor_position();
|
||||
self.changes.or_assign(CHANGE_CURSOR_MOVED);
|
||||
self.apply_changes();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -804,9 +804,6 @@ impl WlSurface {
|
|||
}
|
||||
self.buffer_abs_pos
|
||||
.set(self.buffer_abs_pos.get().with_size(width, height).unwrap());
|
||||
for (_, cursor) in &self.cursors {
|
||||
cursor.handle_buffer_change();
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut pfr = self.pending.frame_request.borrow_mut();
|
||||
|
|
@ -832,6 +829,11 @@ impl WlSurface {
|
|||
if self.need_extents_update.get() {
|
||||
self.calculate_extents();
|
||||
}
|
||||
if buffer_transform_changed || transform_changed {
|
||||
for (_, cursor) in &self.cursors {
|
||||
cursor.handle_buffer_change();
|
||||
}
|
||||
}
|
||||
ext.post_commit();
|
||||
self.client.state.damage();
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -31,10 +31,12 @@ impl CursorSurface {
|
|||
}
|
||||
|
||||
fn update_extents(&self) {
|
||||
let extents = self.extents.get();
|
||||
let (hot_x, hot_y) = self.hotspot.get();
|
||||
self.extents
|
||||
.set(Rect::new_sized(-hot_x, -hot_y, extents.width(), extents.height()).unwrap());
|
||||
.set(self.surface.extents.get().move_(-hot_x, -hot_y));
|
||||
if self.seat.hardware_cursor() {
|
||||
self.seat.update_hardware_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_surface_destroy(&self) {
|
||||
|
|
@ -42,9 +44,6 @@ impl CursorSurface {
|
|||
}
|
||||
|
||||
pub fn handle_buffer_change(&self) {
|
||||
let (width, height) = self.surface.buffer_abs_pos.get().size();
|
||||
self.extents
|
||||
.set(Rect::new_sized(0, 0, width, height).unwrap());
|
||||
self.update_extents();
|
||||
}
|
||||
|
||||
|
|
@ -62,22 +61,44 @@ impl CursorSurface {
|
|||
|
||||
impl Cursor for CursorSurface {
|
||||
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed) {
|
||||
let extents = self.extents.get().move_(x.round_down(), y.round_down());
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
let extents = self.extents.get().move_(x_int, y_int);
|
||||
if extents.intersects(&renderer.logical_extents()) {
|
||||
let (hot_x, hot_y) = self.hotspot.get();
|
||||
let scale = renderer.scale();
|
||||
if scale != 1 {
|
||||
let scale = scale.to_f64();
|
||||
let (hot_x, hot_y) = self.hotspot.get();
|
||||
let (hot_x, hot_y) = (Fixed::from_int(hot_x), Fixed::from_int(hot_y));
|
||||
let x = ((x - hot_x).to_f64() * scale).round() as _;
|
||||
let y = ((y - hot_y).to_f64() * scale).round() as _;
|
||||
renderer.render_surface_scaled(&self.surface, x, y, None);
|
||||
} else {
|
||||
renderer.render_surface(&self.surface, extents.x1(), extents.y1());
|
||||
renderer.render_surface(&self.surface, x_int - hot_x, y_int - hot_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_hardware_cursor(&self, renderer: &mut Renderer) {
|
||||
let extents = self.surface.extents.get();
|
||||
renderer.render_surface(&self.surface, -extents.x1(), -extents.y1());
|
||||
}
|
||||
|
||||
fn extents_at_scale(&self, scale: Fixed) -> Rect {
|
||||
let rect = self.extents.get();
|
||||
if scale == 1 {
|
||||
return rect;
|
||||
}
|
||||
let scale = scale.to_f64();
|
||||
Rect::new(
|
||||
(rect.x1() as f64 * scale).ceil() as _,
|
||||
(rect.y1() as f64 * scale).ceil() as _,
|
||||
(rect.x2() as f64 * scale).ceil() as _,
|
||||
(rect.y2() as f64 * scale).ceil() as _,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||
self.surface.set_output(output);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,13 +99,8 @@ impl Rect {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
let x1 = self.x1.max(other.x1);
|
||||
let y1 = self.y1.max(other.y1);
|
||||
let x2 = self.x2.min(other.x2);
|
||||
let y2 = self.y2.min(other.y2);
|
||||
x1 < x2 && y1 < y2
|
||||
self.x1 < other.x2 && other.x1 < self.x2 && self.y1 < other.y2 && other.y1 < self.y2
|
||||
}
|
||||
|
||||
pub fn intersect(&self, other: Self) -> Self {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
cursor::Cursor,
|
||||
fixed::Fixed,
|
||||
format::{Format, XRGB8888},
|
||||
rect::Rect,
|
||||
|
|
@ -113,6 +114,7 @@ impl Framebuffer {
|
|||
on_output: bool,
|
||||
result: &mut RenderResult,
|
||||
scale: Fixed,
|
||||
render_hardware_cursor: bool,
|
||||
) {
|
||||
let _ = self.ctx.ctx.with_current(|| {
|
||||
let c = state.theme.colors.background.get();
|
||||
|
|
@ -138,6 +140,9 @@ impl Framebuffer {
|
|||
if let Some(rect) = cursor_rect {
|
||||
let seats = state.globals.lock_seats();
|
||||
for seat in seats.values() {
|
||||
if !render_hardware_cursor && seat.hardware_cursor() {
|
||||
continue;
|
||||
}
|
||||
if let Some(cursor) = seat.get_cursor() {
|
||||
let (mut x, mut y) = seat.get_position();
|
||||
if let Some(dnd_icon) = seat.dnd_icon() {
|
||||
|
|
@ -163,4 +168,33 @@ impl Framebuffer {
|
|||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Fixed) {
|
||||
let _ = self.ctx.ctx.with_current(|| {
|
||||
unsafe {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
|
||||
glViewport(0, 0, self.gl.width, self.gl.height);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
let mut res = RenderResult::default();
|
||||
let mut renderer = Renderer {
|
||||
ctx: &self.ctx,
|
||||
fb: &self.gl,
|
||||
state,
|
||||
on_output: false,
|
||||
result: &mut res,
|
||||
scaled: scale != 1,
|
||||
scale,
|
||||
scalef: scale.to_f64(),
|
||||
logical_extents: Rect::new_empty(0, 0),
|
||||
};
|
||||
cursor.render_hardware_cursor(&mut renderer);
|
||||
unsafe {
|
||||
glFlush();
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
|
|||
false,
|
||||
&mut Default::default(),
|
||||
Fixed::from_int(1),
|
||||
true,
|
||||
);
|
||||
let drm = ctx.gbm.drm.dup_render()?.fd().clone();
|
||||
Ok(Screenshot { drm, bo })
|
||||
|
|
|
|||
30
src/state.rs
30
src/state.rs
|
|
@ -10,7 +10,7 @@ use {
|
|||
cli::RunArgs,
|
||||
client::{Client, Clients, SerialRange, NUM_CACHED_SERIAL_RANGES},
|
||||
config::ConfigProxy,
|
||||
cursor::ServerCursors,
|
||||
cursor::{Cursor, ServerCursors},
|
||||
dbus::Dbus,
|
||||
fixed::Fixed,
|
||||
forker::ForkerProxy,
|
||||
|
|
@ -113,6 +113,7 @@ pub struct State {
|
|||
pub lock: ScreenlockState,
|
||||
pub scales: RefCounted<Fixed>,
|
||||
pub cursor_sizes: RefCounted<u32>,
|
||||
pub hardware_tick_cursor: AsyncQueue<Option<Rc<dyn Cursor>>>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -594,4 +595,31 @@ impl State {
|
|||
self.wheel.clear();
|
||||
self.eng.clear();
|
||||
}
|
||||
|
||||
pub fn disable_hardware_cursors(&self) {
|
||||
for output in self.root.outputs.lock().values() {
|
||||
if let Some(hc) = output.hardware_cursor.get() {
|
||||
hc.set_enabled(false);
|
||||
hc.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh_hardware_cursors(&self) {
|
||||
let seat = self
|
||||
.globals
|
||||
.seats
|
||||
.lock()
|
||||
.values()
|
||||
.find(|s| s.hardware_cursor())
|
||||
.cloned();
|
||||
let seat = match seat {
|
||||
Some(s) => s,
|
||||
_ => {
|
||||
self.disable_hardware_cursors();
|
||||
return;
|
||||
}
|
||||
};
|
||||
seat.update_hardware_cursor();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
mod backend;
|
||||
mod connector;
|
||||
mod drmdev;
|
||||
mod hardware_cursor;
|
||||
mod idle;
|
||||
mod input_device;
|
||||
mod slow_clients;
|
||||
|
||||
pub use idle::idle;
|
||||
use {
|
||||
crate::{
|
||||
state::State,
|
||||
|
|
@ -13,6 +13,7 @@ use {
|
|||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
pub use {hardware_cursor::handle_hardware_cursor_tick, idle::idle};
|
||||
|
||||
pub async fn handle_backend_events(state: Rc<State>) {
|
||||
let mut beh = BackendEventHandler { state };
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ impl ConnectorHandler {
|
|||
pointer_positions: Default::default(),
|
||||
lock_surface: Default::default(),
|
||||
preferred_scale: Cell::new(Fixed::from_int(1)),
|
||||
hardware_cursor: Default::default(),
|
||||
});
|
||||
self.state.add_output_scale(on.preferred_scale.get());
|
||||
let mode = info.initial_mode;
|
||||
|
|
@ -191,6 +192,10 @@ impl ConnectorHandler {
|
|||
while let Some(event) = self.data.connector.event() {
|
||||
match event {
|
||||
ConnectorEvent::Disconnected => break 'outer,
|
||||
ConnectorEvent::HardwareCursor(hc) => {
|
||||
on.hardware_cursor.set(hc);
|
||||
self.state.refresh_hardware_cursors();
|
||||
}
|
||||
ConnectorEvent::ModeChanged(mode) => {
|
||||
on.update_mode(mode);
|
||||
}
|
||||
|
|
|
|||
31
src/tasks/hardware_cursor.rs
Normal file
31
src/tasks/hardware_cursor.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use {
|
||||
crate::{state::State, utils::errorfmt::ErrorFmt},
|
||||
futures_util::{select, FutureExt},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub async fn handle_hardware_cursor_tick(state: Rc<State>) {
|
||||
loop {
|
||||
let cursor = match state.hardware_tick_cursor.pop().await {
|
||||
Some(c) => c,
|
||||
_ => continue,
|
||||
};
|
||||
if !cursor.needs_tick() {
|
||||
continue;
|
||||
}
|
||||
loop {
|
||||
let tick = (cursor.time_until_tick().as_nanos() + 999_999) / 1_000_000;
|
||||
if tick > 0 {
|
||||
let res = select! {
|
||||
_ = state.hardware_tick_cursor.non_empty().fuse() => break,
|
||||
res = state.wheel.timeout(tick as _).fuse() => res,
|
||||
};
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not wait for cursor tick: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.refresh_hardware_cursors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{KeyState, Mode},
|
||||
backend::{HardwareCursor, KeyState, Mode},
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
|
|
@ -52,6 +52,7 @@ pub struct OutputNode {
|
|||
pub pointer_positions: CopyHashMap<SeatId, (i32, i32)>,
|
||||
pub lock_surface: CloneCell<Option<Rc<ExtSessionLockSurfaceV1>>>,
|
||||
pub preferred_scale: Cell<Fixed>,
|
||||
pub hardware_cursor: CloneCell<Option<Rc<dyn HardwareCursor>>>,
|
||||
}
|
||||
|
||||
impl OutputNode {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,11 @@ use {
|
|||
use crate::{
|
||||
backend,
|
||||
utils::{errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt},
|
||||
video::{dmabuf::DmaBuf, INVALID_MODIFIER},
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
drm::sys::{DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH},
|
||||
INVALID_MODIFIER,
|
||||
},
|
||||
};
|
||||
pub use sys::{
|
||||
drm_mode_modeinfo, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET,
|
||||
|
|
@ -235,6 +239,12 @@ impl DrmMaster {
|
|||
mode_getencoder(self.raw(), encoder.0)
|
||||
}
|
||||
|
||||
pub fn get_cursor_size(&self) -> Result<(u64, u64), OsError> {
|
||||
let width = self.get_cap(DRM_CAP_CURSOR_WIDTH)?;
|
||||
let height = self.get_cap(DRM_CAP_CURSOR_HEIGHT)?;
|
||||
Ok((width, height))
|
||||
}
|
||||
|
||||
pub fn get_connector_info(
|
||||
&self,
|
||||
connector: DrmConnector,
|
||||
|
|
|
|||
|
|
@ -217,6 +217,9 @@ const DRM_MODE_PROP_SIGNED_RANGE: u32 = drm_mode_prop_type(2);
|
|||
|
||||
const DRM_MODE_PROP_ATOMIC: u32 = 0x80000000;
|
||||
|
||||
pub const DRM_CAP_CURSOR_WIDTH: u64 = 0x8;
|
||||
pub const DRM_CAP_CURSOR_HEIGHT: u64 = 0x9;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_mode_property_enum {
|
||||
value: u64,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue