Improve Rust output setup API

This commit is contained in:
Ottatop 2024-04-17 18:56:01 -05:00
parent 6c8b643577
commit 5634cbbfe7
7 changed files with 96 additions and 113 deletions

1
Cargo.lock generated
View file

@ -1799,7 +1799,6 @@ version = "0.0.2"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"futures", "futures",
"indexmap 2.2.6",
"num_enum", "num_enum",
"pinnacle-api-defs", "pinnacle-api-defs",
"pinnacle-api-macros", "pinnacle-api-macros",

View file

@ -21,4 +21,3 @@ num_enum = "0.7.2"
xkbcommon = { workspace = true } xkbcommon = { workspace = true }
rand = "0.8.5" rand = "0.8.5"
bitflags = { workspace = true } bitflags = { workspace = true }
indexmap = "2.2.6"

View file

@ -12,7 +12,6 @@
use std::sync::OnceLock; use std::sync::OnceLock;
use futures::FutureExt; use futures::FutureExt;
use indexmap::IndexMap;
use pinnacle_api_defs::pinnacle::output::{ use pinnacle_api_defs::pinnacle::output::{
self, self,
v0alpha1::{ v0alpha1::{
@ -181,6 +180,8 @@ impl Output {
/// connected and that will be connected in the future. It handles the setting of modes, /// connected and that will be connected in the future. It handles the setting of modes,
/// scales, tags, and more. /// scales, tags, and more.
/// ///
/// Setups will be applied top to bottom.
///
/// See [`OutputSetup`] for more information. /// See [`OutputSetup`] for more information.
/// ///
/// # Examples /// # Examples
@ -233,27 +234,28 @@ impl Output {
/// ``` /// ```
/// use pinnacle_api::output::UpdateLocsOn; /// use pinnacle_api::output::UpdateLocsOn;
/// use pinnacle_api::output::OutputLoc; /// use pinnacle_api::output::OutputLoc;
/// use pinnacle_api::output::OutputId;
/// ///
/// output.setup_locs( /// output.setup_locs(
/// // Relayout all outputs when outputs are connected, disconnected, and resized /// // Relayout all outputs when outputs are connected, disconnected, and resized
/// UpdateLocsOn::all(), /// UpdateLocsOn::all(),
/// [ /// [
/// // Anchor eDP-1 to (0, 0) so other outputs can be placed relative to it /// // Anchor eDP-1 to (0, 0) so other outputs can be placed relative to it
/// ("eDP-1", OutputLoc::Point(0, 0)), /// (OutputId::name("eDP-1"), OutputLoc::Point(0, 0)),
/// // Place HDMI-A-1 below it centered /// // Place HDMI-A-1 below it centered
/// ( /// (
/// "HDMI-A-1", /// OutputId::name("HDMI-A-1"),
/// OutputLoc::relative_to("eDP-1", Alignment::BottomAlignCenter), /// OutputLoc::RelativeTo(OutputId::name("eDP-1"), Alignment::BottomAlignCenter),
/// ), /// ),
/// // Place HDMI-A-2 below HDMI-A-1. /// // Place HDMI-A-2 below HDMI-A-1.
/// (
/// OutputId::name("HDMI-A-2"),
/// OutputLoc::RelativeTo(OutputId::name("HDMI-A-1"), Alignment::BottomAlignCenter),
/// ),
/// // Additionally, if HDMI-A-1 isn't connected, place it below eDP-1 instead. /// // Additionally, if HDMI-A-1 isn't connected, place it below eDP-1 instead.
/// ( /// (
/// "HDMI-A-2", /// OutputId::name("HDMI-A-2"),
/// OutputLoc::relative_to_with_fallbacks( /// OutputLoc::RelativeTo(OutputId::name("eDP-1"), Alignment::BottomAlignCenter),
/// "HDMI-A-1",
/// Alignment::BottomAlignCenter,
/// [("eDP-1", Alignment::BottomAlignCenter)],
/// ),
/// ), /// ),
/// ] /// ]
/// ); /// );
@ -261,12 +263,9 @@ impl Output {
pub fn setup_locs( pub fn setup_locs(
&self, &self,
update_locs_on: UpdateLocsOn, update_locs_on: UpdateLocsOn,
setup: impl IntoIterator<Item = (impl ToString, OutputLoc)>, setup: impl IntoIterator<Item = (OutputId, OutputLoc)>,
) { ) {
let setup: IndexMap<_, _> = setup let setup: Vec<_> = setup.into_iter().collect();
.into_iter()
.map(|(name, align)| (name.to_string(), align))
.collect();
let api = self.api.get().unwrap().clone(); let api = self.api.get().unwrap().clone();
let layout_outputs = move || { let layout_outputs = move || {
@ -278,7 +277,9 @@ impl Output {
// Place outputs with OutputSetupLoc::Point // Place outputs with OutputSetupLoc::Point
for output in outputs.iter() { for output in outputs.iter() {
if let Some(&OutputLoc::Point(x, y)) = setup.get(output.name()) { if let Some(&(_, OutputLoc::Point(x, y))) =
setup.iter().find(|(op_id, _)| op_id.matches(output))
{
output.set_location(x, y); output.set_location(x, y);
placed_outputs.push(output.clone()); placed_outputs.push(output.clone());
@ -299,22 +300,19 @@ impl Output {
// //
// Because this code is hideous I'm gonna comment what it does // Because this code is hideous I'm gonna comment what it does
while let Some((output, relative_to, alignment)) = while let Some((output, relative_to, alignment)) =
setup.iter().find_map(|(setup_op_name, loc)| { setup.iter().find_map(|(setup_op_id, loc)| {
// For every location setup, // For every location setup,
// find the first unplaced output it refers to that has a relative location // find the first unplaced output it refers to that has a relative location
outputs outputs
.iter() .iter()
.find(|setup_op| { .find(|setup_op| {
!placed_outputs.contains(setup_op) && setup_op.name() == setup_op_name !placed_outputs.contains(setup_op) && setup_op_id.matches(setup_op)
}) })
.and_then(|setup_op| match loc { .and_then(|setup_op| match loc {
OutputLoc::RelativeTo(relative_tos) => { OutputLoc::RelativeTo(rel_id, alignment) => {
// Return the first placed output in the relative-to list placed_outputs.iter().find_map(|placed_op| {
relative_tos.iter().find_map(|(rel_name, align)| { (rel_id.matches(placed_op))
placed_outputs.iter().find_map(|pl_op| { .then_some((setup_op, placed_op, alignment))
(pl_op.name() == rel_name)
.then_some((setup_op, pl_op, align))
})
}) })
} }
_ => None, _ => None,
@ -389,8 +387,8 @@ impl Output {
/// A matcher for outputs. /// A matcher for outputs.
enum OutputMatcher { enum OutputMatcher {
/// Match outputs by name. /// Match outputs by unique id.
Name(String), Id(OutputId),
/// Match outputs using a function that returns a bool. /// Match outputs using a function that returns a bool.
Fn(Box<dyn Fn(&OutputHandle) -> bool + Send + Sync>), Fn(Box<dyn Fn(&OutputHandle) -> bool + Send + Sync>),
} }
@ -399,7 +397,7 @@ impl OutputMatcher {
/// Returns whether this matcher matches the given output. /// Returns whether this matcher matches the given output.
fn matches(&self, output: &OutputHandle) -> bool { fn matches(&self, output: &OutputHandle) -> bool {
match self { match self {
OutputMatcher::Name(name) => output.name() == name, OutputMatcher::Id(id) => id.matches(output),
OutputMatcher::Fn(matcher) => matcher(output), OutputMatcher::Fn(matcher) => matcher(output),
} }
} }
@ -408,7 +406,7 @@ impl OutputMatcher {
impl std::fmt::Debug for OutputMatcher { impl std::fmt::Debug for OutputMatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Name(name) => f.debug_tuple("Name").field(name).finish(), Self::Id(name) => f.debug_tuple("Name").field(name).finish(),
Self::Fn(_) => f Self::Fn(_) => f
.debug_tuple("Fn") .debug_tuple("Fn")
.field(&"<Box<dyn Fn(&OutputHandle)> -> bool>") .field(&"<Box<dyn Fn(&OutputHandle)> -> bool>")
@ -427,9 +425,9 @@ pub struct OutputSetup {
impl OutputSetup { impl OutputSetup {
/// Creates a new `OutputSetup` that applies to the output with the given name. /// Creates a new `OutputSetup` that applies to the output with the given name.
pub fn new(output_name: impl ToString) -> Self { pub fn new(id: OutputId) -> Self {
Self { Self {
output: OutputMatcher::Name(output_name.to_string()), output: OutputMatcher::Id(id),
mode: None, mode: None,
scale: None, scale: None,
tag_names: None, tag_names: None,
@ -492,62 +490,34 @@ impl OutputSetup {
/// A location for an output. /// A location for an output.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum OutputLoc { pub enum OutputLoc {
/// A specific point in the global space. /// A specific point in the global space of the form (x, y).
Point(i32, i32), Point(i32, i32),
/// A location relative to another output. /// A location relative to another output of the form (output_name, alignment).
/// RelativeTo(OutputId, Alignment),
/// This holds a `Vec` of output names to alignments.
/// The output that is relative to will be chosen from the first
/// connected and placed output in this `Vec`.
RelativeTo(Vec<(String, Alignment)>),
} }
impl OutputLoc { /// An identifier for an output.
/// Creates an `OutputLoc` that will place outputs relative to #[derive(Clone, Debug, PartialEq, Eq, Hash)]
/// the output with the given name using the given alignment. pub enum OutputId {
/// Identify using the output's name.
Name(String),
// TODO: serial
}
impl OutputId {
/// Creates an [`OutputId::Name`].
/// ///
/// # Examples /// This is a convenience function so you don't have to call `.into()`
/// /// or `.to_string()`.
/// ``` pub fn name(name: impl ToString) -> Self {
/// use pinnacle_api::output::OutputLoc; Self::Name(name.to_string())
/// use pinnacle_api::output::Alignment;
///
/// let output_loc = OutputLoc::relative_to("HDMI-1", Alignment::LeftAlignBottom);
/// ```
pub fn relative_to(name: impl ToString, alignment: Alignment) -> Self {
Self::RelativeTo(vec![(name.to_string(), alignment)])
} }
/// Like [`OutputLoc::relative_to`] but will additionally try to place outputs /// Returns whether `output` is identified by this `OutputId`.
/// relative to the specified fallbacks if the given output is not connected. pub fn matches(&self, output: &OutputHandle) -> bool {
/// match self {
/// # Examples OutputId::Name(name) => name == output.name(),
/// }
/// ```
/// use pinnacle_api::output::OutputLoc;
/// use pinnacle_api::output::Alignment;
///
/// let output_loc = OutputLoc::relative_to_with_fallbacks(
/// "HDMI-1",
/// Alignment::LeftAlignBottom,
/// [
/// ("HDMI-2", Alignment::LeftAlignBottom),
/// ("HDMI-3", Alignment::LeftAlignBottom),
/// ],
/// );
/// ```
pub fn relative_to_with_fallbacks(
name: impl ToString,
alignment: Alignment,
fallbacks: impl IntoIterator<Item = (impl ToString, Alignment)>,
) -> Self {
let mut relatives = vec![(name.to_string(), alignment)];
relatives.extend(
fallbacks
.into_iter()
.map(|(name, align)| (name.to_string(), align)),
);
Self::RelativeTo(relatives)
} }
} }
@ -1183,5 +1153,6 @@ pub struct OutputProperties {
pub tags: Vec<TagHandle>, pub tags: Vec<TagHandle>,
/// This output's scaling factor. /// This output's scaling factor.
pub scale: Option<f32>, pub scale: Option<f32>,
/// This output's transform.
pub transform: Option<Transform>, pub transform: Option<Transform>,
} }

View file

@ -34,7 +34,6 @@ use pinnacle_api_defs::pinnacle::{
}; };
use smithay::{ use smithay::{
backend::renderer::TextureFilter, backend::renderer::TextureFilter,
desktop::layer_map_for_output,
input::keyboard::XkbConfig, input::keyboard::XkbConfig,
output::Scale, output::Scale,
reexports::{calloop, input as libinput}, reexports::{calloop, input as libinput},

View file

@ -51,10 +51,7 @@ use smithay::{
vulkan::{self, version::Version, PhysicalDevice}, vulkan::{self, version::Version, PhysicalDevice},
SwapBuffersError, SwapBuffersError,
}, },
desktop::{ desktop::utils::{send_frames_surface_tree, OutputPresentationFeedback},
layer_map_for_output,
utils::{send_frames_surface_tree, OutputPresentationFeedback},
},
input::pointer::CursorImageStatus, input::pointer::CursorImageStatus,
output::{Output, PhysicalProperties, Subpixel}, output::{Output, PhysicalProperties, Subpixel},
reexports::{ reexports::{

View file

@ -15,7 +15,6 @@ use smithay::{
}, },
winit::{self, WinitEvent, WinitGraphicsBackend}, winit::{self, WinitEvent, WinitGraphicsBackend},
}, },
desktop::layer_map_for_output,
input::pointer::CursorImageStatus, input::pointer::CursorImageStatus,
output::{Output, Scale, Subpixel}, output::{Output, Scale, Subpixel},
reexports::{ reexports::{

View file

@ -19,7 +19,8 @@ fn run_rust(run: impl FnOnce(ApiModules) + Send + 'static) {
std::thread::spawn(|| { std::thread::spawn(|| {
run_rust_inner(run); run_rust_inner(run);
}) })
.join(); .join()
.unwrap();
} }
#[tokio::main] #[tokio::main]
@ -39,7 +40,7 @@ fn setup_rust(run: impl FnOnce(ApiModules) + Send + 'static) -> JoinHandle<()> {
mod output { mod output {
use pinnacle::state::WithState; use pinnacle::state::WithState;
use pinnacle_api::output::{Alignment, OutputLoc, OutputSetup, UpdateLocsOn}; use pinnacle_api::output::{Alignment, OutputId, OutputLoc, OutputSetup, UpdateLocsOn};
use smithay::{output::Output, utils::Rectangle}; use smithay::{output::Output, utils::Rectangle};
use super::*; use super::*;
@ -53,13 +54,13 @@ mod output {
OutputSetup::new_with_matcher(|_| true).with_tags(["1", "2", "3"]), OutputSetup::new_with_matcher(|_| true).with_tags(["1", "2", "3"]),
OutputSetup::new_with_matcher(|op| op.name().contains("Test")) OutputSetup::new_with_matcher(|op| op.name().contains("Test"))
.with_tags(["Test 4", "Test 5"]), .with_tags(["Test 4", "Test 5"]),
OutputSetup::new("Second").with_scale(2.0).with_mode( OutputSetup::new(OutputId::name("Second"))
pinnacle_api::output::Mode { .with_scale(2.0)
.with_mode(pinnacle_api::output::Mode {
pixel_width: 6900, pixel_width: 6900,
pixel_height: 420, pixel_height: 420,
refresh_rate_millihertz: 69420, refresh_rate_millihertz: 69420,
}, }),
),
]); ]);
}); });
@ -119,14 +120,20 @@ mod output {
api.output.setup_locs( api.output.setup_locs(
UpdateLocsOn::all(), UpdateLocsOn::all(),
[ [
("Pinnacle Window", OutputLoc::Point(0, 0)), (OutputId::name("Pinnacle Window"), OutputLoc::Point(0, 0)),
( (
"First", OutputId::name("First"),
OutputLoc::relative_to("Second", Alignment::LeftAlignTop), OutputLoc::RelativeTo(
OutputId::name("Second"),
Alignment::LeftAlignTop,
),
), ),
( (
"Second", OutputId::name("Second"),
OutputLoc::relative_to("First", Alignment::RightAlignTop), OutputLoc::RelativeTo(
OutputId::name("First"),
Alignment::RightAlignTop,
),
), ),
], ],
); );
@ -195,21 +202,33 @@ mod output {
api.output.setup_locs( api.output.setup_locs(
UpdateLocsOn::all(), UpdateLocsOn::all(),
[ [
("Pinnacle Window", OutputLoc::Point(0, 0)), (OutputId::name("Pinnacle Window"), OutputLoc::Point(0, 0)),
( (
"First", OutputId::name("First"),
OutputLoc::relative_to("Pinnacle Window", Alignment::BottomAlignLeft), OutputLoc::RelativeTo(
), OutputId::name("Pinnacle Window"),
( Alignment::BottomAlignLeft,
"Second", ),
OutputLoc::relative_to("First", Alignment::BottomAlignLeft), ),
), (
( OutputId::name("Second"),
"Third", OutputLoc::RelativeTo(
OutputLoc::relative_to_with_fallbacks( OutputId::name("First"),
"Second", Alignment::BottomAlignLeft,
),
),
(
OutputId::name("Third"),
OutputLoc::RelativeTo(
OutputId::name("Second"),
Alignment::BottomAlignLeft,
),
),
(
OutputId::name("Third"),
OutputLoc::RelativeTo(
OutputId::name("First"),
Alignment::BottomAlignLeft, Alignment::BottomAlignLeft,
[("First", Alignment::BottomAlignLeft)],
), ),
), ),
], ],