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 = [
"bitflags 2.5.0",
"futures",
"indexmap 2.2.6",
"num_enum",
"pinnacle-api-defs",
"pinnacle-api-macros",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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