update of dotfiles

This commit is contained in:
Frank Zechert
2026-08-09 16:14:55 +02:00
parent fb4f54e9fa
commit d519d0d187
10 changed files with 180 additions and 245 deletions
+133 -238
View File
@@ -1,267 +1,162 @@
-- Monitor configuration and automatic and manual switching--
------------------
---- PROFILES ----
------------------
-- Profiles:
-- Key of the profile is the name of the profile
-- Profiles are ordered by priority, higher number takes precedence over smaller number
-- First (by priority) profile that matches will be applied, all others will be ignored
--
-- A profile matches the current setup if _ALL_ of its match criteria are fulfilled
-- If there are no match criteria, it will match always
local PROFILES = {
-- The most basic fallback if nothing else ever works
fallback = {
priority = -999,
matches = {},
-- if multiple profiles match, profile with highest priority wins.
-- if no profile match at all, the fallback_profile will be applied anyways.
local fallback_profile="laptop"
local profiles = {
home = {
priority = 10,
monitors = {
{
output = "",
mode = "preferred",
position = "auto",
scale = 1,
disabled = false,
},
"desc:GIGA-BYTE TECHNOLOGY CO. LTD. M34WQ 0x00000B52",
"desc:Dell Inc. DELL U2724D DL7R834",
"desc:Dell Inc. DELL U2414H 292K475V26JL",
},
disable_other_monitors = true,
monitor_config = {
["desc:GIGA-BYTE TECHNOLOGY CO. LTD. M34WQ 0x00000B52"] = {disabled=false, scale=1, position="0x0", mode="3440x1440@60"},
["desc:Dell Inc. DELL U2724D DL7R834"] = {disabled=false, scale=1, position="3440x0", mode="2556x1440@60"},
["desc:Dell Inc. DELL U2414H 292K475V26JL"] = {disabled=false, scale=1, position="760x-1080", mode="1920x1080@60"},
},
workspace_rules = {
},
},
work = {
priority = 10,
monitors = {
"eDP-1",
"desc:Dell Inc. DELL U2724DE BD19MF4",
"desc:Dell Inc. DELL U2724D 1CQFMF4",
},
disable_other_monitors = false,
monitor_config = {
["eDP-1"] = {disabled=false, scale=1, position="0x0", mode="1920x1200@120"},
["desc:Dell Inc. DELL U2724DE BD19MF4"] = {disabled=false, scale=1, position="1920x0", mode="2560x1440@120"},
["desc:Dell Inc. DELL U2724D 1CQFMF4"] = {disabled=false, scale=1, position="4480x0", mode="2560x1440@120"},
},
workspace_rules = {
},
},
laptop = {
priority = 0,
matches = {{ output = "eDP-1" }},
monitors = {
{
output = "eDP-1",
mode = "1920x1200@120",
position = "0x0",
scale = 1,
disabled = false,
},
"eDP-1",
},
},
home_docked = {
priority = 10,
matches = {
{ output = "desc:GIGA-BYTE TECHNOLOGY CO. LTD. M34WQ 0x00000B52" },
{ output = "desc:Dell Inc. DELL U2414H 292K475V26JL" },
{ output = "desc:Dell Inc. DELL U2724D DL7R834" },
disable_other_monitors = false,
monitor_config = {
["eDP-1"] = {disabled=false, scale=1, position="0x0", mode="1920x1200@120"},
},
monitors = {
{
output = "eDP-1",
mode = "preferred",
position = "0x0",
scale = 1,
disabled = true,
},
{
output = "desc:GIGA-BYTE TECHNOLOGY CO. LTD. M34WQ 0x00000B52",
mode = "3440x1440@60",
position = "0x0",
scale = 1,
disabled = false,
},
{
output = "desc:Dell Inc. DELL U2414H 292K475V26JL",
mode = "1920x1080@60",
position = "760x-1080",
scale = 1,
disabled = false,
},
{
output = "desc:Dell Inc. DELL U2724D DL7R834",
mode = "2560x1440@60",
position = "3440x0",
scale = 1,
disabled = false,
},
},
postprocess = function()
debug_notify("now applying workspace rules")
for i = 1,5 do
hl.workspace_rule({ workspace = string.format("%d", i), monitor = "desc:GIGA-BYTE TECHNOLOGY CO. LTD. M34WQ 0x00000B52"})
end
hl.workspace_rule({ workspace = "1", default = true, persistent = true})
for i = 6,9 do
hl.workspace_rule({ workspace = string.format("%d", i), monitor = "desc:Dell Inc. DELL U2724D DL7R834"})
end
hl.workspace_rule({ workspace = "9", default = true, persistent = true})
hl.workspace_rule({ workspace = "10", monitor = "desc:Dell Inc. DELL U2414H 292K475V26JL", default = true, persistent = true})
hl.dispatch(hl.dsp.focus({workspace = 10}))
hl.dispatch(hl.dsp.focus({workspace = 9}))
hl.dispatch(hl.dsp.focus({workspace = 1}))
end
},
workspace_rules = {
}
}
}
local function refresh_monitors()
hl.exec_cmd("hyprctl monitors all -j | jq -r '.[] | [.name, .description] | @tsv' > /tmp/hypr_monitors.tsv")
end
local function get_monitors()
-- FIXME: Use hl.get_monitors({all=true}) as soon as it is released
local handle = io.open("/tmp/hypr_monitors.tsv", "r")
if handle ~= nil then
local out = handle:read("*a")
handle:close()
local result = {}
for line in out:gmatch("[^\n]+") do
local name, desc = line:match("([^\t]+)\t(.+)")
table.insert(result, { name = name, description = desc })
end
return result
end
return {}
end
local apply_monitor_request_queue = {}
local function get_matching_profile()
local monitors = get_monitors()
local profiles = {} or {}
local function apply_selected_profile(profile_name, profile)
-- do we want to disable other monitors that are not used in this profile?
if profile.disable_other_monitors then
local monitors = hl.get_monitors()
table.insert(monitors, hl.get_monitor("eDP-1"))
local monitor_debug = map(monitors, function(monitor)
return monitor.name .. ": "..monitor.description
end)
debug_notify("Currently " .. #monitors .. " connected: " .. table.concat(monitor_debug, "; "))
for profile_name, profile in pairs(PROFILES) do
profile.name = profile_name or "unknown"
table.insert(profiles, profile)
end
table.sort(profiles, function(a, b) return (a.priority or 0) > (b.priority or 0) end)
for _, profile in ipairs(profiles) do
local matches = profile.matches or {}
local has_matched = {}
for _, match in ipairs(matches) do
if starts_with(match.output, "desc:") then
table.insert(has_matched, any(monitors, function(m) return "desc:" .. m.description == match.output end))
else
table.insert(has_matched, any(monitors, function(m) return m.name == match.output end))
for _, monitor in ipairs(monitors) do
if monitor ~= nil then
local name = monitor.name
local description = monitor.description
if not any(
profile.monitors,
function(wanted)
return
wanted == name or
wanted == "desc:"..description
end
) then
debug_notify("Disabling output "..name.." ("..description..")")
hl.monitor({output = name, disabled = true})
end
end
end
if #has_matched == 0 then
debug_notify("Monitor profile " .. profile.name .. " is active because it does not require any matches (fallback)")
return profile
elseif all(has_matched, function(v) return v end) then
return profile
else
local failed = map(has_matched, function(v, i)
if not v then
return matches[i].output
else
return ""
end
end)
local failedmsg = table.concat(failed, " ")
debug_notify("Monitor profile " .. profile.name .. " does not match, could not match " .. failedmsg)
end
end
return nil
-- use the configuration in the profile and apply it to hyprland
for _, monitor_selector in ipairs(profile.monitors) do
local config = profile.monitor_config[monitor_selector] or {}
config.output = monitor_selector
hl.monitor(config)
end
debug_notify("Profile " .. profile_name .. " was applied")
end
local PROFILE_CHANGE_ONGOING = false
local PROFILE_CHANGE_RESET_TIMER = nil
local PROFILE_CHANGE_START_TIMER = nil
local POSTPROCESS_TIMER = nil
local function apply_profile(profile)
if PROFILE_CHANGE_ONGOING then
debug_notify("Monitor profile change is inprogress (debounce)")
local function apply_monitor_profile()
if #apply_monitor_request_queue < 1 then
return
end
if profile == nil then
debug_notify("No monitor profile available")
table.remove(apply_monitor_request_queue)
-- try to find all profiles that match current monitors
local matching_profiles = {}
for profile_name, profile in pairs(profiles) do
if all(
profile.monitors,
function(monitor_selector)
local found =
-- if the monitor exists, hyprland should be able to return this monitor
hl.get_monitor(monitor_selector) ~= nil
if not found then
debug_notify("not able to find monitor "..monitor_selector.." for profile "..profile_name)
end
return found
end
)
then
table.insert(matching_profiles, {name = profile_name, profile = profile})
end
end
-- if no profile was found, do nothing
if #matching_profiles < 1 then
local profile = profiles[fallback_profile] or nil
if profile == nil then
debug_notify("No profiles are matching the current monitors and no fallback with name '"..fallback_profile.."' available!")
return
end
debug_notify("No profiles are matching the current monitors, applying fallback")
apply_selected_profile("fallback", profile)
return
end
debug_notify("Applying monitor profile " .. profile.name)
PROFILE_CHANGE_ONGOING = true
-- delay application of the profile for some time, as other monitor change events might still be arriving
PROFILE_CHANGE_START_TIMER = hl.timer(function()
-- after we have started applying our profile, we will generate monitor change events that we do not
-- want to process. Assume 5 seconds of non-interesting monitor events that we do not want to process
-- afterwards, reset the PROFILE_CHANGE_ONGOING flag for the next time we actually want to change
-- monitor profiles
PROFILE_CHANGE_RESET_TIMER = hl.timer(function()
PROFILE_CHANGE_ONGOING = false
debug_notify("Monitor Profile Change Debounce Finished")
PROFILE_CHANGE_RESET_TIMER = nil
end, { timeout = 5000, type = "oneshot" })
local monitors = profile.monitors
for _, monitor in ipairs(monitors) do
hl.monitor(monitor)
end
debug_notify("Monitor Profile " .. profile.name .. " successfully applied")
-- start any post processing
if profile.postprocess ~= nil then
POSTPROCESS_TIMER = hl.timer(function()
if profile.postprocess ~= nil then
profile.postprocess()
end
PROFILE_CHANGE_START_TIMER = nil
POSTPROCESS_TIMER = nil
end, { timeout = 200, type = "oneshot" })
else
PROFILE_CHANGE_START_TIMER = nil
end
end, { timeout = 500, type = "oneshot" })
-- if multiple profiles are matching, select the correct one by priority
table.sort(matching_profiles, function(a, b) return a.profile.priority > b.profile.priority end)
local matching_profile = matching_profiles[1]
local profile_name, profile = matching_profile.name, matching_profile.profile
debug_notify("Matching monitor profile found: " .. profile_name)
apply_selected_profile(profile_name, profile)
end
local function apply_current_profile()
apply_profile(get_matching_profile())
local monitor_profile_timer = nil
local function request_apply_monitor_profile(data)
debug_notify("requested monitor profile update")
table.insert(apply_monitor_request_queue, data or {})
if monitor_profile_timer == nil or not monitor_profile_timer:is_enabled() then
monitor_profile_timer = hl.timer(apply_monitor_profile, { timeout = 3000, type = "repeat" })
debug_notify("monitor profile apply timer has been restarted")
end
end
local function force_refresh_profile()
if PROFILE_CHANGE_RESET_TIMER ~= nil then PROFILE_CHANGE_RESET_TIMER:set_enabled(false) end
if POSTPROCESS_TIMER ~= nil then POSTPROCESS_TIMER:set_enabled(false) end
if PROFILE_CHANGE_START_TIMER ~= nil then PROFILE_CHANGE_START_TIMER:set_enabled(false) end
hl.on("hyprland.start", function()
debug_notify("hyprland start :)")
hl.timer(function()
hl.monitor({output = "eDP-1", disabled = false})
hl.dispatch(function() request_apply_monitor_profile() end)
end, { timeout = 1000, type = "oneshot" })
end)
PROFILE_CHANGE_RESET_TIMER = nil
POSTPROCESS_TIMER = nil
PROFILE_CHANGE_START_TIMER = nil
PROFILE_CHANGE_ONGOING = false
debug_notify("Force applying monitor profile")
refresh_monitors()
apply_current_profile()
end
local function on_lid_close()
debug_notify("Lid was closed")
refresh_monitors()
hl.monitor({ output = "eDP-1", disabled = true })
end
local function on_lid_open()
debug_notify("Lid was opened")
refresh_monitors()
apply_current_profile()
end
local function on_monitor_added(_event)
debug_notify("A monitor was added")
refresh_monitors()
apply_current_profile()
end
local function on_monitor_removed(_event)
debug_notify("A monitor was removed")
refresh_monitors()
apply_current_profile()
end
hl.bind("switch:on:Lid Switch", on_lid_close, { locked = true })
hl.bind("switch:off:Lid Switch", on_lid_open, { locked = true })
hl.bind("SUPER + P", function() force_refresh_profile() end, { locked = true, description = "Force Refresh Monitor Profile" })
hl.on("monitor.added", on_monitor_added)
hl.on("monitor.removed", on_monitor_removed)
hl.on("hyprland.start", function() refresh_monitors() end)
hl.on("monitor.layout_changed", request_apply_monitor_profile)
hl.bind("SUPER + P", function()
hl.monitor({output = "eDP-1", disabled = true})
request_apply_monitor_profile()
end, {locked=true, description="Detect & Apply Monitor Profile"})
hl.bind("SUPER + SHIFT + P", function()
hl.monitor({output = "eDP-1", disabled = false})
hl.dispatch(function() request_apply_monitor_profile() end)
end, {locked=true, description="Detect & Apply Monitor Profile"})
@@ -217,6 +217,10 @@ rule(
{ matches_class("^(org.gnome.seahorse.Application)$") },
{ float(), center(), size(800, 600) }
)
rule(
{ matches_class("^(hyprland-share-picker)$") },
{ float(), center(), pin() }
)
-- Nemo
rule(
@@ -277,3 +281,9 @@ rule(
{ float(), size(700, 600), center(), pin() }
)
-- Teams
rule(
{ matches_title("^(Teams for Linux - Screen is being shared)$") },
{ float(), size(600, 300), pin() }
)