108 lines
3.6 KiB
Lua
108 lines
3.6 KiB
Lua
-- Presentation mode: every external output shows the laptop panel.
|
|
--
|
|
-- Toggled from lua/binds/shell.lua. Mirroring is just a `mirror` field on the
|
|
-- monitor rule, so switching it off clears that field and re-applies the specs
|
|
-- from lua/monitors.lua to put the Samsung back where it belongs.
|
|
--
|
|
-- Anything plugged in while mirroring is on gets mirrored too, which is the
|
|
-- usual order at a talk: press the key, then find the HDMI cable.
|
|
|
|
local monitors = require("lua/monitors")
|
|
|
|
local M = {}
|
|
|
|
local function notify(text)
|
|
hl.notification.create({ text = text, timeout = 3000 })
|
|
end
|
|
|
|
-- The panel is off while docked with the lid shut, see lua/lid.lua. Mirroring
|
|
-- a disabled output would leave every screen black.
|
|
local function builtin()
|
|
for _, mon in ipairs(hl.get_monitors()) do
|
|
if mon.name == monitors.laptop then
|
|
return mon
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- A mirroring output drops out of hl.get_monitors(), so the only place left
|
|
-- to find it is the panel's own `mirrors` list. That list is also the state:
|
|
-- a Lua flag would reset on every config reload while the outputs stay
|
|
-- mirrored.
|
|
local function mirroring(panel)
|
|
local names = {}
|
|
for _, mon in ipairs(panel and panel.mirrors or {}) do
|
|
table.insert(names, mon.name)
|
|
end
|
|
return names
|
|
end
|
|
|
|
-- Outputs keep their own mode while mirroring, and the panel image gets
|
|
-- rescaled to fit it every frame. The Dells at 1920x1200 flicker with that,
|
|
-- so match the panel's resolution; Hyprland falls back to the preferred mode
|
|
-- if the output cannot do it.
|
|
local function mirror_all(panel)
|
|
local mode = panel.width .. "x" .. panel.height
|
|
for _, mon in ipairs(monitors.externals_present()) do
|
|
hl.monitor({ output = mon.name, mirror = monitors.laptop, mode = mode, position = "auto" })
|
|
end
|
|
end
|
|
|
|
-- The bar and popouts stay on the outputs they were created for, so
|
|
-- respawn the shell the same way lua/autostart.lua starts it.
|
|
local function restart_clients()
|
|
hl.exec_cmd("systemctl restart --user hyprpaper")
|
|
hl.exec_cmd("sh -c 'qs kill; exec uwsm app -- qs'")
|
|
end
|
|
|
|
-- hl.monitor() only takes effect after the calling handler returns, so
|
|
-- anything that has to see the new layout goes on a timer.
|
|
local function later(fn)
|
|
hl.timer(fn, { timeout = 500, type = "oneshot" })
|
|
end
|
|
|
|
-- Clearing `mirror` alone brings the image back, but Hyprland never
|
|
-- re-announces the output to clients: hyprpaper and Quickshell keep seeing
|
|
-- only the panel, however often they restart. Cycling the output through
|
|
-- disabled makes it a fresh wl_output. Undeclared outputs get their auto
|
|
-- defaults back, apply_external() then overrides what lua/monitors.lua knows
|
|
-- better.
|
|
local function unmirror_all(names)
|
|
for _, name in ipairs(names) do
|
|
hl.monitor({ output = name, mirror = "", disabled = true })
|
|
end
|
|
later(function()
|
|
for _, name in ipairs(names) do
|
|
hl.monitor({ output = name, disabled = false, mode = "preferred", position = "auto" })
|
|
end
|
|
monitors.apply_external()
|
|
later(restart_clients)
|
|
end)
|
|
end
|
|
|
|
function M.toggle()
|
|
local panel = builtin()
|
|
local mirrored = mirroring(panel)
|
|
|
|
if #mirrored > 0 then
|
|
unmirror_all(mirrored)
|
|
notify("Mirroring off")
|
|
elseif panel then
|
|
mirror_all(panel)
|
|
later(restart_clients)
|
|
notify("Mirroring " .. monitors.laptop .. " to all screens")
|
|
else
|
|
notify("Laptop panel is off, open the lid first")
|
|
end
|
|
end
|
|
|
|
hl.on("monitor.added", function()
|
|
local panel = builtin()
|
|
if #mirroring(panel) > 0 then
|
|
mirror_all(panel)
|
|
end
|
|
end)
|
|
|
|
return M
|