mirror of
https://gitlab.freedesktop.org/pipewire/helvum.git
synced 2026-05-19 22:58:12 +02:00
Use typesafe GEnums
This commit is contained in:
parent
8a75db1987
commit
9d290793e7
6 changed files with 181 additions and 80 deletions
|
|
@ -239,7 +239,7 @@ mod imp {
|
|||
return;
|
||||
};
|
||||
|
||||
port.set_media_type(media_type.as_raw())
|
||||
port.set_media_type(media_type)
|
||||
}
|
||||
|
||||
/// Remove the port with the id `id` from the node with the id `node_id`
|
||||
|
|
|
|||
|
|
@ -114,8 +114,10 @@ pub enum PipewireMessage {
|
|||
Disconnected,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, glib::Enum)]
|
||||
#[enum_type(name = "HelvumNodeType")]
|
||||
pub enum NodeType {
|
||||
#[default]
|
||||
Input,
|
||||
Output,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ mod imp {
|
|||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use adw::gtk::gdk::{self};
|
||||
use adw::gtk::gdk;
|
||||
use libspa::param::format::MediaType;
|
||||
use libspa::utils::Direction;
|
||||
use crate::ui::graph::PortDirection;
|
||||
use log::warn;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
|
|
@ -617,13 +617,12 @@ mod imp {
|
|||
});
|
||||
let other_anchor = picked_port_anchor.unwrap_or(drag_cursor);
|
||||
|
||||
let (output_anchor, input_anchor) = match Direction::from_raw(port.direction()) {
|
||||
Direction::Output => (&port_anchor, &other_anchor),
|
||||
Direction::Input => (&other_anchor, &port_anchor),
|
||||
_ => unreachable!(),
|
||||
let (output_anchor, input_anchor) = match port.port_direction() {
|
||||
PortDirection::Output => (&port_anchor, &other_anchor),
|
||||
PortDirection::Input => (&other_anchor, &port_anchor),
|
||||
};
|
||||
|
||||
let mut media_type = MediaType::from_raw(port.media_type());
|
||||
let mut media_type = MediaType::from(port.media_type());
|
||||
if media_type == MediaType::Unknown {
|
||||
media_type = MediaType::Unknown;
|
||||
}
|
||||
|
|
@ -642,14 +641,14 @@ mod imp {
|
|||
};
|
||||
|
||||
for link in self.links.borrow().iter() {
|
||||
let mut media_type = link.media_type();
|
||||
let mut media_type = MediaType::from(link.media_type());
|
||||
|
||||
// If link media type is unknown, try to fall back to port media types.
|
||||
if media_type == MediaType::Unknown {
|
||||
if let Some(output_port) = link.output_port() {
|
||||
media_type = MediaType::from_raw(output_port.media_type());
|
||||
media_type = MediaType::from(output_port.media_type());
|
||||
} else if let Some(input_port) = link.input_port() {
|
||||
media_type = MediaType::from_raw(input_port.media_type());
|
||||
media_type = MediaType::from(input_port.media_type());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
use adw::{glib, prelude::*, subclass::prelude::*};
|
||||
use libspa::param::format::MediaType;
|
||||
|
||||
use super::Port;
|
||||
use super::{Port, PortMediaType};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
@ -30,7 +30,7 @@ mod imp {
|
|||
pub output_port: glib::WeakRef<Port>,
|
||||
pub input_port: glib::WeakRef<Port>,
|
||||
pub active: Cell<bool>,
|
||||
pub media_type: Cell<MediaType>,
|
||||
pub media_type: Cell<PortMediaType>,
|
||||
}
|
||||
|
||||
impl Default for Link {
|
||||
|
|
@ -39,7 +39,7 @@ mod imp {
|
|||
output_port: glib::WeakRef::default(),
|
||||
input_port: glib::WeakRef::default(),
|
||||
active: Cell::default(),
|
||||
media_type: Cell::new(MediaType::Unknown),
|
||||
media_type: Cell::new(PortMediaType::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -65,8 +65,7 @@ mod imp {
|
|||
.default_value(false)
|
||||
.flags(glib::ParamFlags::READWRITE)
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("media-type")
|
||||
.default_value(MediaType::Unknown.as_raw())
|
||||
glib::ParamSpecEnum::builder::<PortMediaType>("media-type")
|
||||
.flags(glib::ParamFlags::READWRITE)
|
||||
.build(),
|
||||
]
|
||||
|
|
@ -80,7 +79,7 @@ mod imp {
|
|||
"output-port" => self.output_port.upgrade().to_value(),
|
||||
"input-port" => self.input_port.upgrade().to_value(),
|
||||
"active" => self.active.get().to_value(),
|
||||
"media-type" => self.media_type.get().as_raw().to_value(),
|
||||
"media-type" => self.media_type.get().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -90,9 +89,7 @@ mod imp {
|
|||
"output-port" => self.output_port.set(value.get().unwrap()),
|
||||
"input-port" => self.input_port.set(value.get().unwrap()),
|
||||
"active" => self.active.set(value.get().unwrap()),
|
||||
"media-type" => self
|
||||
.media_type
|
||||
.set(MediaType::from_raw(value.get().unwrap())),
|
||||
"media-type" => self.media_type.set(value.get().expect("Value should be a PortMediaType")),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -132,12 +129,12 @@ impl Link {
|
|||
self.set_property("active", active);
|
||||
}
|
||||
|
||||
pub fn media_type(&self) -> MediaType {
|
||||
MediaType::from_raw(self.property("media-type"))
|
||||
pub fn media_type(&self) -> PortMediaType {
|
||||
self.property("media-type")
|
||||
}
|
||||
|
||||
pub fn set_media_type(&self, media_type: MediaType) {
|
||||
self.set_property("media-type", media_type.as_raw())
|
||||
self.set_property("media-type", PortMediaType::from(media_type))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use adw::{glib, gtk, prelude::*, subclass::prelude::*};
|
||||
use libspa::utils::Direction;
|
||||
use crate::ui::graph::PortDirection;
|
||||
|
||||
use crate::NodeId;
|
||||
use super::Port;
|
||||
|
|
@ -120,14 +120,13 @@ mod imp {
|
|||
|
||||
ports
|
||||
.iter()
|
||||
.for_each(|port| match Direction::from_raw(port.direction()) {
|
||||
Direction::Output => {
|
||||
.for_each(|port| match port.port_direction() {
|
||||
PortDirection::Output => {
|
||||
ports_out.push(port);
|
||||
}
|
||||
Direction::Input => {
|
||||
PortDirection::Input => {
|
||||
ports_in.push(port);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
|
||||
ports_out.sort_unstable_by_key(|port| port.name());
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ use adw::{
|
|||
prelude::*,
|
||||
subclass::prelude::*,
|
||||
};
|
||||
pub use imp::{PortDirection, PortMediaType};
|
||||
use crate::PortId;
|
||||
use libspa::utils::Direction;
|
||||
use libspa::{param::format::MediaType, utils::Direction};
|
||||
|
||||
use super::PortHandle;
|
||||
|
||||
|
|
@ -31,37 +32,78 @@ mod imp {
|
|||
|
||||
use std::cell::Cell;
|
||||
|
||||
use libspa::{param::format::MediaType, utils::Direction};
|
||||
use libspa::param::format::MediaType;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, glib::Enum)]
|
||||
#[enum_type(name = "HelvumPortDirection")]
|
||||
pub enum PortDirection {
|
||||
Input,
|
||||
#[default]
|
||||
Output,
|
||||
}
|
||||
|
||||
impl From<Direction> for PortDirection {
|
||||
fn from(direction: Direction) -> Self {
|
||||
match direction {
|
||||
Direction::Input => PortDirection::Input,
|
||||
Direction::Output => PortDirection::Output,
|
||||
_ => PortDirection::Input,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PortDirection> for Direction {
|
||||
fn from(direction: PortDirection) -> Self {
|
||||
match direction {
|
||||
PortDirection::Input => Direction::Input,
|
||||
PortDirection::Output => Direction::Output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, glib::Enum)]
|
||||
#[enum_type(name = "HelvumPortMediaType")]
|
||||
pub enum PortMediaType {
|
||||
#[default]
|
||||
Unknown,
|
||||
Audio,
|
||||
Video,
|
||||
Application,
|
||||
Stream,
|
||||
}
|
||||
|
||||
impl From<MediaType> for PortMediaType {
|
||||
fn from(m: MediaType) -> Self {
|
||||
match m {
|
||||
MediaType::Audio => PortMediaType::Audio,
|
||||
MediaType::Video => PortMediaType::Video,
|
||||
MediaType::Application => PortMediaType::Application,
|
||||
MediaType::Stream => PortMediaType::Stream,
|
||||
_ => PortMediaType::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PortMediaType> for MediaType {
|
||||
fn from(m: PortMediaType) -> Self {
|
||||
match m {
|
||||
PortMediaType::Audio => MediaType::Audio,
|
||||
PortMediaType::Video => MediaType::Video,
|
||||
PortMediaType::Application => MediaType::Application,
|
||||
PortMediaType::Stream => MediaType::Stream,
|
||||
_ => MediaType::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Graphical representation of a pipewire port.
|
||||
#[derive(gtk::CompositeTemplate, glib::Properties)]
|
||||
#[properties(wrapper_type = super::Port)]
|
||||
#[derive(gtk::CompositeTemplate)]
|
||||
#[template(file = "port.ui")]
|
||||
pub struct Port {
|
||||
#[property(get, set, construct_only)]
|
||||
pub(super) pipewire_id: Cell<u32>,
|
||||
#[property(
|
||||
type = u32,
|
||||
get = |_| self.media_type.get().as_raw(),
|
||||
set = Self::set_media_type
|
||||
)]
|
||||
pub(super) media_type: Cell<MediaType>,
|
||||
#[property(
|
||||
type = u32,
|
||||
get = |_| self.direction.get().as_raw(),
|
||||
set = Self::set_direction,
|
||||
construct_only
|
||||
)]
|
||||
pub(super) direction: Cell<Direction>,
|
||||
#[property(
|
||||
name = "name", type = String,
|
||||
get = |this: &Self| this.label.text().to_string(),
|
||||
set = |this: &Self, val| {
|
||||
this.label.set_text(val);
|
||||
this.label.set_tooltip_text(Some(val));
|
||||
}
|
||||
)]
|
||||
pub(super) media_type: Cell<PortMediaType>,
|
||||
pub(super) direction: Cell<PortDirection>,
|
||||
#[template_child]
|
||||
pub(super) label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
|
|
@ -72,8 +114,8 @@ mod imp {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
pipewire_id: Cell::new(0),
|
||||
media_type: Cell::new(MediaType::Unknown),
|
||||
direction: Cell::new(Direction::Output),
|
||||
media_type: Cell::new(PortMediaType::Unknown),
|
||||
direction: Cell::new(PortDirection::Output),
|
||||
label: TemplateChild::default(),
|
||||
handle: TemplateChild::default(),
|
||||
}
|
||||
|
|
@ -97,13 +139,15 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for Port {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
// Force left-to-right direction for the ports grid to avoid messed up UI when defaulting to right-to-left
|
||||
self.obj().set_direction(gtk::TextDirection::Ltr);
|
||||
gtk::prelude::WidgetExt::set_direction(&*self.obj(), gtk::TextDirection::Ltr);
|
||||
|
||||
// Initial UI update
|
||||
self.update_ui_for_direction();
|
||||
|
||||
// Display a grab cursor when the mouse is over the port so the user knows it can be dragged to another port.
|
||||
self.obj()
|
||||
|
|
@ -122,6 +166,57 @@ mod imp {
|
|||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: LazyLock<Vec<glib::ParamSpec>> = LazyLock::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecEnum::builder::<PortDirection>("port-direction")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("pipewire-id")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecEnum::builder::<PortMediaType>("media-type")
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("name")
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"port-direction" => {
|
||||
let val = value.get().expect("Value should be a PortDirection");
|
||||
self.direction.set(val);
|
||||
self.update_ui_for_direction();
|
||||
}
|
||||
"pipewire-id" => {
|
||||
self.pipewire_id.set(value.get().expect("Value should be a u32"));
|
||||
}
|
||||
"media-type" => {
|
||||
let val = value.get().expect("Value should be a PortMediaType");
|
||||
self.set_media_type(val);
|
||||
}
|
||||
"name" => {
|
||||
let val: String = value.get().expect("Value should be a String");
|
||||
self.label.set_text(&val);
|
||||
self.label.set_tooltip_text(Some(&val));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"port-direction" => self.direction.get().to_value(),
|
||||
"pipewire-id" => self.pipewire_id.get().to_value(),
|
||||
"media-type" => self.media_type.get().to_value(),
|
||||
"name" => self.label.text().to_string().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for Port {
|
||||
|
|
@ -164,8 +259,8 @@ mod imp {
|
|||
let (_, nat_handle_width, _, _) =
|
||||
self.handle.measure(gtk::Orientation::Horizontal, width);
|
||||
|
||||
match Direction::from_raw(self.obj().direction()) {
|
||||
Direction::Input => {
|
||||
match self.obj().port_direction() {
|
||||
PortDirection::Input => {
|
||||
let alloc = gtk::Allocation::new(
|
||||
-nat_handle_width / 2,
|
||||
(height - nat_handle_height) / 2,
|
||||
|
|
@ -182,7 +277,7 @@ mod imp {
|
|||
);
|
||||
self.label.size_allocate(&alloc, -1);
|
||||
}
|
||||
Direction::Output => {
|
||||
PortDirection::Output => {
|
||||
let alloc = gtk::Allocation::new(
|
||||
width - (nat_handle_width / 2),
|
||||
(height - nat_handle_height) / 2,
|
||||
|
|
@ -194,7 +289,6 @@ mod imp {
|
|||
let alloc = gtk::Allocation::new(0, 0, width - (nat_handle_width / 2), height);
|
||||
self.label.size_allocate(&alloc, -1);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -277,10 +371,9 @@ mod imp {
|
|||
return false;
|
||||
}
|
||||
|
||||
let (output_port, input_port) = match Direction::from_raw(port.direction()) {
|
||||
Direction::Output => (&port, &other_port),
|
||||
Direction::Input => (&other_port, &port),
|
||||
_ => unreachable!(),
|
||||
let (output_port, input_port) = match port.port_direction() {
|
||||
PortDirection::Output => (&port, &other_port),
|
||||
PortDirection::Input => (&other_port, &port),
|
||||
};
|
||||
|
||||
port.emit_by_name::<()>(
|
||||
|
|
@ -293,9 +386,7 @@ mod imp {
|
|||
obj.add_controller(drop_target);
|
||||
}
|
||||
|
||||
fn set_media_type(&self, media_type: u32) {
|
||||
let media_type = MediaType::from_raw(media_type);
|
||||
|
||||
fn set_media_type(&self, media_type: PortMediaType) {
|
||||
self.media_type.set(media_type);
|
||||
|
||||
for css_class in ["video", "audio", "midi"] {
|
||||
|
|
@ -303,7 +394,7 @@ mod imp {
|
|||
}
|
||||
|
||||
// Color the port according to its media type.
|
||||
match media_type {
|
||||
match MediaType::from(media_type) {
|
||||
MediaType::Video => self.handle.add_css_class("video"),
|
||||
MediaType::Audio => self.handle.add_css_class("audio"),
|
||||
MediaType::Application | MediaType::Stream => self.handle.add_css_class("midi"),
|
||||
|
|
@ -311,21 +402,18 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_direction(&self, direction: u32) {
|
||||
let direction = Direction::from_raw(direction);
|
||||
|
||||
self.direction.set(direction);
|
||||
fn update_ui_for_direction(&self) {
|
||||
let direction = self.direction.get();
|
||||
|
||||
match direction {
|
||||
Direction::Input => {
|
||||
PortDirection::Input => {
|
||||
self.obj().set_halign(gtk::Align::Start);
|
||||
self.label.set_halign(gtk::Align::Start);
|
||||
}
|
||||
Direction::Output => {
|
||||
PortDirection::Output => {
|
||||
self.obj().set_halign(gtk::Align::End);
|
||||
self.label.set_halign(gtk::Align::End);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -340,11 +428,27 @@ impl Port {
|
|||
pub fn new(id: PortId, name: &str, direction: Direction) -> Self {
|
||||
glib::Object::builder()
|
||||
.property("pipewire-id", id.0)
|
||||
.property("direction", direction.as_raw())
|
||||
.property("port-direction", PortDirection::from(direction))
|
||||
.property("name", name)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn port_direction(&self) -> PortDirection {
|
||||
self.property("port-direction")
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
self.property("name")
|
||||
}
|
||||
|
||||
pub fn set_media_type(&self, media_type: MediaType) {
|
||||
self.set_property("media-type", PortMediaType::from(media_type));
|
||||
}
|
||||
|
||||
pub fn media_type(&self) -> PortMediaType {
|
||||
self.property("media-type")
|
||||
}
|
||||
|
||||
pub fn pw_id(&self) -> PortId {
|
||||
PortId(self.property("pipewire-id"))
|
||||
}
|
||||
|
|
@ -360,6 +464,6 @@ impl Port {
|
|||
}
|
||||
|
||||
pub fn is_linkable_to(&self, other_port: &Self) -> bool {
|
||||
self.direction() != other_port.direction()
|
||||
self.port_direction() != other_port.port_direction()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue