186 lines
7.0 KiB
TypeScript
186 lines
7.0 KiB
TypeScript
import { bind, Binding } from "astal";
|
|
import { Gtk } from "astal/gtk4";
|
|
import AstalWp from "gi://AstalWp";
|
|
|
|
const wp = AstalWp.get_default()!;
|
|
|
|
const AudioModule = () => {
|
|
const setVolumeSpeaker = (volume: number) => {
|
|
wp.defaultSpeaker.set_volume(volume / 100);
|
|
};
|
|
|
|
const setVolumeMicrophone = (volume: number) => {
|
|
wp.defaultMicrophone.set_volume(volume / 100);
|
|
};
|
|
|
|
const speakerSelector = SinkSelectPopover(AstalWp.MediaClass.AUDIO_SPEAKER);
|
|
const micSelector = SinkSelectPopover(AstalWp.MediaClass.AUDIO_MICROPHONE);
|
|
|
|
return (
|
|
<box cssClasses={["audio-box"]} vertical>
|
|
<box hexpand vexpand>
|
|
<button
|
|
onClicked={() =>
|
|
wp.defaultSpeaker.set_mute(
|
|
!wp.defaultSpeaker.get_mute(),
|
|
)
|
|
}
|
|
tooltipText={"Mute audio output"}
|
|
child={
|
|
<image
|
|
iconName={bind(wp.defaultSpeaker, "volumeIcon")}
|
|
marginEnd={3}
|
|
></image>
|
|
}
|
|
></button>
|
|
<label
|
|
label={bind(wp.defaultSpeaker, "volume").as(
|
|
v => Math.round(100 * v) + "%",
|
|
)}
|
|
></label>
|
|
<slider
|
|
value={bind(wp.defaultSpeaker, "volume").as(v => 100 * v)}
|
|
max={100}
|
|
min={0}
|
|
step={1}
|
|
hexpand
|
|
vexpand
|
|
onChangeValue={self => setVolumeSpeaker(self.value)}
|
|
></slider>
|
|
<button
|
|
cssClasses={["sink-select-button"]}
|
|
tooltipText={"Pick audio output"}
|
|
child={
|
|
<box>
|
|
<image iconName={"speaker-symbolic"}></image>
|
|
{speakerSelector}
|
|
</box>
|
|
}
|
|
onClicked={() => speakerSelector.popup()}
|
|
></button>
|
|
</box>
|
|
<box hexpand vexpand>
|
|
<button
|
|
onClicked={() =>
|
|
wp.defaultMicrophone.set_mute(
|
|
!wp.defaultMicrophone.get_mute(),
|
|
)
|
|
}
|
|
tooltipText={"Mute audio input"}
|
|
child={
|
|
<image
|
|
iconName={bind(wp.defaultMicrophone, "volumeIcon")}
|
|
marginEnd={3}
|
|
></image>
|
|
}
|
|
></button>
|
|
<label
|
|
label={bind(wp.defaultMicrophone, "volume").as(
|
|
v => Math.round(100 * v) + "%",
|
|
)}
|
|
></label>
|
|
<slider
|
|
value={bind(wp.defaultMicrophone, "volume").as(
|
|
v => 100 * v,
|
|
)}
|
|
max={100}
|
|
min={0}
|
|
step={1}
|
|
hexpand
|
|
vexpand
|
|
onChangeValue={self => setVolumeMicrophone(self.value)}
|
|
></slider>
|
|
<button
|
|
cssClasses={["sink-select-button"]}
|
|
tooltipText={"Select audio input"}
|
|
child={
|
|
<box>
|
|
<image iconName={"microphone"}></image>
|
|
{micSelector}
|
|
</box>
|
|
}
|
|
onClicked={() => micSelector.popup()}
|
|
></button>
|
|
</box>
|
|
</box>
|
|
);
|
|
};
|
|
|
|
const SinkPicker = (type: AstalWp.MediaClass) => {
|
|
const devices = bind(wp, "endpoints");
|
|
|
|
return (
|
|
<box vertical>
|
|
<label
|
|
label={`Available Audio ${type === AstalWp.MediaClass.AUDIO_SPEAKER ? "Output" : type === AstalWp.MediaClass.AUDIO_MICROPHONE ? "Input" : ""} Devices`}
|
|
cssClasses={[ 'title-2' ]}
|
|
></label>
|
|
<Gtk.Separator marginBottom={5} marginTop={3}></Gtk.Separator>
|
|
<box vertical cssClasses={["sink-picker"]}>
|
|
{devices.as(d => {
|
|
return d.map(device => {
|
|
if (device.get_media_class() !== type) {
|
|
return <box cssClasses={[ 'empty' ]}></box>;
|
|
}
|
|
return (
|
|
<button
|
|
cssClasses={bind(device, "id").as(id => {
|
|
if (
|
|
id ===
|
|
(type ===
|
|
AstalWp.MediaClass.AUDIO_SPEAKER
|
|
? wp.defaultSpeaker.id
|
|
: type ===
|
|
AstalWp.MediaClass
|
|
.AUDIO_MICROPHONE
|
|
? wp.defaultMicrophone.id
|
|
: "")
|
|
) {
|
|
return [
|
|
"sink-option",
|
|
"currently-selected-sink-option",
|
|
];
|
|
} else {
|
|
return ["sink-option"];
|
|
}
|
|
})}
|
|
child={
|
|
<box halign={Gtk.Align.START}>
|
|
<image
|
|
iconName={bind(device, "icon").as(
|
|
icon => icon,
|
|
)}
|
|
marginEnd={3}
|
|
></image>
|
|
<label
|
|
label={bind(
|
|
device,
|
|
"description",
|
|
).as(t => t ?? "")}
|
|
></label>
|
|
</box>
|
|
}
|
|
onClicked={() => {
|
|
device.set_is_default(true);
|
|
}}
|
|
></button>
|
|
);
|
|
});
|
|
})}
|
|
</box>
|
|
</box>
|
|
);
|
|
};
|
|
|
|
const SinkSelectPopover = (type: AstalWp.MediaClass) => {
|
|
const popover = new Gtk.Popover();
|
|
|
|
popover.set_child(SinkPicker(type));
|
|
|
|
return popover;
|
|
};
|
|
|
|
export default {
|
|
AudioModule,
|
|
};
|