From 5d498ae6fc1024ed76d5aa7c05b02c8c3efc9b33 Mon Sep 17 00:00:00 2001 From: andres parra Date: Tue, 18 Aug 2026 15:20:08 +0200 Subject: [PATCH 1/2] Add cast-tv plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Casts the desktop to a Wi-Fi Direct (Miracast) smart TV via FluxCast — a bar icon and a Control Center shortcut, both opening a panel with a live TV scan, 720p/audio/bitrate options, and Cast/Stop. --- cast-tv/README.md | 89 +++++++++++ cast-tv/panel.luau | 333 +++++++++++++++++++++++++++++++++++++++++ cast-tv/plugin.toml | 28 ++++ cast-tv/shortcut.luau | 21 +++ cast-tv/thumbnail.webp | Bin 0 -> 46948 bytes cast-tv/widget.luau | 25 ++++ 6 files changed, 496 insertions(+) create mode 100644 cast-tv/README.md create mode 100644 cast-tv/panel.luau create mode 100644 cast-tv/plugin.toml create mode 100644 cast-tv/shortcut.luau create mode 100644 cast-tv/thumbnail.webp create mode 100644 cast-tv/widget.luau diff --git a/cast-tv/README.md b/cast-tv/README.md new file mode 100644 index 00000000..d6ffebc5 --- /dev/null +++ b/cast-tv/README.md @@ -0,0 +1,89 @@ +# Cast to TV + +A [Noctalia](https://github.com/noctalia-dev/noctalia) plugin that casts your +desktop to a smart TV over Wi-Fi Direct (Miracast), using +[FluxCast](https://github.com/IlyaP358/fluxcast) — a bar icon and a Control +Center shortcut, both opening the same panel: pick a TV from a live scan, +toggle 720p / TV audio / bitrate, click Cast. + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `andresparrab/cast-tv` | +| Entries | Bar widget: `bar`; panel: `picker`; shortcut: `toggle` | + +## Requirements + +Install `fluxcast` and `wf-recorder` on `PATH` (AUR: `paru -S fluxcast-git +wf-recorder`). + +- A Wi-Fi adapter with Wi-Fi Direct support, driven by NetworkManager (the + plugin scans via `fluxcast --protocol wfd --wfd-scan`, which shells out to + NetworkManager's Wi-Fi Direct discovery) +- `wf-recorder` is used as fluxcast's capture backend — see "Why + `wf-recorder`" below for why it's forced rather than left as fluxcast's + default + +The plugin only talks to the `fluxcast` binary directly — no dependency on +any personal dotfiles or wrapper scripts. If `fluxcast` isn't found, the +panel says so instead of failing silently. + +## Usage + +Add the bar icon (recommended — one click, no Control Center detour) or the +Control Center shortcut tile, or both — neither placement is automatic, both +are opt-in like any other Noctalia widget/shortcut: + +- **Bar icon**: add `"cast-tv"` to `[bar.default].end` (or `.start`/ + `.center`) in `settings.toml`, and add + ```toml + [widget.cast-tv] + type = "andresparrab/cast-tv:bar" + ``` +- **Control Center shortcut tile**: add + ```toml + [[control_center.shortcuts]] + type = "andresparrab/cast-tv:toggle" + ``` + to `settings.toml`. The Home tab's shortcut grid is capped at 6 tiles, so + this may mean swapping one out. + +Then `noctalia msg config-reload`, or restart Noctalia if you added the bar +widget (a brand-new widget type needs a real restart to register — a reload +alone won't pick it up). + +Click the bar icon (or the Control Center shortcut) to open the panel. It +scans for Wi-Fi Direct peers automatically (a few seconds — that's inherent +to Wi-Fi Direct discovery, not the plugin). Pick a TV, adjust options if +needed, click **Cast**. Click **Stop Casting** to end it. + +Or open the panel directly: + +```sh +noctalia msg panel-toggle andresparrab/cast-tv:picker +``` + +## Why `wf-recorder` as the capture backend + +FluxCast's default portal-based screen capture negotiates through two +capability checks before falling back to one that works — on a wlroots +compositor (niri, Sway, ...) that can take ~15s, long enough that some TVs +give up and disconnect before the stream starts. Forcing +`--wfd-capture-backend wf-recorder` skips that negotiation and connects in +about 2 seconds. This is hardcoded rather than exposed as an option because +it's a strict improvement on this class of compositor, not a tradeoff. + +## Notes + +- **Stop Casting** sends `pkill -f wfd-peer`, which cleanly kills the + specific `fluxcast` process this plugin started (matched by the + `--wfd-peer` flag in its command line), not any other `fluxcast` instance + you might have running (e.g. `fluxcast --tray`). +- If the plugin is ever misbehaving, you can still cast from a plain + terminal with the same tuned flags this plugin uses: + ```sh + fluxcast --protocol wfd --wfd-scan + fluxcast --wfd-capture-backend wf-recorder --wfd-monitor \ + --bitrate 6M --wfd-peer --wfd-no-audio + ``` diff --git a/cast-tv/panel.luau b/cast-tv/panel.luau new file mode 100644 index 00000000..25ec7473 --- /dev/null +++ b/cast-tv/panel.luau @@ -0,0 +1,333 @@ +--!strict + +-- Talks to `fluxcast` (github.com/IlyaP358/fluxcast) directly — no dependency +-- on any personal dotfiles wrapper script, so this plugin is self-contained: +-- install it anywhere `fluxcast` is installed and it works. The flag choices +-- below (wf-recorder capture backend, --wfd-no-audio default, 6M bitrate) +-- were tuned interactively on a niri/wlroots setup — see README.md. + +local BITRATE_OPTIONS = { "4M", "6M", "8M", "10M" } +local DEFAULT_BITRATE_INDEX = 1 -- "6M" — a good default for slides/tickets over Wi-Fi Direct + +type Peer = { mac: string, label: string } + +local peers: { Peer } = {} +local selectedIndex = 0 +local scanning = false +local scanError: string? = nil +local res720 = false +local audio = false +local bitrateIndex = DEFAULT_BITRATE_INDEX +local casting = false +local fluxcastAvailable = true + +-- Host callback args always arrive as strings (ControlCallback is +-- string-typed on the wire), regardless of what the .d.luau type hints say. +local function asBool(value: any): boolean + return value == true or value == "true" +end + +local function asIndex(value: any): number + return tonumber(value) or 0 +end + +local function peerNames(): { string } + local names = {} + for i, p in peers do + names[i] = p.label + end + return names +end + +-- noctalia.focusedOutputName() tracks keyboard-focused-window state, which a +-- panel isn't tied to the way a bar widget instance is — it can legitimately +-- come back nil here. Scan noctalia.outputs() for the focused one instead, +-- falling back to the first output (always resolves on a single-monitor setup). +local function detectMonitor(): string? + local outputs = noctalia.outputs() + for _, o in outputs do + if o.focused then + return o.name + end + end + if #outputs > 0 then + return outputs[1].name + end + return nil +end + +local render +local startScan +local startCast +local stopCast + +-- POSIX single-quote escaping: wrap in '...', turn each embedded ' into '\''. +-- Needed because this build's `runAsync` only accepts a shell command string +-- (the argv-table calling form errors "string expected, got table" here — +-- it's a real plugin_api >= 24 feature this binary doesn't implement, not +-- just a manifest-declared-version formality). +local function shellQuote(s: string): string + return "'" .. s:gsub("'", "'\\''") .. "'" +end + +function render() + if not fluxcastAvailable then + panel.render(ui.column( + { padding = 12, gap = 8 }, + { + ui.row( + { align = "center", gap = 8 }, + { + ui.glyph({ name = "cast-off", size = 18, color = "on_surface_variant" }), + ui.label({ text = "Cast to TV", fontSize = 16, fontWeight = "semibold", color = "on_surface", flexGrow = 1 }), + } + ), + ui.label({ + text = "fluxcast is not installed. Install it (e.g. `paru -S fluxcast-git`) and reopen this panel.", + fontSize = 13, + color = "on_surface_variant", + }), + } + )) + return + end + + local tvSection + if scanning then + tvSection = ui.label({ + text = "Scanning for TVs…", + fontSize = 13, + color = "on_surface_variant", + }) + elseif scanError ~= nil then + tvSection = ui.label({ + text = scanError, + fontSize = 13, + color = "error", + }) + elseif #peers == 0 then + tvSection = ui.label({ + text = "No Wi-Fi Direct TVs found nearby.", + fontSize = 13, + color = "on_surface_variant", + }) + else + tvSection = ui.select({ + options = peerNames(), + selectedIndex = selectedIndex, + controlSize = "sm", + flexGrow = 1, + onChange = function(idx: any, _text: any) + selectedIndex = asIndex(idx) + render() + end, + }) + end + + local castLabel = casting and "Stop Casting" or "Cast" + local castGlyph = casting and "cast-off" or "cast" + local castVariant = casting and "destructive" or "primary" + local castEnabled = casting or #peers > 0 + + panel.render(ui.column( + { padding = 12, gap = 12 }, + { + ui.row( + { align = "center", gap = 8 }, + { + ui.glyph({ name = "cast", size = 18, color = "primary" }), + ui.label({ + text = "Cast to TV", + fontSize = 16, + fontWeight = "semibold", + color = "on_surface", + flexGrow = 1, + }), + ui.button({ + glyph = "refresh", + variant = "ghost", + controlSize = "sm", + tooltip = "Rescan", + enabled = not scanning, + onClick = function() + startScan() + end, + }), + } + ), + ui.column( + { fill = "surface_variant/0.55", radius = 6, padding = 8, gap = 6 }, + { + ui.row( + { align = "center", gap = 6 }, + { + ui.glyph({ name = "device-tv", size = 14, color = "on_surface_variant" }), + ui.label({ text = "Smart TV", fontSize = 13, color = "on_surface_variant", flexGrow = 1 }), + } + ), + tvSection, + } + ), + ui.column( + { fill = "surface_variant/0.55", radius = 6, padding = 8, gap = 8 }, + { + ui.label({ text = "Options", fontSize = 13, color = "on_surface_variant" }), + ui.row( + { align = "center", gap = 8, minHeight = 32 }, + { + ui.label({ text = "1280×720 (more fluid)", fontSize = 14, color = "on_surface", flexGrow = 1 }), + ui.toggle({ + checked = res720, + onChange = function(value: any) + res720 = asBool(value) + render() + end, + }), + } + ), + ui.row( + { align = "center", gap = 8, minHeight = 32 }, + { + ui.label({ text = "Include TV audio", fontSize = 14, color = "on_surface", flexGrow = 1 }), + ui.toggle({ + checked = audio, + onChange = function(value: any) + audio = asBool(value) + render() + end, + }), + } + ), + ui.row( + { align = "center", gap = 8, minHeight = 32 }, + { + ui.label({ text = "Bitrate", fontSize = 14, color = "on_surface", flexGrow = 1 }), + ui.select({ + options = BITRATE_OPTIONS, + selectedIndex = bitrateIndex, + controlSize = "sm", + width = 90, + onChange = function(idx: any, _text: any) + bitrateIndex = asIndex(idx) + render() + end, + }), + } + ), + } + ), + ui.button({ + text = castLabel, + glyph = castGlyph, + variant = castVariant, + enabled = castEnabled, + onClick = function() + if casting then + stopCast() + else + startCast() + end + end, + }), + } + )) +end + +function startScan() + if not fluxcastAvailable then + return + end + scanning = true + scanError = nil + peers = {} + render() + noctalia.runAsync("fluxcast --protocol wfd --wfd-scan", function(result) + scanning = false + if result.timedOut then + scanError = "Scan timed out." + elseif result.exitCode ~= 0 then + -- A nonzero exit is always a real failure, whether or not fluxcast + -- printed anything to stdout (its own error messages go to stdout, + -- not stderr, so the old `and result.stdout == ""` check let real + -- failures — e.g. WiFi radio soft-blocked, wpa_supplicant not + -- managing the interface — silently masquerade as "no TVs found"). + scanError = "Scan failed — is Wi-Fi Direct available?" + end + local found: { Peer } = {} + for line in result.stdout:gmatch("[^\n]+") do + local mac, name = line:match("%[%d+%]%s+(%x%x:%x%x:%x%x:%x%x:%x%x:%x%x)%s+(.-)%s+via NetworkManager") + if mac then + table.insert(found, { mac = mac, label = (name ~= "" and name) or mac }) + end + end + peers = found + if #peers > 0 then + selectedIndex = 0 + end + render() + end, 15000) +end + +function startCast() + if #peers == 0 then + return + end + local peer = peers[selectedIndex + 1] + if peer == nil then + return + end + local monitor = detectMonitor() + if monitor == nil then + noctalia.notifyError("Cast to TV", "Could not detect the active output.") + return + end + + -- --wfd-capture-backend wf-recorder: skips FluxCast's default portal capture + -- negotiation, which can take ~15s and time out some TVs (confirmed on a + -- niri/wlroots setup — see README.md). --wfd-no-audio is the default since + -- typical use is screen-sharing slides/tickets, not video/sound. + local argv = { + "fluxcast", + "--wfd-capture-backend", "wf-recorder", + "--wfd-monitor", shellQuote(monitor), + "--bitrate", BITRATE_OPTIONS[bitrateIndex + 1], + "--wfd-peer", shellQuote(peer.mac), + } + if not audio then + table.insert(argv, "--wfd-no-audio") + end + if res720 then + table.insert(argv, "--output-res") + table.insert(argv, "1280x720") + end + + noctalia.runAsync(table.concat(argv, " ")) + casting = true + noctalia.notify("Casting to " .. peer.label) + render() +end + +function stopCast() + -- "wfd-peer" (no leading dashes) — pkill's getopt parser treats a + -- "--"-prefixed pattern as an unrecognized option, not the search text. + noctalia.runAsync("pkill -f wfd-peer", function(_result) + casting = false + render() + end) +end + +function onOpen(_context: any) + fluxcastAvailable = noctalia.commandExists("fluxcast") + casting = false + render() + if not fluxcastAvailable then + return + end + noctalia.processMatches(function(matched: boolean) + if matched ~= casting then + casting = matched + render() + end + end, "fluxcast", "--wfd-peer") + startScan() +end diff --git a/cast-tv/plugin.toml b/cast-tv/plugin.toml new file mode 100644 index 00000000..b7a50eb2 --- /dev/null +++ b/cast-tv/plugin.toml @@ -0,0 +1,28 @@ +id = "andresparrab/cast-tv" +name = "Cast to TV" +version = "1.0.1" +author = "andresparrab" +license = "MIT" +icon = "cast" +description = "Cast this desktop to a Wi-Fi Direct (Miracast) smart TV via FluxCast." +plugin_api = 23 +tags = ["bar", "panel", "shortcut", "network", "media"] +dependencies = ["fluxcast", "wf-recorder"] + +[[shortcut]] +id = "toggle" +entry = "shortcut.luau" + +[[widget]] +id = "bar" +entry = "widget.luau" + +[[panel]] +id = "picker" +entry = "panel.luau" +width = 340 +height = 460 +placement = "attached" +position = "auto" +dismiss_on_outside_click = true +keyboard_focus = "on_demand" diff --git a/cast-tv/shortcut.luau b/cast-tv/shortcut.luau new file mode 100644 index 00000000..6b176db6 --- /dev/null +++ b/cast-tv/shortcut.luau @@ -0,0 +1,21 @@ +--!strict + +shortcut.setLabel("Cast to TV") +shortcut.setIcon("cast-off") +noctalia.setUpdateInterval(2000) + +local lastActive = false + +function update() + noctalia.processMatches(function(matched: boolean) + if matched ~= lastActive then + lastActive = matched + shortcut.setActive(matched) + shortcut.setIcon(matched and "cast" or "cast-off") + end + end, "fluxcast", "--wfd-peer") +end + +function onClick() + noctalia.togglePanel("andresparrab/cast-tv:picker") +end diff --git a/cast-tv/thumbnail.webp b/cast-tv/thumbnail.webp new file mode 100644 index 0000000000000000000000000000000000000000..22042f7bc44e8935651614e1909df12d50d74392 GIT binary patch literal 46948 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 b7JhSnqT^g>IF68FgR&Q)vU0JHYybcNmTb;& literal 0 HcmV?d00001 diff --git a/cast-tv/widget.luau b/cast-tv/widget.luau new file mode 100644 index 00000000..a5e914ed --- /dev/null +++ b/cast-tv/widget.luau @@ -0,0 +1,25 @@ +--!strict + +local lastActive = false + +local function refreshIcon() + noctalia.processMatches(function(matched: boolean) + if matched ~= lastActive then + lastActive = matched + barWidget.setGlyph(matched and "cast" or "cast-off") + barWidget.setGlyphColor(matched and "primary" or "on_surface") + end + end, "fluxcast", "--wfd-peer") +end + +barWidget.setGlyph("cast-off") +barWidget.setTooltip("Cast to TV") +noctalia.setUpdateInterval(2000) + +function update() + refreshIcon() +end + +function onClick() + noctalia.togglePanel("andresparrab/cast-tv:picker") +end From a3baf903999e3583ccd0bb127bcfccefb3c9d114 Mon Sep 17 00:00:00 2001 From: andres parra Date: Tue, 18 Aug 2026 15:23:25 +0200 Subject: [PATCH 2/2] Add translations/en.json (required file, no keys needed) The manifest declares no label_key/description_key, but the CI validator requires the file to exist unconditionally. --- cast-tv/translations/en.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 cast-tv/translations/en.json diff --git a/cast-tv/translations/en.json b/cast-tv/translations/en.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/cast-tv/translations/en.json @@ -0,0 +1 @@ +{}