diff options
Diffstat (limited to 'src/modules/mediadisplay/visualiser.tsx')
| -rw-r--r-- | src/modules/mediadisplay/visualiser.tsx | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/src/modules/mediadisplay/visualiser.tsx b/src/modules/mediadisplay/visualiser.tsx new file mode 100644 index 0000000..fa1adc6 --- /dev/null +++ b/src/modules/mediadisplay/visualiser.tsx @@ -0,0 +1,76 @@ +import { Gtk } from "astal/gtk3"; +import cairo from "cairo"; +import AstalCava from "gi://AstalCava"; +import PangoCairo from "gi://PangoCairo"; + +export default () => ( + <drawingarea + className="visualiser" + setup={self => { + const cava = AstalCava.get_default(); + + if (cava) { + cava.set_stereo(true); + cava.set_noise_reduction(0.77); + cava.set_input(AstalCava.Input.PIPEWIRE); + + self.hook(cava, "notify::values", () => self.queue_draw()); + self.connect("size-allocate", () => { + const width = self.get_allocated_width(); + const barWidth = self + .get_style_context() + .get_property("min-width", Gtk.StateFlags.NORMAL) as number; + const gaps = self.get_style_context().get_margin(Gtk.StateFlags.NORMAL).right; + const bars = Math.floor((width - gaps) / (barWidth + gaps)); + if (bars > 0) cava.set_bars(bars); + }); + } + + self.connect("draw", (_, cr: cairo.Context) => { + const { width, height } = self.get_allocation(); + + if (!cava) { + // Show error text if cava unavailable + const fg = self.get_style_context().get_color(Gtk.StateFlags.NORMAL); + cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha); + const layout = self.create_pango_layout("Visualiser unavailable"); + const [w, h] = layout.get_pixel_size(); + cr.moveTo((width - w) / 2, (height - h) / 2); + cr.setAntialias(cairo.Antialias.BEST); + PangoCairo.show_layout(cr, layout); + + return; + } + + const bg = self.get_style_context().get_background_color(Gtk.StateFlags.NORMAL); + cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha); + const barWidth = self.get_style_context().get_property("min-width", Gtk.StateFlags.NORMAL) as number; + const gaps = self.get_style_context().get_margin(Gtk.StateFlags.NORMAL).right; + + const values = cava.get_values(); + const len = values.length - 1; + const radius = barWidth / 2; + const xOff = (width - len * (barWidth + gaps) - gaps) / 2 - radius; + const center = height / 2; + const half = Math.floor(len / 2); + + // Render channels facing each other + for (let i = half - 1; i >= 0; i--) { + const x = (half - i) * (barWidth + gaps) + xOff; + const value = center * values[i]; + cr.arc(x, center + value, radius, 0, Math.PI); + cr.arc(x, center - value, radius, Math.PI, Math.PI * 2); + cr.fill(); + } + + for (let i = half; i < len; i++) { + const x = (i + 1) * (barWidth + gaps) + xOff; + const value = center * values[i]; + cr.arc(x, center + value, radius, 0, Math.PI); + cr.arc(x, center - value, radius, Math.PI, Math.PI * 2); + cr.fill(); + } + }); + }} + /> +); |