363 lines
15 KiB
Lua
363 lines
15 KiB
Lua
-- PRÉREQUIS (à faire une fois) :
|
|
-- mkdir -p ~/.config/hypr/plugins
|
|
-- git clone https://github.com/zjeffer/split-monitor-workspaces ~/.config/hypr/plugins/split-monitor-workspaces
|
|
|
|
|
|
-- Custom, non versionned parameters
|
|
local custom_ok, custom = pcall(require, "custom")
|
|
|
|
--------------------------------------
|
|
---- SPLIT-MONITOR-WORKSPACES ----
|
|
--------------------------------------
|
|
|
|
-- Hyprland restreint package.path au dossier de config : on ajoute les patterns relatifs.
|
|
package.path = package.path .. ";./?.lua;./?/init.lua"
|
|
local smw = require("plugins.split-monitor-workspaces")
|
|
|
|
smw.setup({
|
|
workspace_count = 10,
|
|
monitor_priority = { "DP-3", "DP-2", "HDMI-A-1" },
|
|
keep_focused = true,
|
|
enable_wrapping = true,
|
|
})
|
|
|
|
|
|
------------------
|
|
---- MONITORS ----
|
|
------------------
|
|
pcall(require, "monitors")
|
|
|
|
|
|
---------------------
|
|
---- MES PROGRAMMES ----
|
|
---------------------
|
|
|
|
local terminal = "new-terminal"
|
|
local fileManager = "dolphin"
|
|
local menu = "rofi -show drun -theme ~/.config/rofi/theme.rasi"
|
|
local emoji = [[rofimoji --selector-args="-theme ~/.config/rofi/theme.rasi"]]
|
|
local hyprscript = "~/.config/hypr/hyprscript" -- ne sert plus que pour `stat`
|
|
local social = [[firefox "http://notes.tforgione.fr/" "https://ksuite.infomaniak.com/1220491/mail/0" "https://ksuite.infomaniak.com/1085038/mail" "https://web.telegram.org" "https://web.whatsapp.com/" "https://discord.com/app" "https://nuage.tforgione.fr/index.php/apps/calendar/" "https://gotify.tforgione.fr"]]
|
|
local yuzzit = [[sh -c 'firefox -P Yuzzit "https://mail.google.com/" "https://app.slack.com/client/T03UFFGJK/D07KS0S1LDT" "https://yip.atlassian.net/wiki/spaces/Y/overview" "https://yip.atlassian.net/"' & chromium "https://app.gather.town/app/bZRkf3gh7MIximOa/ytopenspace" ]]
|
|
|
|
if custom_ok and type(custom) == "table" then
|
|
social = custom.social or social
|
|
yuzzit = custom.yuzzit or yuzzit
|
|
end
|
|
|
|
-------------------
|
|
---- AUTOSTART ----
|
|
-------------------
|
|
|
|
-- exec-once -> exécuté une seule fois au démarrage via l'event hyprland.start.
|
|
-- hl.exec_cmd() est async (pas besoin de & disown).
|
|
-- Les règles (workspace/silent) se passent en 2e argument.
|
|
-- NB: l'assignation auto au workspace via exec_cmd peut être capricieuse selon les apps
|
|
-- (cf. discussions upstream sur exec_cmd + rules). Si une app n'atterrit pas au bon
|
|
-- endroit, bascule sur une window_rule par classe.
|
|
hl.on("hyprland.start", function()
|
|
hl.exec_cmd(hyprscript .. " stat") -- daemon psutil -> waybar (reste en Python)
|
|
hl.exec_cmd("hyprpaper")
|
|
hl.exec_cmd("hypridle")
|
|
hl.exec_cmd("nm-applet")
|
|
hl.exec_cmd("blueberry-tray")
|
|
hl.exec_cmd("nextcloud --background")
|
|
hl.exec_cmd("feishin", { workspace = "special:e_music silent" })
|
|
hl.exec_cmd(social, { workspace = "special:a_social silent" })
|
|
-- hl.exec_cmd("thunderbird", { workspace = "special:b_mail silent" })
|
|
end)
|
|
|
|
pcall(require, "exec-once")
|
|
|
|
|
|
-------------------------------
|
|
---- VARIABLES D'ENVIRONNEMENT ----
|
|
-------------------------------
|
|
|
|
-- NB uwsm: si tu lances Hyprland via uwsm, mets plutôt ces variables dans
|
|
-- ~/.config/uwsm/env / ~/.config/uwsm/env-hyprland (sinon ignore cette note).
|
|
hl.env("XCURSOR_SIZE", "18")
|
|
hl.env("HYPRCURSOR_SIZE", "18")
|
|
|
|
-- Nvidia
|
|
hl.env("LIBVA_DRIVER_NAME", "nvidia")
|
|
hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
|
|
|
|
|
|
-----------------------
|
|
---- LOOK AND FEEL ----
|
|
-----------------------
|
|
|
|
hl.config({
|
|
general = {
|
|
gaps_in = 0,
|
|
gaps_out = 0,
|
|
|
|
border_size = 0, -- inversé: 0 par défaut + règle positive sur Alacritty (voir plus bas)
|
|
|
|
col = {
|
|
active_border = "rgba(ffffffaa)",
|
|
inactive_border = "rgba(ffffff22)",
|
|
},
|
|
|
|
resize_on_border = false,
|
|
allow_tearing = false,
|
|
|
|
layout = "dwindle",
|
|
},
|
|
|
|
decoration = {
|
|
rounding = 0,
|
|
rounding_power = 2,
|
|
|
|
active_opacity = 1.0,
|
|
inactive_opacity = 1.0,
|
|
|
|
shadow = {
|
|
enabled = false,
|
|
range = 4,
|
|
render_power = 3,
|
|
color = 0xee1a1a1a,
|
|
},
|
|
|
|
blur = {
|
|
enabled = true,
|
|
size = 3,
|
|
passes = 1,
|
|
vibrancy = 0.1696,
|
|
},
|
|
},
|
|
|
|
animations = {
|
|
enabled = true,
|
|
},
|
|
|
|
dwindle = {
|
|
preserve_split = true,
|
|
force_split = 2, -- split à droite / en bas
|
|
},
|
|
|
|
master = {
|
|
new_status = "master",
|
|
},
|
|
|
|
misc = {
|
|
force_default_wallpaper = -1,
|
|
disable_hyprland_logo = true,
|
|
disable_splash_rendering = true,
|
|
focus_on_activate = true,
|
|
mouse_move_enables_dpms = true,
|
|
key_press_enables_dpms = true,
|
|
},
|
|
|
|
ecosystem = {
|
|
no_update_news = true,
|
|
no_donation_nag = true,
|
|
},
|
|
})
|
|
|
|
-- Courbes (anciens `bezier = ...`)
|
|
hl.curve("easeOutQuint", { type = "bezier", points = { {0.23, 1}, {0.32, 1} } })
|
|
hl.curve("easeInOutCubic", { type = "bezier", points = { {0.65, 0.05}, {0.36, 1} } })
|
|
hl.curve("linear", { type = "bezier", points = { {0, 0}, {1, 1} } })
|
|
hl.curve("almostLinear", { type = "bezier", points = { {0.5, 0.5}, {0.75, 1} } })
|
|
hl.curve("quick", { type = "bezier", points = { {0.15, 0}, {0.1, 1} } })
|
|
|
|
-- Animations (anciens `animation = NAME, ON, SPEED, CURVE, [STYLE]`)
|
|
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
|
|
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
|
|
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" })
|
|
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" })
|
|
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" })
|
|
hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" })
|
|
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" })
|
|
hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" })
|
|
hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" })
|
|
hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" })
|
|
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" })
|
|
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" })
|
|
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" })
|
|
hl.animation({ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
|
|
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" })
|
|
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
|
|
|
|
|
|
---------------
|
|
---- INPUT ----
|
|
---------------
|
|
|
|
hl.config({
|
|
input = {
|
|
kb_layout = "fr",
|
|
kb_variant = "",
|
|
kb_model = "",
|
|
kb_options = "",
|
|
kb_rules = "",
|
|
|
|
follow_mouse = 1,
|
|
|
|
sensitivity = 0,
|
|
numlock_by_default = true,
|
|
|
|
touchpad = {
|
|
natural_scroll = false,
|
|
tap_to_click = false, -- TODO: si refusé, essaie ["tap-to-click"] = false
|
|
},
|
|
},
|
|
})
|
|
|
|
hl.device({
|
|
name = "epic-mouse-v1",
|
|
sensitivity = -0.5,
|
|
})
|
|
|
|
|
|
---------------------
|
|
---- KEYBINDINGS ----
|
|
---------------------
|
|
|
|
local mainMod = "SUPER"
|
|
|
|
-- Apps & fenêtres
|
|
hl.bind(mainMod .. " + return", hl.dsp.exec_cmd(terminal))
|
|
hl.bind(mainMod .. " + A", hl.dsp.exec_cmd("firefox"))
|
|
hl.bind(mainMod .. " + SHIFT + A", hl.dsp.exec_cmd("firefox -P Yuzzit"))
|
|
hl.bind(mainMod .. " + SHIFT + C", hl.dsp.window.close())
|
|
hl.bind(mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }))
|
|
hl.bind(mainMod .. " + R", hl.dsp.exec_cmd(menu))
|
|
hl.bind(mainMod .. " + T", hl.dsp.exec_cmd(emoji))
|
|
hl.bind(mainMod .. " + Tab", hl.dsp.window.cycle_next())
|
|
hl.bind(mainMod .. " + F", hl.dsp.window.fullscreen()) -- TODO: vérifier (peut prendre { mode = ... })
|
|
|
|
hl.bind(mainMod .. " + Z", hl.dsp.exec_cmd(social))
|
|
hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(yuzzit))
|
|
hl.bind(mainMod .. " + C", hl.dsp.exec_cmd("alacritty -e numbat"))
|
|
hl.bind(mainMod .. " + P", hl.dsp.exec_cmd("pavucontrol"))
|
|
hl.bind(mainMod .. " + B", hl.dsp.exec_cmd("blueberry"))
|
|
hl.bind(mainMod .. " + D", hl.dsp.exec_cmd("makoctl mode -t do-not-disturb"))
|
|
hl.bind(mainMod .. " + M", hl.dsp.exec_cmd("~/.config/dotfiles/bin-extra/youtube"))
|
|
|
|
-- Quitter / recharger
|
|
hl.bind(mainMod .. " + SHIFT + Q", hl.dsp.exit())
|
|
hl.bind(mainMod .. " + CTRL + R", hl.dsp.exec_cmd("systemctl restart waybar --user"))
|
|
|
|
-- Moniteurs (remplace hyprscript focusnextmonitor / movewindow)
|
|
hl.bind(mainMod .. " + J", hl.dsp.focus({ monitor = "+1" }))
|
|
hl.bind(mainMod .. " + O", hl.dsp.window.move({ monitor = "+1" })) -- TODO: vérifier le champ monitor sur window.move
|
|
|
|
-- Verrouillage / extinction écran
|
|
-- DPMS direct en keybind est déconseillé -> on passe par un timer (recommandation wiki).
|
|
hl.bind(mainMod .. " + L", function()
|
|
hl.timer(function()
|
|
hl.dispatch(hl.dsp.dpms({ action = "off" })) -- TODO: si erreur, le wiki montre action="disable"
|
|
end, { timeout = 500, type = "oneshot" })
|
|
end)
|
|
hl.bind(mainMod .. " + SHIFT + L",
|
|
hl.dsp.exec_cmd([[loginctl lock-session && sleep 0.5s && (pidof hyprlock || hyprlock) && hyprctl dispatch 'hl.dsp.dpms({ action = "off" })']]))
|
|
|
|
-- Saisie d'adresses mail (wtype)
|
|
hl.bind(mainMod .. " + SHIFT + M", hl.dsp.exec_cmd([[sleep 0.5s && wtype "thomas@forgione.fr"]]))
|
|
hl.bind(mainMod .. " + SHIFT + code:48", hl.dsp.exec_cmd([[sleep 0.5s && wtype "thomas@polymny.studio"]]))
|
|
hl.bind(mainMod .. " + SHIFT + code:51", hl.dsp.exec_cmd([[sleep 0.5s && wtype "thomas.forgione@yuzzitpro.com"]]))
|
|
|
|
-- Screenshot
|
|
hl.bind("Print", hl.dsp.exec_cmd([[grim -g "$(slurp)" - | satty -f - --floating-hack --early-exit all --copy-command wl-copy ]]))
|
|
|
|
-- Workspaces (split-monitor-workspaces, par moniteur)
|
|
|
|
-- Ferme le scratchpad affiché sur le moniteur focalisé, s'il y en a un.
|
|
-- Lit l'état réel via la fenêtre active (pas de variable d'état) -> correct en multi-écran.
|
|
-- Un scratchpad visible a toujours une fenêtre focalisable, donc get_active_window suffit.
|
|
local function closeFocusedSpecial()
|
|
local w = hl.get_active_window()
|
|
local m = w and w.monitor
|
|
local special = m and m.active_special_workspace
|
|
if special ~= nil then
|
|
hl.dispatch(hl.dsp.workspace.toggle_special((special.name:gsub("^special:", ""))))
|
|
end
|
|
end
|
|
|
|
-- smw.* peut renvoyer une fonction ou un dispatcher selon le plugin : on gère les deux.
|
|
local function runSmw(action)
|
|
if type(action) == "function" then action() else hl.dispatch(action) end
|
|
end
|
|
|
|
-- Binds par keycode (azerty : la rangée des chiffres reste accessible sans Shift).
|
|
-- On ferme le scratchpad du moniteur courant AVANT de switcher : ça couvre le cas
|
|
-- "rebascule sur le workspace déjà actif" (où aucun event workspace.active ne partirait).
|
|
for i = 1, 10 do
|
|
local n = tostring(i)
|
|
hl.bind(mainMod .. " + code:" .. (9 + i), function()
|
|
closeFocusedSpecial()
|
|
runSmw(smw.workspace(n))
|
|
end)
|
|
hl.bind(mainMod .. " + SHIFT + code:" .. (9 + i), smw.move_to_workspace(n)) -- déplace la fenêtre (avec suivi)
|
|
end
|
|
|
|
-- Workspace précédent / suivant (sur le moniteur courant)
|
|
local function cycle(dir)
|
|
return function()
|
|
closeFocusedSpecial()
|
|
runSmw(smw.cycle_workspaces(dir))
|
|
end
|
|
end
|
|
hl.bind(mainMod .. " + left", cycle("prev"))
|
|
hl.bind(mainMod .. " + right", cycle("next"))
|
|
hl.bind(mainMod .. " + mouse_down", cycle("next"))
|
|
hl.bind(mainMod .. " + mouse_up", cycle("prev"))
|
|
|
|
-- Special workspaces (scratchpads). L'auto-fermeture au changement de workspace
|
|
-- est gérée dans les binds workspace ci-dessus (closeFocusedSpecial).
|
|
hl.bind(mainMod .. " + F1", hl.dsp.workspace.toggle_special("a_social"))
|
|
hl.bind(mainMod .. " + F2", hl.dsp.workspace.toggle_special("c_yuzzit"))
|
|
hl.bind(mainMod .. " + F3", hl.dsp.workspace.toggle_special("d_ai"))
|
|
hl.bind(mainMod .. " + F4", hl.dsp.workspace.toggle_special("e_music"))
|
|
|
|
-- Déplacer/redimensionner à la souris
|
|
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
|
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
|
|
|
|
-- Volume / luminosité (repeating + locked)
|
|
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), { locked = true, repeating = true })
|
|
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { locked = true, repeating = true })
|
|
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true, repeating = true })
|
|
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), { locked = true, repeating = true })
|
|
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s 2%+"), { locked = true, repeating = true })
|
|
hl.bind("XF86MonBrightnessDown",hl.dsp.exec_cmd("brightnessctl s 2%-"), { locked = true, repeating = true })
|
|
|
|
-- Lecteur média (locked) - Feishin
|
|
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next -p Feishin"), { locked = true })
|
|
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause -p Feishin"), { locked = true })
|
|
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause -p Feishin"), { locked = true })
|
|
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous -p Feishin"), { locked = true })
|
|
|
|
|
|
--------------------------------
|
|
---- WINDOWS & WORKSPACES ----
|
|
--------------------------------
|
|
|
|
-- "Bordures uniquement sur Alacritty"
|
|
-- Original (hyprlang) : windowrule = border_size 0, match:class negative:Alacritty
|
|
-- Le negative-match en Lua n'étant pas certain, on inverse la logique : bordure globale
|
|
-- à 0 (voir general.border_size ci-dessus) + règle positive sur Alacritty. Équivalent visuel.
|
|
hl.window_rule({
|
|
name = "border-only-alacritty",
|
|
match = { class = "^(Alacritty)$" },
|
|
border_size = 1,
|
|
})
|
|
|
|
|
|
--------------------------------
|
|
---- POPUP FIXES ----
|
|
--------------------------------
|
|
hl.on("window.open", function(w)
|
|
-- on ne touche qu'aux dialogs (flottants), pas aux fenêtres tuilées normales
|
|
if not w.floating then return end
|
|
|
|
-- le special actuellement visible sur le moniteur de cette fenêtre
|
|
local special = w.monitor and w.monitor.special_workspace -- nom du champ à confirmer
|
|
if special and special ~= "" then
|
|
hl.dispatch(hl.dsp.window.move({ window = w, workspace = "special:" .. special }))
|
|
end
|
|
end)
|