commit
9dc58129b6
31 changed files with 727 additions and 52 deletions
|
|
@ -126,6 +126,10 @@ Jay supports leasing VR headsets to applications.
|
|||
|
||||
Jay supports adaptive sync with configurable cursor refresh rates.
|
||||
|
||||
## Tearing
|
||||
|
||||
Jay supports tearing presentation for games.
|
||||
|
||||
## Protocol Support
|
||||
|
||||
Jay supports the following wayland protocols:
|
||||
|
|
@ -153,7 +157,7 @@ Jay supports the following wayland protocols:
|
|||
| wp_presentation | 1 | |
|
||||
| wp_security_context_manager_v1 | 1 | |
|
||||
| wp_single_pixel_buffer_manager_v1 | 1 | |
|
||||
| wp_tearing_control_manager_v1 | 1[^no_tearing] | |
|
||||
| wp_tearing_control_manager_v1 | 1 | |
|
||||
| wp_viewporter | 1 | |
|
||||
| xdg_activation_v1 | 1 | |
|
||||
| xdg_toplevel_drag_manager_v1 | 1 | |
|
||||
|
|
@ -176,7 +180,6 @@ Jay supports the following wayland protocols:
|
|||
| zxdg_output_manager_v1 | 3 | |
|
||||
|
||||
[^no_touch]: Touch input is not supported.
|
||||
[^no_tearing]: Tearing screen updates are not supported.
|
||||
[^lsaccess]: Sandboxes can restrict access to this protocol.
|
||||
[^ts_rejected]: Seat creation is always rejected.
|
||||
|
||||
|
|
@ -185,4 +188,3 @@ Jay supports the following wayland protocols:
|
|||
The following features are currently not supported but might get implemented in the future:
|
||||
|
||||
- Touch support.
|
||||
- Tearing updates of fullscreen games.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use {
|
|||
timer::Timer,
|
||||
video::{
|
||||
connector_type::{ConnectorType, CON_UNKNOWN},
|
||||
Connector, DrmDevice, GfxApi, Mode, Transform, VrrMode,
|
||||
Connector, DrmDevice, GfxApi, Mode, TearingMode, Transform, VrrMode,
|
||||
},
|
||||
Axis, Direction, ModifiedKeySym, PciId, Workspace,
|
||||
},
|
||||
|
|
@ -808,6 +808,10 @@ impl Client {
|
|||
self.send(&ClientMessage::SetVrrCursorHz { connector, hz })
|
||||
}
|
||||
|
||||
pub fn set_tearing_mode(&self, connector: Option<Connector>, mode: TearingMode) {
|
||||
self.send(&ClientMessage::SetTearingMode { connector, mode })
|
||||
}
|
||||
|
||||
pub fn drm_devices(&self) -> Vec<DrmDevice> {
|
||||
let res = self.send_with_response(&ClientMessage::GetDrmDevices);
|
||||
get_response!(res, vec![], GetDrmDevices { devices });
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ use {
|
|||
logging::LogLevel,
|
||||
theme::{colors::Colorable, sized::Resizable, Color},
|
||||
timer::Timer,
|
||||
video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi, Transform, VrrMode},
|
||||
video::{
|
||||
connector_type::ConnectorType, Connector, DrmDevice, GfxApi, TearingMode, Transform,
|
||||
VrrMode,
|
||||
},
|
||||
Axis, Direction, PciId, Workspace,
|
||||
_private::{PollableId, WireMode},
|
||||
},
|
||||
|
|
@ -495,6 +498,10 @@ pub enum ClientMessage<'a> {
|
|||
connector: Option<Connector>,
|
||||
hz: f64,
|
||||
},
|
||||
SetTearingMode {
|
||||
connector: Option<Connector>,
|
||||
mode: TearingMode,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -262,6 +262,11 @@ impl Connector {
|
|||
pub fn set_vrr_cursor_hz(self, hz: f64) {
|
||||
get!().set_vrr_cursor_hz(Some(self), hz)
|
||||
}
|
||||
|
||||
/// Sets the tearing mode.
|
||||
pub fn set_tearing_mode(self, mode: TearingMode) {
|
||||
get!().set_tearing_mode(Some(self), mode)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all available DRM devices.
|
||||
|
|
@ -580,3 +585,30 @@ pub fn set_vrr_mode(mode: VrrMode) {
|
|||
pub fn set_vrr_cursor_hz(hz: f64) {
|
||||
get!().set_vrr_cursor_hz(None, hz)
|
||||
}
|
||||
|
||||
/// The tearing mode of a connector.
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||
pub struct TearingMode(pub u32);
|
||||
|
||||
impl TearingMode {
|
||||
/// Tearing is never enabled.
|
||||
pub const NEVER: Self = Self(0);
|
||||
/// Tearing is always enabled.
|
||||
pub const ALWAYS: Self = Self(1);
|
||||
/// Tearing is enabled when one or more applications are displayed fullscreen.
|
||||
pub const VARIANT_1: Self = Self(2);
|
||||
/// Tearing is enabled when a single application is displayed fullscreen.
|
||||
pub const VARIANT_2: Self = Self(3);
|
||||
/// Tearing is enabled when a single application is displayed fullscreen and the
|
||||
/// application has requested tearing.
|
||||
///
|
||||
/// This is the default.
|
||||
pub const VARIANT_3: Self = Self(4);
|
||||
}
|
||||
|
||||
/// Sets the default tearing mode.
|
||||
///
|
||||
/// This setting can be overwritten on a per-connector basis with [Connector::set_tearing_mode].
|
||||
pub fn set_tearing_mode(mode: TearingMode) {
|
||||
get!().set_tearing_mode(None, mode)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
- Add fine-grained damage tracking.
|
||||
- Add support for adaptive sync.
|
||||
- Add support for tearing.
|
||||
|
||||
# 1.4.0 (2024-07-07)
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ pub trait Connector {
|
|||
fn set_vrr_enabled(&self, enabled: bool) {
|
||||
let _ = enabled;
|
||||
}
|
||||
fn set_tearing_enabled(&self, enabled: bool) {
|
||||
let _ = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ use {
|
|||
DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFramebuffer, DrmLease, DrmMaster,
|
||||
DrmModeInfo, DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition,
|
||||
DrmPropertyType, DrmVersion, PropBlob, DRM_CLIENT_CAP_ATOMIC,
|
||||
DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
|
||||
DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_ASYNC,
|
||||
DRM_MODE_PAGE_FLIP_EVENT,
|
||||
},
|
||||
gbm::{GbmBo, GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT},
|
||||
Modifier, INVALID_MODIFIER,
|
||||
|
|
@ -89,6 +90,7 @@ pub struct MetalDrmDevice {
|
|||
pub _max_height: u32,
|
||||
pub cursor_width: u64,
|
||||
pub cursor_height: u64,
|
||||
pub supports_async_commit: bool,
|
||||
pub gbm: GbmDevice,
|
||||
pub handle_events: HandleEvents,
|
||||
pub ctx: CloneCell<Rc<MetalRenderContext>>,
|
||||
|
|
@ -456,6 +458,8 @@ pub struct MetalConnector {
|
|||
pub active_framebuffer: RefCell<Option<PresentFb>>,
|
||||
pub next_framebuffer: OpaqueCell<Option<PresentFb>>,
|
||||
pub direct_scanout_active: Cell<bool>,
|
||||
|
||||
pub tearing_requested: Cell<bool>,
|
||||
}
|
||||
|
||||
impl Debug for MetalConnector {
|
||||
|
|
@ -947,6 +951,16 @@ impl MetalConnector {
|
|||
let cursor = self.cursor_plane.get();
|
||||
let mut new_fb = None;
|
||||
let mut changes = self.master.change();
|
||||
let mut try_async_flip = self.tearing_requested.get() && self.dev.supports_async_commit;
|
||||
macro_rules! change {
|
||||
($c:expr, $prop:expr, $new:expr) => {{
|
||||
if $prop.value.get() != $new {
|
||||
$c.change($prop.id, $new as u64);
|
||||
try_async_flip = false;
|
||||
$prop.pending_value.set(Some($new));
|
||||
}
|
||||
}};
|
||||
}
|
||||
if self.has_damage.get() {
|
||||
if !self.backend.check_render_context(&self.dev) {
|
||||
return Ok(());
|
||||
|
|
@ -978,13 +992,13 @@ impl MetalConnector {
|
|||
let in_fence = fb.sync_file.as_ref().map(|s| s.raw()).unwrap_or(-1);
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, fb.fb.id().0 as _);
|
||||
c.change(plane.src_w.id, (src_width as u64) << 16);
|
||||
c.change(plane.src_h.id, (src_height as u64) << 16);
|
||||
c.change(plane.crtc_x.id, crtc_x as u64);
|
||||
c.change(plane.crtc_y.id, crtc_y as u64);
|
||||
c.change(plane.crtc_w.id, crtc_w as u64);
|
||||
c.change(plane.crtc_h.id, crtc_h as u64);
|
||||
if !self.dev.is_nvidia {
|
||||
change!(c, plane.src_w, (src_width as u32) << 16);
|
||||
change!(c, plane.src_h, (src_height as u32) << 16);
|
||||
change!(c, plane.crtc_x, crtc_x);
|
||||
change!(c, plane.crtc_y, crtc_y);
|
||||
change!(c, plane.crtc_w, crtc_w);
|
||||
change!(c, plane.crtc_h, crtc_h);
|
||||
if !try_async_flip && !self.dev.is_nvidia {
|
||||
c.change(plane.in_fence_fd, in_fence as u64);
|
||||
}
|
||||
});
|
||||
|
|
@ -1002,6 +1016,7 @@ impl MetalConnector {
|
|||
let mut cursor_swap_buffer = false;
|
||||
let mut cursor_sync_file = None;
|
||||
if self.cursor_changed.get() && cursor.is_some() {
|
||||
try_async_flip = false;
|
||||
let plane = cursor.unwrap();
|
||||
if self.cursor_enabled.get() {
|
||||
cursor_swap_buffer = self.cursor_swap_buffer.get();
|
||||
|
|
@ -1039,7 +1054,18 @@ impl MetalConnector {
|
|||
});
|
||||
}
|
||||
}
|
||||
if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) {
|
||||
let mut res;
|
||||
'commit: {
|
||||
const FLAGS: u32 = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
|
||||
if try_async_flip {
|
||||
res = changes.commit(FLAGS | DRM_MODE_PAGE_FLIP_ASYNC, 0);
|
||||
if res.is_ok() {
|
||||
break 'commit;
|
||||
}
|
||||
}
|
||||
res = changes.commit(FLAGS, 0);
|
||||
}
|
||||
if let Err(e) = res {
|
||||
if let DrmError::Atomic(OsError(c::EACCES)) = e {
|
||||
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
||||
self.render_result
|
||||
|
|
@ -1069,6 +1095,19 @@ impl MetalConnector {
|
|||
.discard_presentation_feedback();
|
||||
Err(MetalError::Commit(e))
|
||||
} else {
|
||||
macro_rules! apply_change {
|
||||
($prop:expr) => {
|
||||
if let Some(v) = $prop.pending_value.take() {
|
||||
$prop.value.set(v);
|
||||
}
|
||||
};
|
||||
}
|
||||
apply_change!(plane.src_w);
|
||||
apply_change!(plane.src_h);
|
||||
apply_change!(plane.crtc_x);
|
||||
apply_change!(plane.crtc_y);
|
||||
apply_change!(plane.crtc_w);
|
||||
apply_change!(plane.crtc_h);
|
||||
node.schedule.presented();
|
||||
self.perform_screencopies(&new_fb, &node);
|
||||
if let Some(fb) = new_fb {
|
||||
|
|
@ -1374,6 +1413,19 @@ impl Connector for MetalConnector {
|
|||
crtc.vrr_enabled.value.set(new_enabled);
|
||||
self.send_vrr_enabled();
|
||||
}
|
||||
|
||||
fn set_tearing_enabled(&self, enabled: bool) {
|
||||
if !self.dev.supports_async_commit {
|
||||
return;
|
||||
}
|
||||
if self.tearing_requested.replace(enabled) != enabled {
|
||||
let msg = match enabled {
|
||||
true => "Enabling",
|
||||
false => "Disabling",
|
||||
};
|
||||
log::debug!("{msg} tearing on output {}", self.kernel_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MetalCrtc {
|
||||
|
|
@ -1524,6 +1576,7 @@ fn create_connector(
|
|||
next_framebuffer: Default::default(),
|
||||
direct_scanout_active: Cell::new(false),
|
||||
next_flip_nsec: Cell::new(0),
|
||||
tearing_requested: Cell::new(false),
|
||||
});
|
||||
let futures = ConnectorFutures {
|
||||
_present: backend
|
||||
|
|
@ -1810,6 +1863,7 @@ impl CollectedProperties {
|
|||
Some((def, value)) => Ok(MutableProperty {
|
||||
id: def.id,
|
||||
value: Cell::new(*value),
|
||||
pending_value: Cell::new(None),
|
||||
}),
|
||||
_ => Err(DrmError::MissingProperty(name.to_string().into_boxed_str())),
|
||||
}
|
||||
|
|
@ -1820,6 +1874,7 @@ impl CollectedProperties {
|
|||
pub struct MutableProperty<T: Copy> {
|
||||
pub id: DrmProperty,
|
||||
pub value: Cell<T>,
|
||||
pub pending_value: Cell<Option<T>>,
|
||||
}
|
||||
|
||||
impl<T: Copy> MutableProperty<T> {
|
||||
|
|
@ -1830,6 +1885,7 @@ impl<T: Copy> MutableProperty<T> {
|
|||
MutableProperty {
|
||||
id: self.id,
|
||||
value: Cell::new(f(self.value.into_inner())),
|
||||
pending_value: Cell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1987,6 +2043,7 @@ impl MetalBackend {
|
|||
disconnect |= !old.is_same_monitor(&dd);
|
||||
}
|
||||
if disconnect {
|
||||
c.tearing_requested.set(false);
|
||||
if let Some(lease_id) = c.lease.get() {
|
||||
if let Some(lease) = dev.dev.leases.remove(&lease_id) {
|
||||
if !lease.try_revoke() {
|
||||
|
|
@ -2152,6 +2209,7 @@ impl MetalBackend {
|
|||
_max_height: resources.max_height,
|
||||
cursor_width,
|
||||
cursor_height,
|
||||
supports_async_commit: master.supports_async_commit(),
|
||||
gbm,
|
||||
handle_events: HandleEvents {
|
||||
handle_events: Cell::new(None),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use {
|
|||
},
|
||||
clap::{Args, Subcommand, ValueEnum},
|
||||
isnt::std_1::vec::IsntVecExt,
|
||||
jay_config::video::{Transform, VrrMode},
|
||||
jay_config::video::{TearingMode, Transform, VrrMode},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
fmt::{Display, Formatter},
|
||||
|
|
@ -120,6 +120,8 @@ pub enum OutputCommand {
|
|||
NonDesktop(NonDesktopArgs),
|
||||
/// Change VRR settings.
|
||||
Vrr(VrrArgs),
|
||||
/// Change tearing settings.
|
||||
Tearing(TearingArgs),
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone)]
|
||||
|
|
@ -144,13 +146,13 @@ pub struct VrrArgs {
|
|||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum VrrCommand {
|
||||
/// Sets the mode that determines when VRR is enabled.
|
||||
SetMode(SetModeArgs),
|
||||
SetMode(SetVrrModeArgs),
|
||||
/// Sets the maximum refresh rate of the cursor.
|
||||
SetCursorHz(CursorHzArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct SetModeArgs {
|
||||
pub struct SetVrrModeArgs {
|
||||
#[clap(value_enum)]
|
||||
pub mode: VrrModeArg,
|
||||
}
|
||||
|
|
@ -175,6 +177,41 @@ pub struct CursorHzArgs {
|
|||
pub rate: String,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct TearingArgs {
|
||||
#[clap(subcommand)]
|
||||
pub command: TearingCommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TearingCommand {
|
||||
/// Sets the mode that determines when tearing is enabled.
|
||||
SetMode(SetTearingModeArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct SetTearingModeArgs {
|
||||
#[clap(value_enum)]
|
||||
pub mode: TearingModeArg,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Copy, Clone, Hash, PartialEq)]
|
||||
pub enum TearingModeArg {
|
||||
/// Tearing is never enabled.
|
||||
Never,
|
||||
/// Tearing is always enabled.
|
||||
Always,
|
||||
/// Tearing is enabled when one or more applications are displayed fullscreen.
|
||||
Variant1,
|
||||
/// Tearing is enabled when a single application is displayed fullscreen.
|
||||
Variant2,
|
||||
/// Tearing is enabled when a single application is displayed fullscreen and the
|
||||
/// application has requested tearing.
|
||||
///
|
||||
/// This is the default.
|
||||
Variant3,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct PositionArgs {
|
||||
/// The top-left x coordinate.
|
||||
|
|
@ -280,6 +317,7 @@ struct Output {
|
|||
pub vrr_enabled: bool,
|
||||
pub vrr_mode: VrrMode,
|
||||
pub vrr_cursor_hz: Option<f64>,
|
||||
pub tearing_mode: TearingMode,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -487,6 +525,27 @@ impl Randr {
|
|||
}
|
||||
}
|
||||
}
|
||||
OutputCommand::Tearing(a) => {
|
||||
self.handle_error(randr, move |msg| {
|
||||
eprintln!("Could not change the tearing setting: {}", msg);
|
||||
});
|
||||
match a.command {
|
||||
TearingCommand::SetMode(a) => {
|
||||
let mode = match a.mode {
|
||||
TearingModeArg::Never => VrrMode::NEVER,
|
||||
TearingModeArg::Always => VrrMode::ALWAYS,
|
||||
TearingModeArg::Variant1 => VrrMode::VARIANT_1,
|
||||
TearingModeArg::Variant2 => VrrMode::VARIANT_2,
|
||||
TearingModeArg::Variant3 => VrrMode::VARIANT_3,
|
||||
};
|
||||
tc.send(jay_randr::SetTearingMode {
|
||||
self_id: randr,
|
||||
output: &args.output,
|
||||
mode: mode.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tc.round_trip().await;
|
||||
}
|
||||
|
|
@ -621,6 +680,21 @@ impl Randr {
|
|||
println!(" VRR cursor hz: {}", hz);
|
||||
}
|
||||
}
|
||||
{
|
||||
let mode_str;
|
||||
let mode = match o.tearing_mode {
|
||||
TearingMode::NEVER => "never",
|
||||
TearingMode::ALWAYS => "always",
|
||||
TearingMode::VARIANT_1 => "variant1",
|
||||
TearingMode::VARIANT_2 => "variant2",
|
||||
TearingMode::VARIANT_3 => "variant3",
|
||||
_ => {
|
||||
mode_str = format!("unknown ({})", o.vrr_mode.0);
|
||||
&mode_str
|
||||
}
|
||||
};
|
||||
println!(" Tearing mode: {}", mode);
|
||||
}
|
||||
println!(" position: {} x {}", o.x, o.y);
|
||||
println!(" logical size: {} x {}", o.width, o.height);
|
||||
if let Some(mode) = &o.current_mode {
|
||||
|
|
@ -713,6 +787,7 @@ impl Randr {
|
|||
vrr_enabled: false,
|
||||
vrr_mode: VrrMode::NEVER,
|
||||
vrr_cursor_hz: None,
|
||||
tearing_mode: TearingMode::NEVER,
|
||||
});
|
||||
});
|
||||
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
||||
|
|
@ -737,6 +812,7 @@ impl Randr {
|
|||
vrr_enabled: false,
|
||||
vrr_mode: VrrMode::NEVER,
|
||||
vrr_cursor_hz: None,
|
||||
tearing_mode: TearingMode::NEVER,
|
||||
});
|
||||
});
|
||||
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
|
||||
|
|
@ -753,6 +829,12 @@ impl Randr {
|
|||
let output = c.output.as_mut().unwrap();
|
||||
output.vrr_cursor_hz = Some(msg.hz);
|
||||
});
|
||||
jay_randr::TearingState::handle(tc, randr, data.clone(), |data, msg| {
|
||||
let mut data = data.borrow_mut();
|
||||
let c = data.connectors.last_mut().unwrap();
|
||||
let output = c.output.as_mut().unwrap();
|
||||
output.tearing_mode = TearingMode(msg.mode);
|
||||
});
|
||||
jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| {
|
||||
let mut data = data.borrow_mut();
|
||||
let c = data.connectors.last_mut().unwrap();
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ use {
|
|||
tasks::{self, idle},
|
||||
tree::{
|
||||
container_layout, container_render_data, float_layout, float_titles,
|
||||
output_render_data, DisplayNode, NodeIds, OutputNode, VrrMode, WorkspaceNode,
|
||||
output_render_data, DisplayNode, NodeIds, OutputNode, TearingMode, VrrMode,
|
||||
WorkspaceNode,
|
||||
},
|
||||
user_session::import_environment,
|
||||
utils::{
|
||||
|
|
@ -249,6 +250,7 @@ fn start_compositor2(
|
|||
damage_visualizer: DamageVisualizer::new(&engine),
|
||||
default_vrr_mode: Cell::new(VrrMode::NEVER),
|
||||
default_vrr_cursor_hz: Cell::new(None),
|
||||
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -425,6 +427,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
pos: Default::default(),
|
||||
vrr_mode: Cell::new(VrrMode::NEVER),
|
||||
vrr_cursor_hz: Default::default(),
|
||||
tearing_mode: Cell::new(&TearingMode::Never),
|
||||
});
|
||||
let connector = Rc::new(DummyOutput {
|
||||
id: state.connector_ids.next(),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use {
|
|||
theme::{Color, ThemeSized, DEFAULT_FONT},
|
||||
tree::{
|
||||
move_ws_to_output, ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase,
|
||||
OutputNode, VrrMode, WsMoveConfig,
|
||||
OutputNode, TearingMode, VrrMode, WsMoveConfig,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
|
|
@ -48,7 +48,10 @@ use {
|
|||
logging::LogLevel,
|
||||
theme::{colors::Colorable, sized::Resizable},
|
||||
timer::Timer as JayTimer,
|
||||
video::{Connector, DrmDevice, GfxApi, Transform, VrrMode as ConfigVrrMode},
|
||||
video::{
|
||||
Connector, DrmDevice, GfxApi, TearingMode as ConfigTearingMode, Transform,
|
||||
VrrMode as ConfigVrrMode,
|
||||
},
|
||||
Axis, Direction, Workspace,
|
||||
},
|
||||
libloading::Library,
|
||||
|
|
@ -1045,7 +1048,7 @@ impl ConfigProxyHandler {
|
|||
Some(c) => {
|
||||
let connector = self.get_output_node(c)?;
|
||||
connector.global.persistent.vrr_mode.set(mode);
|
||||
connector.update_vrr_state();
|
||||
connector.update_presentation_type();
|
||||
}
|
||||
_ => self.state.default_vrr_mode.set(mode),
|
||||
}
|
||||
|
|
@ -1072,6 +1075,25 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_tearing_mode(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
mode: ConfigTearingMode,
|
||||
) -> Result<(), CphError> {
|
||||
let Some(mode) = TearingMode::from_config(mode) else {
|
||||
return Err(CphError::UnknownTearingMode(mode));
|
||||
};
|
||||
match connector {
|
||||
Some(c) => {
|
||||
let connector = self.get_output_node(c)?;
|
||||
connector.global.persistent.tearing_mode.set(mode);
|
||||
connector.update_presentation_type();
|
||||
}
|
||||
_ => self.state.default_tearing_mode.set(mode),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_set_transform(
|
||||
&self,
|
||||
connector: Connector,
|
||||
|
|
@ -1872,6 +1894,9 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetVrrCursorHz { connector, hz } => self
|
||||
.handle_set_vrr_cursor_hz(connector, hz)
|
||||
.wrn("set_vrr_cursor_hz")?,
|
||||
ClientMessage::SetTearingMode { connector, mode } => self
|
||||
.handle_set_tearing_mode(connector, mode)
|
||||
.wrn("set_tearing_mode")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1937,6 +1962,8 @@ enum CphError {
|
|||
UnknownVrrMode(ConfigVrrMode),
|
||||
#[error("Invalid cursor hz {0}")]
|
||||
InvalidCursorHz(f64),
|
||||
#[error("Unknown tearing mode {0:?}")]
|
||||
UnknownTearingMode(ConfigTearingMode),
|
||||
}
|
||||
|
||||
trait WithRequestName {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl Global for JayCompositorGlobal {
|
|||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
2
|
||||
3
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ use {
|
|||
object::{Object, Version},
|
||||
scale::Scale,
|
||||
state::{ConnectorData, DrmDevData, OutputData},
|
||||
tree::{OutputNode, VrrMode},
|
||||
tree::{OutputNode, TearingMode, VrrMode},
|
||||
utils::{gfx_api_ext::GfxApiExt, transform_ext::TransformExt},
|
||||
wire::{jay_randr::*, JayRandrId},
|
||||
},
|
||||
jay_config::video::{GfxApi, Transform, VrrMode as ConfigVrrMode},
|
||||
jay_config::video::{
|
||||
GfxApi, TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode,
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
|
@ -24,6 +26,7 @@ pub struct JayRandr {
|
|||
}
|
||||
|
||||
const VRR_CAPABLE_SINCE: Version = Version(2);
|
||||
const TEARING_SINCE: Version = Version(3);
|
||||
|
||||
impl JayRandr {
|
||||
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
||||
|
|
@ -116,6 +119,12 @@ impl JayRandr {
|
|||
});
|
||||
}
|
||||
}
|
||||
if self.version >= TEARING_SINCE {
|
||||
self.client.event(TearingState {
|
||||
self_id: self.id,
|
||||
mode: node.global.persistent.tearing_mode.get().to_config().0,
|
||||
});
|
||||
}
|
||||
let current_mode = global.mode.get();
|
||||
for mode in &global.modes {
|
||||
self.client.event(Mode {
|
||||
|
|
@ -325,7 +334,7 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
return Ok(());
|
||||
};
|
||||
c.global.persistent.vrr_mode.set(mode);
|
||||
c.update_vrr_state();
|
||||
c.update_presentation_type();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
@ -340,6 +349,22 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
c.schedule.set_cursor_hz(req.hz);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_tearing_mode(
|
||||
&self,
|
||||
req: SetTearingMode<'_>,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
let Some(mode) = TearingMode::from_config(ConfigTearingMode(req.mode)) else {
|
||||
return Err(JayRandrError::UnknownTearingMode(req.mode));
|
||||
};
|
||||
let Some(c) = self.get_output_node(req.output) else {
|
||||
return Ok(());
|
||||
};
|
||||
c.global.persistent.tearing_mode.set(mode);
|
||||
c.update_presentation_type();
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -357,5 +382,7 @@ pub enum JayRandrError {
|
|||
ClientError(Box<ClientError>),
|
||||
#[error("Unknown VRR mode {0}")]
|
||||
UnknownVrrMode(u32),
|
||||
#[error("Unknown tearing mode {0}")]
|
||||
UnknownTearingMode(u32),
|
||||
}
|
||||
efrom!(JayRandrError, ClientError);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use {
|
|||
object::{Object, Version},
|
||||
rect::Rect,
|
||||
state::{ConnectorData, State},
|
||||
tree::{calculate_logical_size, OutputNode, VrrMode},
|
||||
tree::{calculate_logical_size, OutputNode, TearingMode, VrrMode},
|
||||
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, transform_ext::TransformExt},
|
||||
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
||||
},
|
||||
|
|
@ -93,6 +93,7 @@ pub struct PersistentOutputState {
|
|||
pub pos: Cell<(i32, i32)>,
|
||||
pub vrr_mode: Cell<&'static VrrMode>,
|
||||
pub vrr_cursor_hz: Cell<Option<f64>>,
|
||||
pub tearing_mode: Cell<&'static TearingMode>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ pub struct WlSurface {
|
|||
pub constraints: SmallMap<SeatId, Rc<SeatConstraint>, 1>,
|
||||
xwayland_serial: Cell<Option<u64>>,
|
||||
tearing_control: CloneCell<Option<Rc<WpTearingControlV1>>>,
|
||||
tearing: Cell<bool>,
|
||||
pub tearing: Cell<bool>,
|
||||
version: Version,
|
||||
pub has_content_type_manager: Cell<bool>,
|
||||
pub content_type: Cell<Option<ContentType>>,
|
||||
|
|
@ -1213,7 +1213,7 @@ impl WlSurface {
|
|||
let had_frame_requests = self.buffer_had_frame_request.get();
|
||||
let has_frame_requests = {
|
||||
let frs = &mut *self.frame_requests.borrow_mut();
|
||||
frs.extend(pending.frame_request.drain(..));
|
||||
frs.append(&mut pending.frame_request);
|
||||
frs.is_not_empty()
|
||||
};
|
||||
self.buffer_had_frame_request
|
||||
|
|
@ -1235,8 +1235,11 @@ impl WlSurface {
|
|||
self.opaque_region.set(region);
|
||||
}
|
||||
}
|
||||
let mut tearing_changed = false;
|
||||
if let Some(tearing) = pending.tearing.take() {
|
||||
self.tearing.set(tearing);
|
||||
if self.tearing.replace(tearing) != tearing {
|
||||
tearing_changed = true;
|
||||
}
|
||||
}
|
||||
if let Some(content_type) = pending.content_type.take() {
|
||||
self.content_type.set(content_type);
|
||||
|
|
@ -1305,6 +1308,13 @@ impl WlSurface {
|
|||
pending.buffer_damage.clear();
|
||||
pending.surface_damage.clear();
|
||||
pending.damage_full = false;
|
||||
if tearing_changed {
|
||||
if let Some(tl) = self.toplevel.get() {
|
||||
if tl.tl_data().is_fullscreen.get() {
|
||||
self.output.get().update_presentation_type();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ use {
|
|||
time::Time,
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, Direction, DisplayNode, FloatNode, Node, NodeIds,
|
||||
NodeVisitorBase, OutputNode, PlaceholderNode, ToplevelNode, ToplevelNodeBase, VrrMode,
|
||||
WorkspaceNode,
|
||||
NodeVisitorBase, OutputNode, PlaceholderNode, TearingMode, ToplevelNode,
|
||||
ToplevelNodeBase, VrrMode, WorkspaceNode,
|
||||
},
|
||||
utils::{
|
||||
activation_token::ActivationToken, asyncevent::AsyncEvent, bindings::Bindings,
|
||||
|
|
@ -203,6 +203,7 @@ pub struct State {
|
|||
pub damage_visualizer: DamageVisualizer,
|
||||
pub default_vrr_mode: Cell<&'static VrrMode>,
|
||||
pub default_vrr_cursor_hz: Cell<Option<f64>>,
|
||||
pub default_tearing_mode: Cell<&'static TearingMode>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ impl ConnectorHandler {
|
|||
pos: Cell::new((x1, 0)),
|
||||
vrr_mode: Cell::new(self.state.default_vrr_mode.get()),
|
||||
vrr_cursor_hz: Cell::new(self.state.default_vrr_cursor_hz.get()),
|
||||
tearing_mode: Cell::new(self.state.default_tearing_mode.get()),
|
||||
});
|
||||
self.state
|
||||
.persistent_output_states
|
||||
|
|
@ -242,7 +243,7 @@ impl ConnectorHandler {
|
|||
}
|
||||
self.state.add_global(&global);
|
||||
self.state.tree_changed();
|
||||
on.update_vrr_state();
|
||||
on.update_presentation_type();
|
||||
'outer: loop {
|
||||
while let Some(event) = self.data.connector.event() {
|
||||
match event {
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ impl ToolClient {
|
|||
self_id: s.registry,
|
||||
name: s.jay_compositor.0,
|
||||
interface: JayCompositor.name(),
|
||||
version: s.jay_compositor.1.min(2),
|
||||
version: s.jay_compositor.1.min(3),
|
||||
id: id.into(),
|
||||
});
|
||||
self.jay_compositor.set(Some(id));
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use {
|
|||
wire::{JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::{Transform, VrrMode as ConfigVrrMode},
|
||||
jay_config::video::{TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode},
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -789,7 +789,12 @@ impl OutputNode {
|
|||
self.state.tree_changed();
|
||||
}
|
||||
|
||||
pub fn update_vrr_state(&self) {
|
||||
pub fn update_presentation_type(&self) {
|
||||
self.update_vrr_state();
|
||||
self.update_tearing();
|
||||
}
|
||||
|
||||
fn update_vrr_state(&self) {
|
||||
let enabled = match self.global.persistent.vrr_mode.get() {
|
||||
VrrMode::Never => false,
|
||||
VrrMode::Always => true,
|
||||
|
|
@ -821,6 +826,33 @@ impl OutputNode {
|
|||
};
|
||||
self.global.connector.connector.set_vrr_enabled(enabled);
|
||||
}
|
||||
|
||||
fn update_tearing(&self) {
|
||||
let enabled = match self.global.persistent.tearing_mode.get() {
|
||||
TearingMode::Never => false,
|
||||
TearingMode::Always => true,
|
||||
TearingMode::Fullscreen { surface } => 'get: {
|
||||
let Some(ws) = self.workspace.get() else {
|
||||
break 'get false;
|
||||
};
|
||||
let Some(tl) = ws.fullscreen.get() else {
|
||||
break 'get false;
|
||||
};
|
||||
if let Some(req) = surface {
|
||||
let Some(surface) = tl.tl_scanout_surface() else {
|
||||
break 'get false;
|
||||
};
|
||||
if req.tearing_requested {
|
||||
if !surface.tearing.get() {
|
||||
break 'get false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
};
|
||||
self.global.connector.connector.set_tearing_enabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputTitle {
|
||||
|
|
@ -1185,3 +1217,55 @@ impl VrrMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TearingMode {
|
||||
Never,
|
||||
Always,
|
||||
Fullscreen {
|
||||
surface: Option<TearingSurfaceRequirements>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct TearingSurfaceRequirements {
|
||||
tearing_requested: bool,
|
||||
}
|
||||
|
||||
impl TearingMode {
|
||||
pub const NEVER: &'static Self = &Self::Never;
|
||||
pub const ALWAYS: &'static Self = &Self::Always;
|
||||
pub const VARIANT_1: &'static Self = &Self::Fullscreen { surface: None };
|
||||
pub const VARIANT_2: &'static Self = &Self::Fullscreen {
|
||||
surface: Some(TearingSurfaceRequirements {
|
||||
tearing_requested: false,
|
||||
}),
|
||||
};
|
||||
pub const VARIANT_3: &'static Self = &Self::Fullscreen {
|
||||
surface: Some(TearingSurfaceRequirements {
|
||||
tearing_requested: true,
|
||||
}),
|
||||
};
|
||||
|
||||
pub fn from_config(mode: ConfigTearingMode) -> Option<&'static Self> {
|
||||
let res = match mode {
|
||||
ConfigTearingMode::NEVER => Self::NEVER,
|
||||
ConfigTearingMode::ALWAYS => Self::ALWAYS,
|
||||
ConfigTearingMode::VARIANT_1 => Self::VARIANT_1,
|
||||
ConfigTearingMode::VARIANT_2 => Self::VARIANT_2,
|
||||
ConfigTearingMode::VARIANT_3 => Self::VARIANT_3,
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn to_config(&self) -> ConfigVrrMode {
|
||||
match self {
|
||||
Self::NEVER => ConfigVrrMode::NEVER,
|
||||
Self::ALWAYS => ConfigVrrMode::ALWAYS,
|
||||
Self::VARIANT_1 => ConfigVrrMode::VARIANT_1,
|
||||
Self::VARIANT_2 => ConfigVrrMode::VARIANT_2,
|
||||
Self::VARIANT_3 => ConfigVrrMode::VARIANT_3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ impl WorkspaceNode {
|
|||
surface.send_feedback(&fb);
|
||||
}
|
||||
}
|
||||
self.output.get().update_vrr_state();
|
||||
self.output.get().update_presentation_type();
|
||||
}
|
||||
|
||||
pub fn remove_fullscreen_node(&self) {
|
||||
|
|
@ -195,7 +195,7 @@ impl WorkspaceNode {
|
|||
surface.send_feedback(&fb);
|
||||
}
|
||||
}
|
||||
self.output.get().update_vrr_state();
|
||||
self.output.get().update_presentation_type();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,15 @@ use crate::{
|
|||
dmabuf::DmaBuf,
|
||||
drm::sys::{
|
||||
auth_magic, drm_format_modifier, drm_format_modifier_blob, drop_master, get_version,
|
||||
revoke_lease, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT,
|
||||
revoke_lease, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, DRM_CAP_CURSOR_HEIGHT,
|
||||
DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT,
|
||||
},
|
||||
Modifier, INVALID_MODIFIER,
|
||||
},
|
||||
};
|
||||
pub use sys::{
|
||||
drm_mode_modeinfo, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET,
|
||||
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
|
||||
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_ASYNC, DRM_MODE_PAGE_FLIP_EVENT,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -339,6 +340,10 @@ impl DrmMaster {
|
|||
Ok((width, height))
|
||||
}
|
||||
|
||||
pub fn supports_async_commit(&self) -> bool {
|
||||
self.get_cap(DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP) == Ok(1)
|
||||
}
|
||||
|
||||
pub fn get_connector_info(
|
||||
&self,
|
||||
connector: DrmConnector,
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ const DRM_MODE_PROP_ATOMIC: u32 = 0x80000000;
|
|||
|
||||
pub const DRM_CAP_CURSOR_WIDTH: u64 = 0x8;
|
||||
pub const DRM_CAP_CURSOR_HEIGHT: u64 = 0x9;
|
||||
pub const DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP: u64 = 0x15;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_mode_property_enum {
|
||||
|
|
@ -865,6 +866,7 @@ struct drm_mode_atomic {
|
|||
const DRM_IOCTL_MODE_ATOMIC: u64 = drm_iowr::<drm_mode_atomic>(0xbc);
|
||||
|
||||
pub const DRM_MODE_PAGE_FLIP_EVENT: u32 = 0x01;
|
||||
pub const DRM_MODE_PAGE_FLIP_ASYNC: u32 = 0x02;
|
||||
pub const DRM_MODE_ATOMIC_TEST_ONLY: u32 = 0x0100;
|
||||
pub const DRM_MODE_ATOMIC_NONBLOCK: u32 = 0x0200;
|
||||
pub const DRM_MODE_ATOMIC_ALLOW_MODESET: u32 = 0x0400;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use {
|
|||
logging::LogLevel,
|
||||
status::MessageFormat,
|
||||
theme::Color,
|
||||
video::{GfxApi, Transform, VrrMode},
|
||||
video::{GfxApi, TearingMode, Transform, VrrMode},
|
||||
Axis, Direction, Workspace,
|
||||
},
|
||||
std::{
|
||||
|
|
@ -207,6 +207,7 @@ pub struct Output {
|
|||
pub transform: Option<Transform>,
|
||||
pub mode: Option<Mode>,
|
||||
pub vrr: Option<Vrr>,
|
||||
pub tearing: Option<Tearing>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -292,6 +293,11 @@ pub struct Vrr {
|
|||
pub cursor_hz: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Tearing {
|
||||
pub mode: Option<TearingMode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Shortcut {
|
||||
pub mask: Modifiers,
|
||||
|
|
@ -326,6 +332,7 @@ pub struct Config {
|
|||
pub focus_follows_mouse: bool,
|
||||
pub window_management_key: Option<ModifiedKeySym>,
|
||||
pub vrr: Option<Vrr>,
|
||||
pub tearing: Option<Tearing>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ mod output_match;
|
|||
mod repeat_rate;
|
||||
pub mod shortcuts;
|
||||
mod status;
|
||||
mod tearing;
|
||||
mod theme;
|
||||
mod vrr;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use {
|
|||
ShortcutsParserError,
|
||||
},
|
||||
status::StatusParser,
|
||||
tearing::TearingParser,
|
||||
theme::ThemeParser,
|
||||
vrr::VrrParser,
|
||||
},
|
||||
|
|
@ -108,6 +109,7 @@ impl Parser for ConfigParser<'_> {
|
|||
focus_follows_mouse,
|
||||
window_management_key_val,
|
||||
vrr_val,
|
||||
tearing_val,
|
||||
),
|
||||
) = ext.extract((
|
||||
(
|
||||
|
|
@ -141,6 +143,7 @@ impl Parser for ConfigParser<'_> {
|
|||
recover(opt(bol("focus-follows-mouse"))),
|
||||
recover(opt(str("window-management-key"))),
|
||||
opt(val("vrr")),
|
||||
opt(val("tearing")),
|
||||
),
|
||||
))?;
|
||||
let mut keymap = None;
|
||||
|
|
@ -314,6 +317,15 @@ impl Parser for ConfigParser<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut tearing = None;
|
||||
if let Some(value) = tearing_val {
|
||||
match value.parse(&mut TearingParser(self.0)) {
|
||||
Ok(v) => tearing = Some(v),
|
||||
Err(e) => {
|
||||
log::warn!("Could not parse tearing setting: {}", self.0.error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Config {
|
||||
keymap,
|
||||
repeat_rate,
|
||||
|
|
@ -339,6 +351,7 @@ impl Parser for ConfigParser<'_> {
|
|||
focus_follows_mouse: focus_follows_mouse.despan().unwrap_or(true),
|
||||
window_management_key,
|
||||
vrr,
|
||||
tearing,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use {
|
|||
parsers::{
|
||||
mode::ModeParser,
|
||||
output_match::{OutputMatchParser, OutputMatchParserError},
|
||||
tearing::TearingParser,
|
||||
vrr::VrrParser,
|
||||
},
|
||||
Output,
|
||||
|
|
@ -47,16 +48,18 @@ impl<'a> Parser for OutputParser<'a> {
|
|||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.cx, span, table);
|
||||
let (name, match_val, x, y, scale, transform, mode, vrr_val) = ext.extract((
|
||||
opt(str("name")),
|
||||
val("match"),
|
||||
recover(opt(s32("x"))),
|
||||
recover(opt(s32("y"))),
|
||||
recover(opt(fltorint("scale"))),
|
||||
recover(opt(str("transform"))),
|
||||
opt(val("mode")),
|
||||
opt(val("vrr")),
|
||||
))?;
|
||||
let (name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val) =
|
||||
ext.extract((
|
||||
opt(str("name")),
|
||||
val("match"),
|
||||
recover(opt(s32("x"))),
|
||||
recover(opt(s32("y"))),
|
||||
recover(opt(fltorint("scale"))),
|
||||
recover(opt(str("transform"))),
|
||||
opt(val("mode")),
|
||||
opt(val("vrr")),
|
||||
opt(val("tearing")),
|
||||
))?;
|
||||
let transform = match transform {
|
||||
None => None,
|
||||
Some(t) => match t.value {
|
||||
|
|
@ -107,6 +110,15 @@ impl<'a> Parser for OutputParser<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut tearing = None;
|
||||
if let Some(value) = tearing_val {
|
||||
match value.parse(&mut TearingParser(self.cx)) {
|
||||
Ok(v) => tearing = Some(v),
|
||||
Err(e) => {
|
||||
log::warn!("Could not parse tearing setting: {}", self.cx.error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Output {
|
||||
name: name.despan().map(|v| v.to_string()),
|
||||
match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?,
|
||||
|
|
@ -116,6 +128,7 @@ impl<'a> Parser for OutputParser<'a> {
|
|||
transform,
|
||||
mode,
|
||||
vrr,
|
||||
tearing,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
78
toml-config/src/config/parsers/tearing.rs
Normal file
78
toml-config/src/config/parsers/tearing.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
use {
|
||||
crate::{
|
||||
config::{
|
||||
context::Context,
|
||||
extractor::{opt, val, Extractor, ExtractorError},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
Tearing,
|
||||
},
|
||||
toml::{
|
||||
toml_span::{Span, Spanned, SpannedExt},
|
||||
toml_value::Value,
|
||||
},
|
||||
},
|
||||
indexmap::IndexMap,
|
||||
jay_config::video::TearingMode,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TearingParserError {
|
||||
#[error(transparent)]
|
||||
Expected(#[from] UnexpectedDataType),
|
||||
#[error(transparent)]
|
||||
Extract(#[from] ExtractorError),
|
||||
}
|
||||
|
||||
pub struct TearingParser<'a>(pub &'a Context<'a>);
|
||||
|
||||
impl Parser for TearingParser<'_> {
|
||||
type Value = Tearing;
|
||||
type Error = TearingParserError;
|
||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||
|
||||
fn parse_table(
|
||||
&mut self,
|
||||
span: Span,
|
||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.0, span, table);
|
||||
let mode = ext.extract(opt(val("mode")))?;
|
||||
let mode = mode.and_then(|m| match m.parse(&mut TearingModeParser) {
|
||||
Ok(m) => Some(m),
|
||||
Err(e) => {
|
||||
log::error!("Could not parse mode: {}", self.0.error(e));
|
||||
None
|
||||
}
|
||||
});
|
||||
Ok(Tearing { mode })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TearingModeParserError {
|
||||
#[error(transparent)]
|
||||
Expected(#[from] UnexpectedDataType),
|
||||
#[error("Unknown mode {0}")]
|
||||
UnknownMode(String),
|
||||
}
|
||||
|
||||
struct TearingModeParser;
|
||||
|
||||
impl Parser for TearingModeParser {
|
||||
type Value = TearingMode;
|
||||
type Error = TearingModeParserError;
|
||||
const EXPECTED: &'static [DataType] = &[DataType::String];
|
||||
|
||||
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
|
||||
let mode = match string {
|
||||
"never" => TearingMode::NEVER,
|
||||
"always" => TearingMode::ALWAYS,
|
||||
"variant1" => TearingMode::VARIANT_1,
|
||||
"variant2" => TearingMode::VARIANT_2,
|
||||
"variant3" => TearingMode::VARIANT_3,
|
||||
_ => return Err(TearingModeParserError::UnknownMode(string.to_string()).spanned(span)),
|
||||
};
|
||||
Ok(mode)
|
||||
}
|
||||
}
|
||||
|
|
@ -30,8 +30,8 @@ use {
|
|||
video::{
|
||||
connectors, drm_devices, on_connector_connected, on_connector_disconnected,
|
||||
on_graphics_initialized, on_new_connector, on_new_drm_device,
|
||||
set_direct_scanout_enabled, set_gfx_api, set_vrr_cursor_hz, set_vrr_mode, Connector,
|
||||
DrmDevice,
|
||||
set_direct_scanout_enabled, set_gfx_api, set_tearing_mode, set_vrr_cursor_hz,
|
||||
set_vrr_mode, Connector, DrmDevice,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, io::ErrorKind, path::PathBuf, rc::Rc},
|
||||
|
|
@ -564,6 +564,11 @@ impl Output {
|
|||
c.set_vrr_cursor_hz(hz);
|
||||
}
|
||||
}
|
||||
if let Some(tearing) = &self.tearing {
|
||||
if let Some(mode) = tearing.mode {
|
||||
c.set_tearing_mode(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1034,6 +1039,11 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
set_vrr_cursor_hz(hz);
|
||||
}
|
||||
}
|
||||
if let Some(tearing) = config.tearing {
|
||||
if let Some(mode) = tearing.mode {
|
||||
set_tearing_mode(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_command(exec: &Exec) -> Command {
|
||||
|
|
|
|||
|
|
@ -581,6 +581,10 @@
|
|||
"vrr": {
|
||||
"description": "Configures the default VRR settings.\n\nThis can be overwritten for individual outputs.\n\nBy default, the VRR mode is `never` and the cursor refresh rate is unbounded.\n\n- Example:\n \n ```toml\n vrr = { mode = \"always\", cursor-hz = 90 }\n ```\n",
|
||||
"$ref": "#/$defs/Vrr"
|
||||
},
|
||||
"tearing": {
|
||||
"description": "Configures the default tearing settings.\n\nThis can be overwritten for individual outputs.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n tearing.mode = \"never\"\n ```\n",
|
||||
"$ref": "#/$defs/Tearing"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
|
@ -1031,6 +1035,10 @@
|
|||
"vrr": {
|
||||
"description": "Configures the VRR settings of this output.\n\nBy default, the VRR mode is `never` and the cursor refresh rate is unbounded.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n vrr = { mode = \"always\", cursor-hz = 90 }\n ```\n",
|
||||
"$ref": "#/$defs/Vrr"
|
||||
},
|
||||
"tearing": {
|
||||
"description": "Configures the tearing settings of this output.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n tearing.mode = \"never\"\n ```\n",
|
||||
"$ref": "#/$defs/Tearing"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
@ -1148,6 +1156,28 @@
|
|||
"exec"
|
||||
]
|
||||
},
|
||||
"Tearing": {
|
||||
"description": "Describes tearing settings.\n\n- Example:\n\n ```toml\n tearing.mode = \"never\"\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mode": {
|
||||
"description": "The tearing mode.",
|
||||
"$ref": "#/$defs/TearingMode"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"TearingMode": {
|
||||
"type": "string",
|
||||
"description": "The tearing mode of an output.\n\n- Example:\n\n ```toml\n tearing.mode = \"never\"\n ```\n",
|
||||
"enum": [
|
||||
"always",
|
||||
"never",
|
||||
"variant1",
|
||||
"variant2",
|
||||
"variant3"
|
||||
]
|
||||
},
|
||||
"Theme": {
|
||||
"description": "The theme of the compositor.\n",
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -1126,6 +1126,22 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a [Vrr](#types-Vrr).
|
||||
|
||||
- `tearing` (optional):
|
||||
|
||||
Configures the default tearing settings.
|
||||
|
||||
This can be overwritten for individual outputs.
|
||||
|
||||
By default, the tearing mode is `variant3`.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
tearing.mode = "never"
|
||||
```
|
||||
|
||||
The value of this field should be a [Tearing](#types-Tearing).
|
||||
|
||||
|
||||
<a name="types-Connector"></a>
|
||||
### `Connector`
|
||||
|
|
@ -2198,6 +2214,22 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a [Vrr](#types-Vrr).
|
||||
|
||||
- `tearing` (optional):
|
||||
|
||||
Configures the tearing settings of this output.
|
||||
|
||||
By default, the tearing mode is `variant3`.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[outputs]]
|
||||
match.serial-number = "33K03894SL0"
|
||||
tearing.mode = "never"
|
||||
```
|
||||
|
||||
The value of this field should be a [Tearing](#types-Tearing).
|
||||
|
||||
|
||||
<a name="types-OutputMatch"></a>
|
||||
### `OutputMatch`
|
||||
|
|
@ -2532,6 +2564,66 @@ The table has the following fields:
|
|||
The value of this field should be a string.
|
||||
|
||||
|
||||
<a name="types-Tearing"></a>
|
||||
### `Tearing`
|
||||
|
||||
Describes tearing settings.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
tearing.mode = "never"
|
||||
```
|
||||
|
||||
Values of this type should be tables.
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `mode` (optional):
|
||||
|
||||
The tearing mode.
|
||||
|
||||
The value of this field should be a [TearingMode](#types-TearingMode).
|
||||
|
||||
|
||||
<a name="types-TearingMode"></a>
|
||||
### `TearingMode`
|
||||
|
||||
The tearing mode of an output.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
tearing.mode = "never"
|
||||
```
|
||||
|
||||
Values of this type should be strings.
|
||||
|
||||
The string should have one of the following values:
|
||||
|
||||
- `always`:
|
||||
|
||||
Tearing is never enabled.
|
||||
|
||||
- `never`:
|
||||
|
||||
Tearing is always enabled.
|
||||
|
||||
- `variant1`:
|
||||
|
||||
Tearing is enabled when one or more applications are displayed fullscreen.
|
||||
|
||||
- `variant2`:
|
||||
|
||||
Tearing is enabled when a single application is displayed fullscreen.
|
||||
|
||||
- `variant3`:
|
||||
|
||||
Tearing is enabled when a single application is displayed and the application has
|
||||
requested tearing.
|
||||
|
||||
|
||||
|
||||
<a name="types-Theme"></a>
|
||||
### `Theme`
|
||||
|
||||
|
|
|
|||
|
|
@ -1573,6 +1573,21 @@ Output:
|
|||
match.serial-number = "33K03894SL0"
|
||||
vrr = { mode = "always", cursor-hz = 90 }
|
||||
```
|
||||
tearing:
|
||||
ref: Tearing
|
||||
required: false
|
||||
description: |
|
||||
Configures the tearing settings of this output.
|
||||
|
||||
By default, the tearing mode is `variant3`.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[[outputs]]
|
||||
match.serial-number = "33K03894SL0"
|
||||
tearing.mode = "never"
|
||||
```
|
||||
|
||||
|
||||
Transform:
|
||||
|
|
@ -2180,6 +2195,21 @@ Config:
|
|||
```toml
|
||||
vrr = { mode = "always", cursor-hz = 90 }
|
||||
```
|
||||
tearing:
|
||||
ref: Tearing
|
||||
required: false
|
||||
description: |
|
||||
Configures the default tearing settings.
|
||||
|
||||
This can be overwritten for individual outputs.
|
||||
|
||||
By default, the tearing mode is `variant3`.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
tearing.mode = "never"
|
||||
```
|
||||
|
||||
|
||||
Idle:
|
||||
|
|
@ -2367,3 +2397,45 @@ VrrHz:
|
|||
description: The string `none` can be used to disable the limiter.
|
||||
- kind: number
|
||||
description: The refresh rate in HZ.
|
||||
|
||||
|
||||
Tearing:
|
||||
kind: table
|
||||
description: |
|
||||
Describes tearing settings.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
tearing.mode = "never"
|
||||
```
|
||||
fields:
|
||||
mode:
|
||||
ref: TearingMode
|
||||
required: false
|
||||
description: The tearing mode.
|
||||
|
||||
|
||||
TearingMode:
|
||||
description: |
|
||||
The tearing mode of an output.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
tearing.mode = "never"
|
||||
```
|
||||
kind: string
|
||||
values:
|
||||
- value: always
|
||||
description: Tearing is never enabled.
|
||||
- value: never
|
||||
description: Tearing is always enabled.
|
||||
- value: variant1
|
||||
description: Tearing is enabled when one or more applications are displayed fullscreen.
|
||||
- value: variant2
|
||||
description: Tearing is enabled when a single application is displayed fullscreen.
|
||||
- value: variant3
|
||||
description: |
|
||||
Tearing is enabled when a single application is displayed and the application has
|
||||
requested tearing.
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ request set_vrr_cursor_hz (since = 2) {
|
|||
hz: pod(f64),
|
||||
}
|
||||
|
||||
request set_tearing_mode (since = 3) {
|
||||
output: str,
|
||||
mode: u32,
|
||||
}
|
||||
|
||||
# events
|
||||
|
||||
event global {
|
||||
|
|
@ -132,3 +137,7 @@ event vrr_state (since = 2) {
|
|||
event vrr_cursor_hz (since = 2) {
|
||||
hz: pod(f64),
|
||||
}
|
||||
|
||||
event tearing_state (since = 3) {
|
||||
mode: u32,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue