1
0
Fork 0
forked from wry/wry

backend: support outputs with arbitrary modes

This commit is contained in:
Julian Orth 2026-03-17 19:29:11 +01:00
parent dd10e1a585
commit 1a9753847a
23 changed files with 199 additions and 59 deletions

View file

@ -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 {

View file

@ -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);

View file

@ -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;

View file

@ -78,7 +78,7 @@ global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError);
impl Global for JayCompositorGlobal {
fn version(&self) -> u32 {
28
29
}
fn required_caps(&self) -> ClientCaps {

View file

@ -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(&current_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) {

View file

@ -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>,

View file

@ -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();
}

View file

@ -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!(