backend: support outputs with arbitrary modes
This commit is contained in:
parent
dd10e1a585
commit
1a9753847a
23 changed files with 199 additions and 59 deletions
|
|
@ -1191,6 +1191,19 @@ impl ConfigClient {
|
|||
modes.into_iter().map(WireMode::to_mode).collect()
|
||||
}
|
||||
|
||||
pub fn connector_supports_arbitrary_modes(&self, connector: Connector) -> bool {
|
||||
let res =
|
||||
self.send_with_response(&ClientMessage::ConnectorSupportsArbitraryModes { connector });
|
||||
get_response!(
|
||||
res,
|
||||
false,
|
||||
ConnectorSupportsArbitraryModes {
|
||||
supports_arbitrary_modes
|
||||
}
|
||||
);
|
||||
supports_arbitrary_modes
|
||||
}
|
||||
|
||||
pub fn connector_size(&self, connector: Connector) -> (i32, i32) {
|
||||
let res = self.send_with_response(&ClientMessage::ConnectorSize { connector });
|
||||
get_response!(res, (0, 0), ConnectorSize { width, height });
|
||||
|
|
|
|||
|
|
@ -846,6 +846,9 @@ pub enum ClientMessage<'a> {
|
|||
monospace: Option<Vec<&'a str>>,
|
||||
},
|
||||
OpenControlCenter,
|
||||
ConnectorSupportsArbitraryModes {
|
||||
connector: Connector,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
@ -1096,6 +1099,9 @@ pub enum Response {
|
|||
KeymapFromNames {
|
||||
keymap: Keymap,
|
||||
},
|
||||
ConnectorSupportsArbitraryModes {
|
||||
supports_arbitrary_modes: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -157,6 +157,14 @@ impl Connector {
|
|||
get!(Vec::new()).connector_modes(self)
|
||||
}
|
||||
|
||||
/// Returns whether this connector supports arbitrary modes.
|
||||
pub fn supports_arbitrary_modes(self) -> bool {
|
||||
if !self.exists() {
|
||||
return false;
|
||||
}
|
||||
get!(false).connector_supports_arbitrary_modes(self)
|
||||
}
|
||||
|
||||
/// Returns the logical width of the connector.
|
||||
///
|
||||
/// The returned value will be different from `mode().width()` if the scale is not 1.
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ impl Display for Mode {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MonitorInfo {
|
||||
pub modes: Vec<Mode>,
|
||||
pub modes: Option<Vec<Mode>>,
|
||||
pub output_id: Rc<OutputId>,
|
||||
pub width_mm: i32,
|
||||
pub height_mm: i32,
|
||||
|
|
|
|||
|
|
@ -1959,7 +1959,7 @@ impl MetalBackend {
|
|||
let mut state = dd.persistent.state.borrow().clone();
|
||||
state.serial = self.state.backend_connector_state_serials.next();
|
||||
connector.send_event(ConnectorEvent::Connected(MonitorInfo {
|
||||
modes,
|
||||
modes: Some(modes),
|
||||
output_id: dd.output_id.clone(),
|
||||
width_mm: dd.mm_width as _,
|
||||
height_mm: dd.mm_height as _,
|
||||
|
|
|
|||
|
|
@ -590,7 +590,7 @@ impl XBackend {
|
|||
.backend_events
|
||||
.push(BackendEvent::NewConnector(output.clone()));
|
||||
output.events.push(ConnectorEvent::Connected(MonitorInfo {
|
||||
modes: vec![],
|
||||
modes: Some(vec![]),
|
||||
output_id: Rc::new(OutputId::new(
|
||||
String::new(),
|
||||
"X.Org Foundation".to_string(),
|
||||
|
|
|
|||
|
|
@ -527,6 +527,7 @@ struct Output {
|
|||
pub blend_space: Option<String>,
|
||||
pub native_gamut: Option<Primaries>,
|
||||
pub use_native_gamut: bool,
|
||||
pub arbitrary_modes: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -641,9 +642,22 @@ impl Randr {
|
|||
log::error!("Connector {} is not connected", connector.name);
|
||||
return;
|
||||
};
|
||||
let Some(mode) = output.modes.iter().find(|m| {
|
||||
m.width == t.width && m.height == t.height && m.refresh_rate() == t.refresh_rate
|
||||
}) else {
|
||||
let mode = 'mode: {
|
||||
if let Some(mode) = output.modes.iter().find(|m| {
|
||||
m.width == t.width
|
||||
&& m.height == t.height
|
||||
&& m.refresh_rate() == t.refresh_rate
|
||||
}) {
|
||||
break 'mode *mode;
|
||||
}
|
||||
if output.arbitrary_modes {
|
||||
break 'mode Mode {
|
||||
width: t.width,
|
||||
height: t.height,
|
||||
refresh_rate_millihz: (t.refresh_rate * 1_000.0).round() as u32,
|
||||
current: false,
|
||||
};
|
||||
}
|
||||
log::error!(
|
||||
"Output {} does not support this refresh rate",
|
||||
connector.name
|
||||
|
|
@ -1082,6 +1096,9 @@ impl Randr {
|
|||
p.b.0.0, p.b.1.0, p.wp.0.0, p.wp.1.0
|
||||
);
|
||||
}
|
||||
if o.arbitrary_modes {
|
||||
println!(" supports arbitrary modes");
|
||||
}
|
||||
if o.modes.is_not_empty() && modes {
|
||||
println!(" modes:");
|
||||
for mode in &o.modes {
|
||||
|
|
@ -1280,6 +1297,12 @@ impl Randr {
|
|||
let output = c.output.as_mut().unwrap();
|
||||
output.use_native_gamut = true;
|
||||
});
|
||||
jay_randr::ArbitraryModes::handle(tc, randr, data.clone(), |data, _| {
|
||||
let mut data = data.borrow_mut();
|
||||
let c = data.connectors.last_mut().unwrap();
|
||||
let output = c.output.as_mut().unwrap();
|
||||
output.arbitrary_modes = true;
|
||||
});
|
||||
tc.round_trip().await;
|
||||
data.borrow_mut().clone()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -754,7 +754,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
state.globals.name(),
|
||||
state,
|
||||
&connector_data,
|
||||
Vec::new(),
|
||||
Some(Vec::new()),
|
||||
0,
|
||||
0,
|
||||
&output_id,
|
||||
|
|
|
|||
|
|
@ -1205,6 +1205,7 @@ impl ConfigProxyHandler {
|
|||
.global
|
||||
.modes
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|m| WireMode {
|
||||
width: m.width,
|
||||
height: m.height,
|
||||
|
|
@ -1215,6 +1216,17 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_supports_arbitrary_modes(
|
||||
&self,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
self.respond(Response::ConnectorSupportsArbitraryModes {
|
||||
supports_arbitrary_modes: connector.global.modes.is_none(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_name(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
self.respond(Response::GetConnectorName {
|
||||
|
|
@ -3328,6 +3340,9 @@ impl ConfigProxyHandler {
|
|||
monospace,
|
||||
} => self.handle_set_egui_fonts(proportional, monospace),
|
||||
ClientMessage::OpenControlCenter => self.handle_open_control_center(),
|
||||
ClientMessage::ConnectorSupportsArbitraryModes { connector } => self
|
||||
.handle_connector_supports_arbitrary_modes(connector)
|
||||
.wrn("connector_supports_arbitrary_modes")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use {
|
|||
egui::{
|
||||
Align, Button, Checkbox, Color32, ComboBox, DragValue, EventFilter, FontId, Frame, Grid,
|
||||
Id, Key, Layout, PointerButton, Rect, ScrollArea, Sense, Shadow, Stroke, StrokeKind, Style,
|
||||
TextFormat, Ui, UiBuilder, Vec2, Widget, WidgetText, pos2, text::LayoutJob, vec2,
|
||||
TextFormat, Ui, UiBuilder, Vec2, Widget, WidgetText, emath, pos2, text::LayoutJob, vec2,
|
||||
},
|
||||
egui_tiles::{
|
||||
Behavior, Container, Linear, LinearDir, ResizeState, SimplificationOptions, Tile, TileId,
|
||||
|
|
@ -1087,15 +1087,33 @@ fn show_mode(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
|
|||
)
|
||||
};
|
||||
if let Some(monitor_info) = &m.monitor_info
|
||||
&& monitor_info.modes.len() > 1
|
||||
&& let Some(modes) = &monitor_info.modes
|
||||
&& modes.len() > 1
|
||||
{
|
||||
ComboBox::from_id_salt("modes")
|
||||
.selected_text(mode_text(mode))
|
||||
.show_ui(ui, |ui| {
|
||||
for v in &monitor_info.modes {
|
||||
for v in modes {
|
||||
ui.selectable_value(&mut mode, *v, mode_text(*v));
|
||||
}
|
||||
});
|
||||
} else if let Some(monitor_info) = &m.monitor_info
|
||||
&& monitor_info.modes.is_none()
|
||||
{
|
||||
ui.horizontal(|ui| {
|
||||
fn value<T: emath::Numeric>(ui: &mut Ui, v: &mut T, min: T, max: T) -> bool {
|
||||
let res = DragValue::new(v).range(min..=max).speed(1.0).ui(ui);
|
||||
res.changed()
|
||||
}
|
||||
value(ui, &mut mode.width, 1, u16::MAX as i32);
|
||||
ui.label("x");
|
||||
value(ui, &mut mode.height, 1, u16::MAX as i32);
|
||||
ui.label("@");
|
||||
let mut hz = mode.refresh_rate_millihz as f64 / 1_000.0;
|
||||
if value(ui, &mut hz, 0.0, 1_000_000.0) {
|
||||
mode.refresh_rate_millihz = (hz * 1_000.0).round() as u32;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ui.label(mode_text(mode));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,10 @@ impl HeadName {
|
|||
|
||||
pub(in super::super) fn send_modes(&self, state: &HeadState) {
|
||||
self.client.event(Reset { self_id: self.id });
|
||||
if let Some(mi) = &state.monitor_info {
|
||||
for mode in &mi.modes {
|
||||
if let Some(mi) = &state.monitor_info
|
||||
&& let Some(modes) = &mi.modes
|
||||
{
|
||||
for mode in modes {
|
||||
self.client.event(Mode {
|
||||
self_id: self.id,
|
||||
width: mode.width,
|
||||
|
|
@ -73,7 +75,8 @@ impl JayHeadExtModeSetterV1RequestHandler for HeadName {
|
|||
.borrow()
|
||||
.monitor_info
|
||||
.as_deref()
|
||||
.map(|i| i.modes.len())
|
||||
.and_then(|i| i.modes.as_ref())
|
||||
.map(|m| m.len())
|
||||
.unwrap_or(0);
|
||||
let idx = req.idx as usize;
|
||||
if idx >= num_modes {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ impl HeadName {
|
|||
pub(in super::super) fn send_info(&self, state: &HeadState) {
|
||||
self.send_reset();
|
||||
if let Some(mi) = &state.monitor_info {
|
||||
for mode in &mi.modes {
|
||||
for mode in mi.modes.iter().flatten() {
|
||||
self.send_mode(mode);
|
||||
}
|
||||
self.send_manufacturer(&mi.output_id.manufacturer);
|
||||
|
|
|
|||
|
|
@ -422,7 +422,13 @@ impl JayHeadManagerSessionV1RequestHandler for JayHeadManagerSessionV1 {
|
|||
to_send |= COMPOSITOR_SPACE_INFO_SIZE;
|
||||
}
|
||||
HeadOp::SetMode(i) => {
|
||||
state.mode = snapshot.monitor_info.as_deref().unwrap().modes[i];
|
||||
state.mode = snapshot
|
||||
.monitor_info
|
||||
.as_deref()
|
||||
.unwrap()
|
||||
.modes
|
||||
.as_ref()
|
||||
.unwrap()[i];
|
||||
state.update_size();
|
||||
to_send |= MODE_INFO;
|
||||
to_send |= COMPOSITOR_SPACE_INFO_SIZE;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError);
|
|||
|
||||
impl Global for JayCompositorGlobal {
|
||||
fn version(&self) -> u32 {
|
||||
28
|
||||
29
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use {
|
|||
},
|
||||
jay_config::video::{TearingMode as ConfigTearingMode, VrrMode as ConfigVrrMode},
|
||||
linearize::LinearizeExt,
|
||||
std::rc::Rc,
|
||||
std::{rc::Rc, slice},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
|
|
@ -36,6 +36,7 @@ const COLORIMETRY_SINCE: Version = Version(15);
|
|||
const BRIGHTNESS_SINCE: Version = Version(16);
|
||||
const BLEND_SPACE_SINCE: Version = Version(21);
|
||||
const NATIVE_GAMUT_SINCE: Version = Version(23);
|
||||
const ARBITRARY_MODES_SINCE: Version = Version(29);
|
||||
|
||||
impl JayRandr {
|
||||
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
||||
|
|
@ -162,7 +163,11 @@ impl JayRandr {
|
|||
}
|
||||
}
|
||||
let current_mode = global.mode.get();
|
||||
for mode in &global.modes {
|
||||
for mode in global
|
||||
.modes
|
||||
.as_deref()
|
||||
.unwrap_or(slice::from_ref(¤t_mode))
|
||||
{
|
||||
self.client.event(Mode {
|
||||
self_id: self.id,
|
||||
width: mode.width,
|
||||
|
|
@ -232,6 +237,9 @@ impl JayRandr {
|
|||
self.client.event(UseNativeGamut { self_id: self.id });
|
||||
}
|
||||
}
|
||||
if self.version >= ARBITRARY_MODES_SINCE && global.modes.is_none() {
|
||||
self.client.event(ArbitraryModes { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
fn send_error(&self, msg: &str) {
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ pub struct WlOutputGlobal {
|
|||
pub output_id: Rc<OutputId>,
|
||||
pub mode: Cell<backend::Mode>,
|
||||
pub refresh_nsec: Cell<u64>,
|
||||
pub modes: Vec<backend::Mode>,
|
||||
pub modes: Option<Vec<backend::Mode>>,
|
||||
pub formats: CloneCell<Rc<Vec<&'static Format>>>,
|
||||
pub format: Cell<&'static Format>,
|
||||
pub width_mm: i32,
|
||||
|
|
@ -199,7 +199,7 @@ impl WlOutputGlobal {
|
|||
name: GlobalName,
|
||||
state: &Rc<State>,
|
||||
connector: &Rc<ConnectorData>,
|
||||
modes: Vec<backend::Mode>,
|
||||
modes: Option<Vec<backend::Mode>>,
|
||||
width_mm: i32,
|
||||
height_mm: i32,
|
||||
output_id: &Rc<OutputId>,
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ use {
|
|||
scale,
|
||||
state::OutputData,
|
||||
tree::{self, VrrMode},
|
||||
utils::copyhashmap::CopyHashMap,
|
||||
wire::{ZwlrOutputHeadV1Id, zwlr_output_head_v1::*},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
|
@ -44,7 +44,7 @@ pub struct ZwlrOutputHeadV1 {
|
|||
pub(super) manager: Rc<ZwlrOutputManagerV1>,
|
||||
pub(super) head_id: WlrOutputHeadId,
|
||||
pub(super) connector_id: ConnectorId,
|
||||
pub(super) modes: AHashMap<backend::Mode, Rc<ZwlrOutputModeV1>>,
|
||||
pub(super) modes: CopyHashMap<backend::Mode, Rc<ZwlrOutputModeV1>>,
|
||||
}
|
||||
|
||||
impl ZwlrOutputHeadV1 {
|
||||
|
|
@ -177,13 +177,21 @@ impl ZwlrOutputHeadV1 {
|
|||
}
|
||||
|
||||
pub fn handle_mode_change(&self, new: backend::Mode) {
|
||||
let Some(mode) = self.modes.get(&new) else {
|
||||
let Some(mode) = self.modes.get(&new).or_else(|| {
|
||||
self.manager
|
||||
.create_mode(self.head_id, &new, false, false)
|
||||
.inspect(|mode| {
|
||||
self.modes.set(new, mode.clone());
|
||||
self.send_mode(mode);
|
||||
mode.send();
|
||||
})
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
if mode.destroyed.get() {
|
||||
return;
|
||||
}
|
||||
self.send_current_mode(mode);
|
||||
self.send_current_mode(&mode);
|
||||
self.manager.schedule_done();
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +215,7 @@ impl ZwlrOutputHeadV1 {
|
|||
|
||||
pub fn handle_disconnected(&self) {
|
||||
self.send_finished();
|
||||
for mode in self.modes.values() {
|
||||
for mode in self.modes.lock().values() {
|
||||
if !mode.destroyed.get() {
|
||||
mode.send_finished();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::Mode,
|
||||
client::{CAP_HEAD_MANAGER, Client, ClientCaps, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::wlr_output_manager::{
|
||||
zwlr_output_configuration_v1::ZwlrOutputConfigurationV1,
|
||||
zwlr_output_head_v1::{
|
||||
ADAPTIVE_SYNC_SINCE, MAKE_SINCE, MODEL_SINCE, SERIAL_NUMBER_SINCE, ZwlrOutputHeadV1,
|
||||
ADAPTIVE_SYNC_SINCE, MAKE_SINCE, MODEL_SINCE, SERIAL_NUMBER_SINCE, WlrOutputHeadId,
|
||||
ZwlrOutputHeadV1,
|
||||
},
|
||||
zwlr_output_mode_v1::ZwlrOutputModeV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
state::OutputData,
|
||||
utils::numcell::NumCell,
|
||||
utils::{copyhashmap::CopyHashMap, numcell::NumCell},
|
||||
wire::{ZwlrOutputManagerV1Id, zwlr_output_manager_v1::*},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
isnt::std_1::string::IsntStringExt,
|
||||
std::{cell::Cell, rc::Rc},
|
||||
std::{cell::Cell, rc::Rc, slice},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
|
|
@ -134,38 +135,27 @@ impl ZwlrOutputManagerV1 {
|
|||
let state_mode = output.connector.state.borrow().mode;
|
||||
let head_id = self.client.state.wlr_output_managers.head_ids.next();
|
||||
let mut modes_list = vec![];
|
||||
let mut modes = AHashMap::new();
|
||||
let modes = CopyHashMap::new();
|
||||
let mut have_current = false;
|
||||
for (idx, mode) in mi.modes.iter().enumerate() {
|
||||
if modes.contains_key(mode) {
|
||||
for (idx, mode) in mi
|
||||
.modes
|
||||
.as_deref()
|
||||
.unwrap_or(slice::from_ref(&state_mode))
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
if modes.contains(mode) {
|
||||
continue;
|
||||
}
|
||||
let current = !have_current && *mode == state_mode;
|
||||
if current {
|
||||
have_current = true;
|
||||
}
|
||||
let id = match self.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.client.error(e);
|
||||
return;
|
||||
}
|
||||
let Some(output_mode) = self.create_mode(head_id, mode, idx == 0, current) else {
|
||||
return;
|
||||
};
|
||||
let output_mode = Rc::new(ZwlrOutputModeV1 {
|
||||
id,
|
||||
head_id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
mode: *mode,
|
||||
preferred: idx == 0,
|
||||
initial_current: current,
|
||||
destroyed: Cell::new(false),
|
||||
});
|
||||
track!(self.client, output_mode);
|
||||
self.client.add_server_obj(&output_mode);
|
||||
modes_list.push(output_mode.clone());
|
||||
modes.insert(*mode, output_mode);
|
||||
modes.set(*mode, output_mode);
|
||||
}
|
||||
let head = Rc::new(ZwlrOutputHeadV1 {
|
||||
id,
|
||||
|
|
@ -244,6 +234,36 @@ impl ZwlrOutputManagerV1 {
|
|||
.queue
|
||||
.push(self.clone());
|
||||
}
|
||||
|
||||
pub(super) fn create_mode(
|
||||
self: &Rc<Self>,
|
||||
head_id: WlrOutputHeadId,
|
||||
mode: &Mode,
|
||||
preferred: bool,
|
||||
initial_current: bool,
|
||||
) -> Option<Rc<ZwlrOutputModeV1>> {
|
||||
let id = match self.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.client.error(e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let output_mode = Rc::new(ZwlrOutputModeV1 {
|
||||
id,
|
||||
head_id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
mode: *mode,
|
||||
preferred,
|
||||
initial_current,
|
||||
destroyed: Cell::new(false),
|
||||
});
|
||||
track!(self.client, output_mode);
|
||||
self.client.add_server_obj(&output_mode);
|
||||
Some(output_mode)
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ impl TestBackend {
|
|||
refresh_rate_millihz: 60_000,
|
||||
};
|
||||
let default_monitor_info = MonitorInfo {
|
||||
modes: vec![mode],
|
||||
modes: Some(vec![mode]),
|
||||
output_id: Rc::new(OutputId {
|
||||
connector: None,
|
||||
manufacturer: "jay".to_string(),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
damage_calls: NumCell::new(0),
|
||||
});
|
||||
let new_monitor_info = MonitorInfo {
|
||||
modes: vec![],
|
||||
modes: Some(vec![]),
|
||||
output_id: Rc::new(OutputId {
|
||||
connector: None,
|
||||
manufacturer: "jay".to_string(),
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ impl ToolClient {
|
|||
self_id: s.registry,
|
||||
name: s.jay_compositor.0,
|
||||
interface: JayCompositor.name(),
|
||||
version: s.jay_compositor.1.min(28),
|
||||
version: s.jay_compositor.1.min(29),
|
||||
id: id.into(),
|
||||
});
|
||||
self.jay_compositor.set(Some(id));
|
||||
|
|
|
|||
|
|
@ -830,11 +830,20 @@ impl Output {
|
|||
Some(rr) => m.refresh_rate() as f64 / 1000.0 == rr,
|
||||
}
|
||||
});
|
||||
match m {
|
||||
None => {
|
||||
'set_mode: {
|
||||
let (w, h, mhz) = 'mode: {
|
||||
if let Some(m) = m {
|
||||
break 'mode (m.width(), m.height(), m.refresh_rate());
|
||||
}
|
||||
if c.supports_arbitrary_modes()
|
||||
&& let Some(refresh) = mode.refresh_rate
|
||||
{
|
||||
break 'mode (mode.width, mode.height, (refresh * 1_000.0).round() as u32);
|
||||
}
|
||||
log::warn!("Output {} does not support mode {mode}", c.name());
|
||||
}
|
||||
Some(m) => c.set_mode(m.width(), m.height(), Some(m.refresh_rate())),
|
||||
break 'set_mode;
|
||||
};
|
||||
c.set_mode(w, h, Some(mhz));
|
||||
}
|
||||
}
|
||||
if let Some(vrr) = &self.vrr {
|
||||
|
|
|
|||
|
|
@ -229,3 +229,6 @@ event native_gamut (since = 23) {
|
|||
|
||||
event use_native_gamut (since = 23) {
|
||||
}
|
||||
|
||||
event arbitrary_modes (since = 29) {
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue