use std::fmt::{self, Display};
use bitflags::bitflags;
use derive_more::Debug;
use serde::Serialize;
use super::{inputs::InputId, sources::SourceId};
use crate::common::FlagsDebug;
#[derive(Serialize)]
#[serde(tag = "requestType", content = "requestData")]
pub(crate) enum Request<'a> {
#[serde(rename = "GetStudioModeEnabled")]
GetStudioModeEnabled,
#[serde(rename = "SetStudioModeEnabled")]
SetStudioModeEnabled {
#[serde(rename = "studioModeEnabled")]
enabled: bool,
},
#[serde(rename = "OpenInputPropertiesDialog")]
OpenInputPropertiesDialog {
#[serde(flatten)]
input: InputId<'a>,
},
#[serde(rename = "OpenInputFiltersDialog")]
OpenInputFiltersDialog {
#[serde(flatten)]
input: InputId<'a>,
},
#[serde(rename = "OpenInputInteractDialog")]
OpenInputInteractDialog {
#[serde(flatten)]
input: InputId<'a>,
},
#[serde(rename = "GetMonitorList")]
GetMonitorList,
#[serde(rename = "OpenVideoMixProjector")]
OpenVideoMixProjector(OpenVideoMixProjectorInternal),
#[serde(rename = "OpenSourceProjector")]
OpenSourceProjector(OpenSourceProjectorInternal<'a>),
}
impl<'a> From<Request<'a>> for super::RequestType<'a> {
fn from(value: Request<'a>) -> Self {
super::RequestType::Ui(value)
}
}
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct OpenVideoMixProjector {
pub r#type: VideoMixType,
pub location: Option<Location>,
}
#[derive(Serialize)]
pub(crate) struct OpenVideoMixProjectorInternal {
#[serde(rename = "videoMixType")]
pub r#type: VideoMixType,
#[serde(flatten)]
pub location: Option<LocationInternal>,
}
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct OpenSourceProjector<'a> {
pub source: SourceId<'a>,
pub location: Option<Location>,
}
#[derive(Serialize)]
pub(crate) struct OpenSourceProjectorInternal<'a> {
#[serde(flatten)]
pub source: SourceId<'a>,
#[serde(flatten)]
pub location: Option<LocationInternal>,
}
#[non_exhaustive]
pub enum Location {
MonitorIndex(i32),
ProjectorGeometry(QtGeometry),
}
#[derive(Serialize)]
pub(crate) enum LocationInternal {
#[serde(rename = "monitorIndex")]
MonitorIndex(i32),
#[serde(rename = "projectorGeometry")]
ProjectorGeometry(String),
}
impl From<Location> for LocationInternal {
fn from(value: Location) -> Self {
match value {
Location::MonitorIndex(index) => Self::MonitorIndex(index),
Location::ProjectorGeometry(geometry) => Self::ProjectorGeometry(geometry.serialize()),
}
}
}
#[derive(Serialize)]
#[non_exhaustive]
pub enum VideoMixType {
#[serde(rename = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW")]
Preview,
#[serde(rename = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM")]
Program,
#[serde(rename = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW")]
Multiview,
}
#[derive(Debug)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct QtGeometry {
pub screen_number: i32,
pub window_state: QtWindowState,
pub screen_width: i32,
pub rect: QtRect,
}
impl QtGeometry {
pub const DEFAULT_SCREEN: i32 = -1;
#[must_use]
pub fn new(rect: QtRect) -> Self {
Self {
rect,
..Self::default()
}
}
pub(crate) fn serialize(&self) -> String {
use base64::engine::{Engine, general_purpose};
const MAGIC_NUMBER: u32 = 0x01D9_D0CB;
const MAJOR_VERSION: u16 = 3;
const MINOR_VERSION: u16 = 0;
const DATA_LENGTH: usize = 66;
fn serialize_rect(data: &mut Vec<u8>, rect: &QtRect) {
data.extend(rect.left.to_be_bytes());
data.extend(rect.top.to_be_bytes());
data.extend(rect.right.to_be_bytes());
data.extend(rect.bottom.to_be_bytes());
}
let mut data = Vec::<u8>::with_capacity(DATA_LENGTH);
data.extend(MAGIC_NUMBER.to_be_bytes());
data.extend(MAJOR_VERSION.to_be_bytes());
data.extend(MINOR_VERSION.to_be_bytes());
serialize_rect(&mut data, &self.rect); serialize_rect(&mut data, &self.rect);
data.extend(self.screen_number.to_be_bytes());
data.extend(self.window_state.to_be_bytes());
data.extend(self.screen_width.to_be_bytes());
serialize_rect(&mut data, &self.rect);
general_purpose::STANDARD.encode(data)
}
}
impl Default for QtGeometry {
fn default() -> Self {
Self {
screen_number: Self::DEFAULT_SCREEN,
window_state: QtWindowState::default(),
screen_width: 0,
rect: QtRect::default(),
}
}
}
impl Display for QtGeometry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.serialize())
}
}
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)]
pub struct QtWindowState(#[debug("{:?}", FlagsDebug(self))] u32);
bitflags! {
impl QtWindowState: u32 {
const MAXIMIZED = 2;
const FULLSCREEN = 4;
}
}
impl QtWindowState {
fn to_be_bytes(self) -> [u8; 2] {
[
u8::from(self.contains(Self::MAXIMIZED)),
u8::from(self.contains(Self::FULLSCREEN)),
]
}
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct QtRect {
pub left: i32,
pub top: i32,
pub right: i32,
pub bottom: i32,
}