1
0
Fork 0
forked from wry/wry

control-center: add outputs pane

This commit is contained in:
Julian Orth 2026-03-07 14:04:04 +01:00
parent dee142b3bb
commit d328655f8b
13 changed files with 1775 additions and 36 deletions

View file

@ -744,8 +744,7 @@ fn create_dummy_output(state: &Rc<State>) {
wlr_output_heads: Default::default(),
});
let schedule = Rc::new(OutputSchedule::new(
&state.ring,
&state.eng,
state,
&connector_data,
&persistent_state,
));

View file

@ -1484,7 +1484,7 @@ impl ConfigProxyHandler {
match connector {
Some(c) => {
let connector = self.get_output_node(c)?;
connector.schedule.set_cursor_hz(hz);
connector.schedule.set_cursor_hz(&self.state, hz);
}
_ => {
let Some((hz, _)) = map_cursor_hz(hz) else {

View file

@ -2,7 +2,7 @@ use {
crate::{
control_center::{
cc_color_management::ColorManagementPane, cc_compositor::CompositorPane,
cc_idle::IdlePane, cc_xwayland::XwaylandPane,
cc_idle::IdlePane, cc_outputs::OutputsPane, cc_xwayland::XwaylandPane,
},
egui_adapter::egui_platform::{
EggError, EggWindow, EggWindowOwner,
@ -36,6 +36,7 @@ use {
mod cc_color_management;
mod cc_compositor;
mod cc_idle;
mod cc_outputs;
mod cc_sidebar;
mod cc_xwayland;
@ -74,6 +75,7 @@ bitflags! {
CCI_IDLE,
CCI_COLOR_MANAGEMENT,
CCI_XWAYLAND,
CCI_OUTPUTS,
}
pub struct ControlCenter {
@ -118,6 +120,7 @@ enum PaneType {
Idle(IdlePane),
ColorManagement(ColorManagementPane),
Xwayland(XwaylandPane),
Outputs(Box<OutputsPane>),
}
struct CcBehavior<'a> {
@ -140,6 +143,7 @@ impl Pane {
PaneType::Idle(v) => v.title(res),
PaneType::ColorManagement(v) => v.title(res),
PaneType::Xwayland(v) => v.title(res),
PaneType::Outputs(v) => v.title(res),
}
}
@ -149,6 +153,7 @@ impl Pane {
PaneType::Idle(p) => p.show(ui),
PaneType::ColorManagement(p) => p.show(ui),
PaneType::Xwayland(p) => p.show(behavior, ui),
PaneType::Outputs(p) => p.show(&mut self.ps, ui),
}
}
}
@ -160,6 +165,7 @@ impl PaneType {
PaneType::Idle(_) => CCI_IDLE,
PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT,
PaneType::Xwayland(_) => CCI_XWAYLAND,
PaneType::Outputs(_) => CCI_OUTPUTS,
}
}
}
@ -415,7 +421,6 @@ fn icon_label(icon: &str) -> Label {
Label::new(icon).selectable(false)
}
#[expect(dead_code)]
fn grid_label(ui: &mut Ui, label: &str) {
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
ui.label(label);

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@ enum PaneName {
Idle,
ColorManagement,
Xwayland,
Outputs,
}
impl PaneName {
@ -21,6 +22,7 @@ impl PaneName {
PaneName::Idle => "Idle",
PaneName::ColorManagement => "Color Management",
PaneName::Xwayland => "Xwayland",
PaneName::Outputs => "Outputs",
}
}
}
@ -55,6 +57,9 @@ impl ControlCenterInner {
PaneName::Xwayland => {
PaneType::Xwayland(self.create_xwayland_pane())
}
PaneName::Outputs => {
PaneType::Outputs(Box::new(self.create_outputs_pane()))
}
};
self.open(tree, ty);
ui.ctx().request_repaint();

View file

@ -6,7 +6,6 @@ use {
egui::{Color32, Rgba},
};
#[expect(dead_code)]
pub trait Color32Ext {
fn to_oklab(self) -> Oklab;
fn to_oklch(self) -> Oklch;

View file

@ -110,7 +110,6 @@ pub enum EggError {
}
pub mod icons {
#[expect(dead_code)]
pub const ICON_ADD: &str = "\u{e145}";
pub const ICON_CLOSE: &str = "\u{e5cd}";
pub const ICON_DRAG_INDICATOR: &str = "\u{e945}";
@ -119,7 +118,6 @@ pub mod icons {
pub const ICON_OPEN_IN_NEW: &str = "\u{e89e}";
#[expect(dead_code)]
pub const ICON_PENDING: &str = "\u{ef64}";
#[expect(dead_code)]
pub const ICON_REMOVE: &str = "\u{e15b}";
}

View file

@ -105,14 +105,13 @@ pub struct ReadOnlyHeadState {
}
impl ReadOnlyHeadState {
#[expect(dead_code)]
pub fn borrow(&self) -> Ref<'_, HeadState> {
self.state.borrow()
}
}
impl HeadState {
fn update_in_compositor_space(&mut self, wl_output: Option<GlobalName>) {
pub fn update_in_compositor_space(&mut self, wl_output: Option<GlobalName>) {
self.in_compositor_space = false;
self.wl_output = None;
if !self.connector_enabled {
@ -131,7 +130,7 @@ impl HeadState {
self.wl_output = wl_output;
}
fn update_size(&mut self) {
pub fn update_size(&mut self) {
self.size =
OutputNode::calculate_extents_(self.mode, self.transform, self.scale, self.position)
.size();
@ -213,7 +212,7 @@ pub enum HeadCommonError {
}
pub struct HeadManagers {
name: HeadName,
pub name: HeadName,
state: Rc<RefCell<HeadState>>,
managers: CopyHashMap<(ClientId, JayHeadManagerSessionV1Id), Rc<Head>>,
}
@ -235,7 +234,6 @@ impl HeadManagers {
}
}
#[expect(dead_code)]
pub fn state(&self) -> ReadOnlyHeadState {
ReadOnlyHeadState {
state: self.state.clone(),

View file

@ -456,7 +456,7 @@ impl JayRandrRequestHandler for JayRandr {
let Some(c) = self.get_output_node(req.output) else {
return Ok(());
};
c.schedule.set_cursor_hz(req.hz);
c.schedule.set_cursor_hz(&self.state, req.hz);
Ok(())
}

View file

@ -2,9 +2,10 @@ use {
crate::{
async_engine::AsyncEngine,
backend::HardwareCursor,
control_center::CCI_OUTPUTS,
ifs::wl_output::PersistentOutputState,
io_uring::{IoUring, IoUringError},
state::ConnectorData,
state::{ConnectorData, State},
utils::{
asyncevent::AsyncEvent, cell_ext::CellExt, clonecell::CloneCell, errorfmt::ErrorFmt,
numcell::NumCell,
@ -51,8 +52,7 @@ pub struct OutputSchedule {
impl OutputSchedule {
pub fn new(
ring: &Rc<IoUring>,
eng: &Rc<AsyncEngine>,
state: &State,
connector: &Rc<ConnectorData>,
persistent: &Rc<PersistentOutputState>,
) -> Self {
@ -60,8 +60,8 @@ impl OutputSchedule {
changed: Default::default(),
run: Default::default(),
connector: connector.clone(),
ring: ring.clone(),
eng: eng.clone(),
ring: state.ring.clone(),
eng: state.eng.clone(),
vrr_enabled: Default::default(),
hardware_cursor_change: Cell::new(Change::None),
software_cursor_change: Cell::new(Change::None),
@ -72,7 +72,7 @@ impl OutputSchedule {
iteration: Default::default(),
};
if let Some(hz) = persistent.vrr_cursor_hz.get() {
slf.set_cursor_hz(hz);
slf.set_cursor_hz(state, hz);
}
slf
}
@ -118,7 +118,7 @@ impl OutputSchedule {
self.trigger();
}
pub fn set_cursor_hz(&self, hz: f64) {
pub fn set_cursor_hz(&self, state: &State, hz: f64) {
let (hz, delta) = match map_cursor_hz(hz) {
None => {
log::warn!("Ignoring cursor frequency {hz}");
@ -128,6 +128,7 @@ impl OutputSchedule {
};
self.persistent.vrr_cursor_hz.set(hz);
self.connector.head_managers.handle_cursor_hz_change(hz);
state.trigger_cci(CCI_OUTPUTS);
self.cursor_delta_nsec.set(delta);
self.trigger();
}

View file

@ -17,7 +17,8 @@ use {
compositor::{LIBEI_SOCKET, LogLevel},
config::ConfigProxy,
control_center::{
CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_IDLE, CCI_XWAYLAND, ControlCenters,
CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_IDLE, CCI_OUTPUTS, CCI_XWAYLAND,
ControlCenters,
},
copy_device::CopyDeviceRegistry,
cpu_worker::CpuWorker,
@ -493,30 +494,39 @@ impl ConnectorData {
return;
}
*self.state.borrow_mut() = s.clone();
if old.enabled != s.enabled {
macro_rules! b {
($expr:expr) => {{
let e = $expr;
if e {
state.trigger_cci(CCI_OUTPUTS);
}
e
}};
}
if b!(old.enabled != s.enabled) {
self.head_managers.handle_enabled_change(s.enabled);
}
if old.active != s.active {
if b!(old.active != s.active) {
self.head_managers.handle_active_change(s.active);
}
if old.non_desktop_override != s.non_desktop_override {
if b!(old.non_desktop_override != s.non_desktop_override) {
self.head_managers
.handle_non_desktop_override_changed(s.non_desktop_override);
}
if old.vrr != s.vrr {
if b!(old.vrr != s.vrr) {
self.head_managers.handle_vrr_change(s.vrr);
}
if old.tearing != s.tearing {
if b!(old.tearing != s.tearing) {
self.head_managers.handle_tearing_enabled_change(s.tearing);
}
if old.format != s.format {
if b!(old.format != s.format) {
self.head_managers.handle_format_change(s.format);
}
if (old.color_space, old.eotf) != (s.color_space, s.eotf) {
if b!((old.color_space, old.eotf) != (s.color_space, s.eotf)) {
self.head_managers
.handle_colors_change(s.color_space, s.eotf);
}
if old.mode != s.mode {
if b!(old.mode != s.mode) {
self.head_managers.handle_mode_change(s.mode);
for head in self.wlr_output_heads.lock().values() {
head.handle_mode_change(s.mode);

View file

@ -4,6 +4,7 @@ use {
BackendConnectorState, BackendConnectorStateSerial, Connector, ConnectorEvent,
ConnectorId, MonitorInfo,
},
control_center::CCI_OUTPUTS,
format::XRGB8888,
globals::GlobalName,
ifs::{
@ -108,6 +109,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
for mgr in state.head_managers.lock().values() {
mgr.announce(&data);
}
state.trigger_cci(CCI_OUTPUTS);
if state.connectors.set(id, data).is_some() {
panic!("Connector id has been reused");
}
@ -147,6 +149,7 @@ impl ConnectorHandler {
self.data.handler.set(None);
self.state.connectors.remove(&self.id);
self.data.head_managers.handle_removed();
self.state.trigger_cci(CCI_OUTPUTS);
}
async fn handle_connected(&self, info: MonitorInfo) {
@ -162,6 +165,7 @@ impl ConnectorHandler {
}
self.data.connected.set(false);
self.data.head_managers.handle_output_disconnected();
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.data.wlr_output_heads.lock().drain_values() {
head.handle_disconnected();
}
@ -213,12 +217,7 @@ impl ConnectorHandler {
info.primaries,
info.luminance,
));
let schedule = Rc::new(OutputSchedule::new(
&self.state.ring,
&self.state.eng,
&self.data,
&desired_state,
));
let schedule = Rc::new(OutputSchedule::new(&self.state, &self.data, &desired_state));
let _schedule = self
.state
.eng
@ -341,6 +340,7 @@ impl ConnectorHandler {
self.data
.head_managers
.handle_output_connected(&output_data);
self.state.trigger_cci(CCI_OUTPUTS);
self.state.wlr_output_managers.announce_head(&output_data);
'outer: loop {
while let Some(event) = self.data.connector.event() {
@ -353,6 +353,7 @@ impl ConnectorHandler {
}
ConnectorEvent::FormatsChanged(formats) => {
self.data.head_managers.handle_formats_change(&formats);
self.state.trigger_cci(CCI_OUTPUTS);
on.global.formats.set(formats);
}
ConnectorEvent::State(state) => {
@ -466,6 +467,7 @@ impl ConnectorHandler {
self.data
.head_managers
.handle_output_connected(&output_data);
self.state.trigger_cci(CCI_OUTPUTS);
self.state.wlr_output_managers.announce_head(&output_data);
'outer: loop {
while let Some(event) = self.data.connector.event() {

View file

@ -6,6 +6,7 @@ use {
},
client::ClientId,
cmm::cmm_description::ColorDescription,
control_center::CCI_OUTPUTS,
cursor::KnownCursor,
fixed::Fixed,
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
@ -243,6 +244,7 @@ impl OutputNode {
.connector
.head_managers
.handle_tearing_active_change(tearing);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -501,6 +503,7 @@ impl OutputNode {
.connector
.head_managers
.handle_scale_change(scale);
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.global.connector.wlr_output_heads.lock().values() {
head.handle_new_scale(scale);
}
@ -873,6 +876,7 @@ impl OutputNode {
.connector
.head_managers
.handle_transform_change(transform);
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.global.connector.wlr_output_heads.lock().values() {
head.hande_transform_change(transform);
}
@ -935,6 +939,7 @@ impl OutputNode {
.connector
.head_managers
.handle_position_size_change(self);
self.state.trigger_cci(CCI_OUTPUTS);
}
pub fn update_state(self: &Rc<Self>, old: BackendConnectorState, state: BackendConnectorState) {
@ -989,6 +994,7 @@ impl OutputNode {
.connector
.head_managers
.handle_brightness_change(brightness);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -1004,6 +1010,7 @@ impl OutputNode {
.connector
.head_managers
.handle_use_native_gamut_change(use_native_gamut);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -1015,6 +1022,7 @@ impl OutputNode {
.connector
.head_managers
.handle_blend_space_change(blend_space);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
fn find_stacked_at(
@ -1480,6 +1488,7 @@ impl OutputNode {
.connector
.head_managers
.handle_vrr_mode_change(mode);
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.global.connector.wlr_output_heads.lock().values() {
head.handle_vrr_mode_change(mode);
}
@ -1494,6 +1503,7 @@ impl OutputNode {
.connector
.head_managers
.handle_tearing_mode_change(mode);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -1543,6 +1553,7 @@ impl OutputNode {
pub fn set_flip_margin(&self, margin_ns: u64) {
self.flip_margin_ns.set(Some(margin_ns));
self.state.trigger_cci(CCI_OUTPUTS);
}
}