mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +01:00
Improve Rust output setup API
This commit is contained in:
parent
6c8b643577
commit
5634cbbfe7
7 changed files with 96 additions and 113 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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::{
|
||||||
|
|
|
@ -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::{
|
||||||
|
|
|
@ -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)],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in a new issue