diff --git a/.gitignore b/.gitignore index aa7feaa..ad011d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ .stat.txt .mypy_cache monitors.conf +monitors.lua exec-once.conf +exec-once.lua +plugins diff --git a/hyprland.conf b/hyprland.conf deleted file mode 100644 index 0498ae4..0000000 --- a/hyprland.conf +++ /dev/null @@ -1,355 +0,0 @@ -# This is an example Hyprland config file. -# Refer to the wiki for more information. -# https://wiki.hyprland.org/Configuring/ - -# Please note not all available settings / options are set here. -# For a full list, see the wiki - -# You can split this configuration into multiple files -# Create your files separately and then link them to this file like this: -# source = ~/.config/hypr/myColors.conf - - -################ -### MONITORS ### -################ - -source = ~/.config/hypr/monitors.conf - - -################### -### MY PROGRAMS ### -################### - -# See https://wiki.hyprland.org/Configuring/Keywords/ - -# Set programs that you use -$terminal = new-terminal -$fileManager = dolphin -$menu = rofi -show drun -theme ~/.config/rofi/theme.rasi -$emoji = rofimoji --selector-args="-theme ~/.config/rofi/theme.rasi" -$hyprscript = ~/.config/hypr/hyprscript -$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" -$yuzzit = firefox -P Yuzzit "https://mail.google.com/" "https://app.gather.town/app/bZRkf3gh7MIximOa/ytopenspace" "https://app.slack.com/client/T03UFFGJK/D07KS0S1LDT" "https://yip.atlassian.net/wiki/spaces/Y/overview" "https://yip.atlassian.net/" - - -################# -### AUTOSTART ### -################# - -# Autostart necessary processes (like notifications daemons, status bars, etc.) -# Or execute your favorite apps at launch like this: - -exec-once = $hyprscript stat -exec-once = hyprpaper -exec-once = XDG_CURRENT_DESKTOP=sway XDG_SESSION_DESKTOP=sway QT_QPA_PLATFORM=wayland flameshot -exec-once = hypridle -exec-once = nm-applet -exec-once = blueberry-tray -exec-once = nextcloud --background -exec-once = [workspace special:e_music silent] feishin -exec-once = [workspace special:a_social silent] $social -# exec-once = [workspace special:b_mail silent] thunderbird - -source = ~/.config/hypr/exec-once.conf - -############################# -### ENVIRONMENT VARIABLES ### -############################# - -# See https://wiki.hyprland.org/Configuring/Environment-variables/ - -env = XCURSOR_SIZE,18 -env = HYPRCURSOR_SIZE,18 - - -##################### -### LOOK AND FEEL ### -##################### - -# Refer to https://wiki.hyprland.org/Configuring/Variables/ - -# https://wiki.hyprland.org/Configuring/Variables/#general -general { - gaps_in = 0 - gaps_out = 0 - - border_size = 1 - - # https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors - col.active_border = rgba(ffffffaa) - col.inactive_border = rgba(ffffff22) - - # Set to true enable resizing windows by clicking and dragging on borders and gaps - resize_on_border = false - - # Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on - allow_tearing = false - - layout = dwindle -} - -# https://wiki.hyprland.org/Configuring/Variables/#decoration -decoration { - rounding = 0 - rounding_power = 2 - - # Change transparency of focused and unfocused windows - active_opacity = 1.0 - inactive_opacity = 1.0 - - shadow { - enabled = false - range = 4 - render_power = 3 - color = rgba(1a1a1aee) - } - - # https://wiki.hyprland.org/Configuring/Variables/#blur - blur { - enabled = true - size = 3 - passes = 1 - - vibrancy = 0.1696 - } -} - -# https://wiki.hyprland.org/Configuring/Variables/#animations -animations { - enabled = yes, please :) - - # Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more - - bezier = easeOutQuint,0.23,1,0.32,1 - bezier = easeInOutCubic,0.65,0.05,0.36,1 - bezier = linear,0,0,1,1 - bezier = almostLinear,0.5,0.5,0.75,1.0 - bezier = quick,0.15,0,0.1,1 - - animation = global, 1, 10, default - animation = border, 1, 5.39, easeOutQuint - animation = windows, 1, 4.79, easeOutQuint - animation = windowsIn, 1, 4.1, easeOutQuint, popin 87% - animation = windowsOut, 1, 1.49, linear, popin 87% - animation = fadeIn, 1, 1.73, almostLinear - animation = fadeOut, 1, 1.46, almostLinear - animation = fade, 1, 3.03, quick - animation = layers, 1, 3.81, easeOutQuint - animation = layersIn, 1, 4, easeOutQuint, fade - animation = layersOut, 1, 1.5, linear, fade - animation = fadeLayersIn, 1, 1.79, almostLinear - animation = fadeLayersOut, 1, 1.39, almostLinear - animation = workspaces, 1, 1.94, almostLinear, fade - animation = workspacesIn, 1, 1.21, almostLinear, fade - animation = workspacesOut, 1, 1.94, almostLinear, fade -} - -# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/ -# "Smart gaps" / "No gaps when only" -# uncomment all if you wish to use that. -# workspace = w[tv1], gapsout:0, gapsin:0 -# workspace = f[1], gapsout:0, gapsin:0 -# windowrule = bordersize 0, floating:0, onworkspace:w[tv1] -# windowrule = rounding 0, floating:0, onworkspace:w[tv1] -# windowrule = bordersize 0, floating:0, onworkspace:f[1] -# windowrule = rounding 0, floating:0, onworkspace:f[1] - -# Borders only on alacritty -windowrule = border_size 0, match:class negative:Alacritty - -# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more -dwindle { - # pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below - preserve_split = true # You probably want this - force_split = 2 # split to the right / bottom -} - -# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more -master { - new_status = master -} - -# https://wiki.hyprland.org/Configuring/Variables/#misc -misc { - force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers - disable_hyprland_logo = true # If true disables the random hyprland logo / anime girl background. :( - disable_splash_rendering = true # If true disables the random hyprland logo / anime girl background. :( - focus_on_activate = true - mouse_move_enables_dpms = true - key_press_enables_dpms = true -} - - -############# -### INPUT ### -############# - -# https://wiki.hyprland.org/Configuring/Variables/#input -input { - kb_layout = fr - kb_variant = - kb_model = - kb_options = - kb_rules = - - follow_mouse = 1 - - sensitivity = 0 # -1.0 - 1.0, 0 means no modification. - numlock_by_default = true - - touchpad { - natural_scroll = false - tap-to-click = false - } -} - -# Example per-device config -# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more -device { - name = epic-mouse-v1 - sensitivity = -0.5 -} - -################# -### ECOSYSTEM ### -################# -ecosystem { - no_update_news = true - no_donation_nag = true -} - - -################### -### KEYBINDINGS ### -################### - -# See https://wiki.hyprland.org/Configuring/Keywords/ -$mainMod = SUPER # Sets "Windows" key as main modifier - -# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more -bind = $mainMod, return, exec, $terminal -bind = $mainMod, A, exec, firefox -bind = $mainMod SHIFT, A, exec, firefox -P Yuzzit -bind = $mainMod SHIFT, C, killactive, -bind = $mainMod, V, togglefloating, -bind = $mainMod, R, exec, $menu -bind = $mainMod, T, exec, $emoji -bind = $mainMod, J, exec, $hyprscript focusnextmonitor - -bind = $mainMod, O, exec, $hyprscript movewindow -bind = $mainMod, Tab, cyclenext - -bind = $mainMod CTRL, R, exec, $hyprscript reload -bind = $mainMod SHIFT, Q, exec, hyprctl dispatch exit - -bind = $mainMod, Z, exec, $social -bind = $mainMod, E, exec, $yuzzit - -bind = $mainMod, C, exec, alacritty -e numbat -bind = $mainMod, P, exec, pavucontrol -bind = $mainMod, B, exec, blueberry -bind = $mainMod, D, exec, makoctl mode -t do-not-disturb - -bind = $mainMod, L, exec, sleep 0.5s && hyprctl dispatch dpms off -bind = $mainMod SHIFT, L, exec, loginctl lock-session && sleep 0.5s && (pidof hyprlock || hyprlock) && hyprctl dispatch dpms off - -bind = $mainMod SHIFT, M, exec, sleep 0.5s && wtype "thomas@forgione.fr" -bind = $mainMod SHIFT, code:48, exec, sleep 0.5s && wtype "thomas@polymny.studio" -bind = $mainMod SHIFT, code:51, exec, sleep 0.5s && wtype "thomas.forgione@yuzzitpro.com" - -# Move focus with mainMod + arrow keys -# bind = $mainMod, left, movefocus, l -# bind = $mainMod, right, movefocus, r -# bind = $mainMod, up, movefocus, u -# bind = $mainMod, down, movefocus, d - -bind = $mainMod, left, exec, $hyprscript workspace previous -bind = $mainMod, right, exec, $hyprscript workspace next - -# Switch workspaces with mainMod + [0-9] -bind = $mainMod, code:10, exec, $hyprscript workspace 1 -bind = $mainMod, code:11, exec, $hyprscript workspace 2 -bind = $mainMod, code:12, exec, $hyprscript workspace 3 -bind = $mainMod, code:13, exec, $hyprscript workspace 4 -bind = $mainMod, code:14, exec, $hyprscript workspace 5 -bind = $mainMod, code:15, exec, $hyprscript workspace 6 -bind = $mainMod, code:16, exec, $hyprscript workspace 7 -bind = $mainMod, code:17, exec, $hyprscript workspace 8 -bind = $mainMod, code:18, exec, $hyprscript workspace 9 -bind = $mainMod, code:19, exec, $hyprscript workspace 10 - -# Move active window to a workspace with mainMod + SHIFT + [0-9] -bind = $mainMod SHIFT, code:10, exec, $hyprscript movetoworkspace 1 -bind = $mainMod SHIFT, code:11, exec, $hyprscript movetoworkspace 2 -bind = $mainMod SHIFT, code:12, exec, $hyprscript movetoworkspace 3 -bind = $mainMod SHIFT, code:13, exec, $hyprscript movetoworkspace 4 -bind = $mainMod SHIFT, code:14, exec, $hyprscript movetoworkspace 5 -bind = $mainMod SHIFT, code:15, exec, $hyprscript movetoworkspace 6 -bind = $mainMod SHIFT, code:16, exec, $hyprscript movetoworkspace 7 -bind = $mainMod SHIFT, code:17, exec, $hyprscript movetoworkspace 8 -bind = $mainMod SHIFT, code:18, exec, $hyprscript movetoworkspace 9 -bind = $mainMod SHIFT, code:19, exec, $hyprscript movetoworkspace 10 - -# Open youtube in MPV -bind = $mainMod, M, exec, ~/.config/dotfiles/bin-extra/youtube - -# Screenshot -bind = , Print, exec, XDG_CURRENT_DESKTOP=sway XDG_SESSION_DESKTOP=sway QT_QPA_PLATFORM=wayland flameshot gui -# bind =, Print, exec, grim -g "$(slurp)" - | wl-copy - -# Special workspaces -bind = $mainMod, F1, togglespecialworkspace, a_social -# bind = $mainMod, F2, togglespecialworkspace, b_mail -bind = $mainMod, F2, togglespecialworkspace, c_yuzzit -bind = $mainMod, F3, togglespecialworkspace, d_ai -bind = $mainMod, F4, togglespecialworkspace, e_music - -# Scroll through existing workspaces with mainMod + scroll -bind = $mainMod, mouse_down, workspace, e+1 -bind = $mainMod, mouse_up, workspace, e-1 - -# Move/resize windows with mainMod + LMB/RMB and dragging -bindm = $mainMod, mouse:272, movewindow -bindm = $mainMod, mouse:273, resizewindow - -# Laptop multimedia keys for volume and LCD brightness -bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ -bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- -bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle -bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle -bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 2%+ -bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 2%- - -# Requires playerctl -bindl = , XF86AudioNext, exec, playerctl next -p Feishin -bindl = , XF86AudioPause, exec, playerctl play-pause -p Feishin -bindl = , XF86AudioPlay, exec, playerctl play-pause -p Feishin -bindl = , XF86AudioPrev, exec, playerctl previous -p Feishin - -############################## -### WINDOWS AND WORKSPACES ### -############################## - -# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more -# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules - -# Example windowrule -# windowrule = float,class:^(kitty)$,title:^(kitty)$ - -# Ignore maximize requests from apps. You'll probably like this. -# windowrule = suppressevent maximize, match:class .* - -# Fix some dragging issues with XWayland -# windowrule = no_focus on,match:class ^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0 - -# Nvidia config -env = LIBVA_DRIVER_NAME,nvidia -env = __GLX_VENDOR_LIBRARY_NAME,nvidia - -# Flameshot -# windowrule = pin, class:flameshot,title:flameshot-pin -# windowrule = float, class:flameshot,title:flameshot-pin -# windowrule = noborder, class:flameshot,title:flameshot-pin -# windowrule = noblur, class:flameshot,title:flameshot-pin -# windowrule = noshadow, class:flameshot,title:flameshot-pin diff --git a/hyprland.lua b/hyprland.lua new file mode 100644 index 0000000..30e525c --- /dev/null +++ b/hyprland.lua @@ -0,0 +1,340 @@ +-- 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 + + +-------------------------------------- +---- 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 ---- +------------------ +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 = [[firefox -P Yuzzit "https://mail.google.com/" "https://app.gather.town/app/bZRkf3gh7MIximOa/ytopenspace" "https://app.slack.com/client/T03UFFGJK/D07KS0S1LDT" "https://yip.atlassian.net/wiki/spaces/Y/overview" "https://yip.atlassian.net/"]] + + +------------------- +---- 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)" - | swappy -f -]])) + +-- 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, +}) diff --git a/hyprscript b/hyprscript deleted file mode 100755 index baae722..0000000 --- a/hyprscript +++ /dev/null @@ -1,328 +0,0 @@ -#!/usr/bin/env python - -import json -import os -import os.path -import subprocess -import sys -import typing - -os.chdir(os.path.expanduser('~/.config/hypr')) -bars = "_▂▃▄▅▆▇█" -batteries = "󰂎󰁺󰁻󰁼󰁽󰁾󰁿󰂀󰂁󰂁󰁹" -batteries_charging = "󰢟󰢜󰂆󰂇󰂈󰢝󰂉󰢞󰂊󰂋󰂅" - - -class Monitor: - """ - A monitor on which many workspaces can appear. - """ - - def __init__(self, id: int, name: str, special_workspace: typing.Optional['Workspace']): - """ - Constructs a monitor from its id and its name. - """ - self.id = id - self.name = name - self.special_workspace = special_workspace - - def __eq__(self, other): - """ - Returns true if the monitors are the same. - """ - self.id == other.id - - @staticmethod - def active(monitors: list['Monitor']) -> 'Monitor': - """ - Returns the active monitor. - """ - proc = subprocess.run(['hyprctl', 'activeworkspace', '-j'], capture_output=True) - result = json.loads(proc.stdout) - return next((monitor for monitor in monitors if monitor.id == result['monitorID'])) - - @staticmethod - def all() -> list['Monitor']: - """ - Returns the list of available monitors. - """ - monitors: list['Monitor'] = [] - - proc = subprocess.run(['hyprctl', 'monitors', '-j'], capture_output=True) - result = json.loads(proc.stdout) - - for mon in result: - if mon['specialWorkspace']['id'] == 0: - monitors.append(Monitor(mon['id'], mon['name'], None)) - else: - special_workspace = Workspace(mon['specialWorkspace']['id'], mon['specialWorkspace']['name']) - monitors.append(Monitor(mon['id'], mon['name'], special_workspace)) - - return monitors - - def previous(self, monitors: list['Monitor'], move: bool = False): - """ - Moves the focus to the previous monitor. - - Params: - move: whether you want to move the active window to the new monitor. - """ - mon = monitors[(monitors.index(self) - 1) % len(monitors)] - - if move: - subprocess.run(['hyprctl', 'dispatch', 'movewindow', 'mon:' + str(mon.id)]) - else: - subprocess.run(['hyprctl', 'dispatch', 'focusmonitor', str(mon.id)]) - - def next(self, monitors: list['Monitor'], move: bool = False): - """ - Moves the focus to the next monitor. - - Params: - move: whether you want to move the active window to the new monitor. - """ - mon = monitors[(monitors.index(self) + 1) % len(monitors)] - - if move: - subprocess.run(['hyprctl', 'dispatch', 'movewindow', 'mon:' + str(mon.name)]) - else: - subprocess.run(['hyprctl', 'dispatch', 'focusmonitor', str(mon.name)]) - - -class Workspace: - """ - A workspace bound to a monitor. - - Workspaces with id from 10 * n + k belongs to the same monitor for k in [1, 10]. - """ - - def __init__(self, id: int, name: str): - """ - Constructs a workspace from its id and its name. - """ - self.id = id - self.name = name - - def is_special(self) -> bool: - """ - Returns true if the workspace is a magic workspace. - """ - return self.id < 0 - - def special_name(self) -> typing.Optional[str]: - """ - Returns the part after the : if the workspace is special, none otherwise. - """ - if self.is_special(): - return ':'.join(self.name.split(':')[1:]) - else: - return None - - @staticmethod - def active() -> 'Workspace': - """ - Returns the active workspace. - """ - proc = subprocess.run(['hyprctl', 'activeworkspace', '-j'], capture_output=True) - result = json.loads(proc.stdout) - return Workspace(result['id'], result['name']) - - def nth(self, n: int, monitor: Monitor, move: bool = False): - """ - Goes to the nth workspace on the same monitor as the current workspace. - - Params: - n: workspace to go to, between 1 and 10. - monitor: active monitor. - move: whether you want to move the active window to the new workspace. - """ - new_id = (self.id - 1) // 10 * 10 + n - - subprocess.run(['hyprctl', 'dispatch', 'movetoworkspace' if move else 'workspace', str(new_id)]) - - current = monitor.special_workspace - if current is not None: - subprocess.run(['hyprctl', 'dispatch', 'togglespecialworkspace', current.special_name()]) - - def previous(self, monitor: Monitor, move: bool = False): - """ - Goes to the previous workspace on the same monitor, or the last if we're on the first. - - Params: - monitor: active monitor. - move: whether you want to move the active window to the new workspace. - """ - new_id = self.id - 1 - - if new_id % 10 == 0: - new_id += 10 - - subprocess.run(['hyprctl', 'dispatch', 'movetoworkspace' if move else 'workspace', str(new_id)]) - - current = monitor.special_workspace - if current is not None: - subprocess.run(['hyprctl', 'dispatch', 'togglespecialworkspace', current.special_name()]) - - def next(self, monitor: Monitor, move: bool = False): - """ - Goes to the next workspace on the same monitor, or the first if we're on the last. - - Params: - monitor: active monitor. - move: whether you want to move the active window to the new workspace. - """ - new_id = self.id + 1 - - if new_id % 10 == 1: - new_id -= 10 - - subprocess.run(['hyprctl', 'dispatch', 'movetoworkspace' if move else 'workspace', str(new_id)]) - - current = monitor.special_workspace - if current is not None: - subprocess.run(['hyprctl', 'dispatch', 'togglespecialworkspace', current.special_name()]) - - -def to_bar(x): - return bars[round((len(bars) - 1) * x / 100)] - - -def monitor_stats(): - """ - Monitors CPU and MEM usage and prints info in file. - """ - - try: - import psutil - except ModuleNotFoundError: - print('Stats requires psutil (run `pip install psutil`)', file=sys.stderr) - sys.exit(1) - - cpu_values = [0] * 10 - mem_values = [0] * 10 - temp_values = [0] * 10 - - min_temp = 45 - max_temp = 95 - - while True: - cpu_percent = psutil.cpu_percent(interval=2) - mem_percent = psutil.virtual_memory().percent - battery = psutil.sensors_battery() - all_temps = psutil.sensors_temperatures(fahrenheit=False) - - temps = all_temps.get('k10temp', None) - if temps is None: - temps = all_temps.get('coretemp') - - temp = max([x.current for x in temps if x.label != 'Tctl']) - - temp_percent = 100 * min(max([temp - min_temp, 0]) / (max_temp - min_temp), 1) - - for i in range(len(cpu_values) - 1): - cpu_values[i] = cpu_values[i+1] - mem_values[i] = mem_values[i+1] - temp_values[i] = temp_values[i+1] - - cpu_values[-1] = cpu_percent - mem_values[-1] = mem_percent - temp_values[-1] = temp_percent - - # Notify when memory greater than 75% - if mem_percent >= 75 and not any(map(lambda x: x > 75, mem_values[:-1])): - subprocess.run(['notify-send', '-u', 'critical', 'High memory usage', "% 5.1f" % mem_percent + '% of RAM used']) - - if battery is None: - battery_json = '{"text": "", "class": "hidden"}' - else: - battery_index = round(battery.percent / 10) - battery_icon = batteries_charging[battery_index] if battery.power_plugged else batteries[battery_index] - - if battery.power_plugged: - battery_class = 'charging' - elif battery.percent < 30.0: - battery_class = 'low' - elif battery.percent < 15.0: - battery_class = 'critical' - else: - battery_class = None - - battery_text = "% 4.0f" % battery.percent if battery.percent <= 99.95 else " 100" - battery_json = f', "class": "{battery_class}"' if battery_class else '' - battery_json = '{"text": "' + battery_icon + ' ' + battery_text + '%"' + battery_json + '}' - - cpu_percent_text = "% 5.1f" % cpu_percent if cpu_percent <= 99.95 else " 100" - mem_percent_text = "% 5.1f" % mem_percent if mem_percent <= 99.95 else " 100" - - with open('.stat.txt', 'w') as f: - f.write( - ' ' + ''.join([to_bar(x) for x in cpu_values]) + ' ' + cpu_percent_text + '%\n' + - ' ' + ''.join([to_bar(x) for x in mem_values]) + ' ' + mem_percent_text + '%\n' + - '  ' + ''.join([to_bar(x) for x in temp_values]) + ' ' + "% 5.1f" % temp + '°\n' + - battery_json - ) - - -def start_hyprlock(): - proc = subprocess.run(['pidof', 'hyprlock']) - if proc.returncode != 0: - # We need to start this in the background so that the screen turns off afterwards - subprocess.Popen(['hyprlock'], start_new_session=True) - - -def lock_session(): - if os.environ.get('HYPR_DISABLE_LOCK', None) is None: - subprocess.run(['loginctl', 'lock-session']) - - -def main(): - if len(sys.argv) < 2: - return - - if sys.argv[1] == 'workspace' or sys.argv[1] == 'movetoworkspace': - monitors = Monitor.all() - monitor = Monitor.active(monitors) - - workspace = Workspace.active() - move = sys.argv[1] == 'movetoworkspace' - - if sys.argv[2] == 'next': - workspace.next(monitor, move) - - elif sys.argv[2] == 'previous': - workspace.previous(monitor, move) - - else: - new_workspace = int(sys.argv[2]) - workspace.nth(new_workspace, monitor, move) - - elif sys.argv[1] in ['movewindow', 'focusnextmonitor', 'focuspreviousmonitor']: - monitors = Monitor.all() - monitor = Monitor.active(monitors) - - if sys.argv[1] == 'movewindow': - monitor.next(monitors, True) - elif sys.argv[1] == 'focusnextmonitor': - monitor.next(monitors) - elif sys.argv[1] == 'focuspreviousmonitor': - monitor.previous(monitors) - - elif sys.argv[1] == 'reload': - subprocess.run(['systemctl', 'restart', 'waybar', '--user']) - - elif sys.argv[1] == 'stat': - monitor_stats() - - elif sys.argv[1] == 'start-hyprlock': - start_hyprlock() - - elif sys.argv[1] == 'lock-session': - lock_session() - - else: - print(f'Command not found: {sys.argv[1]}', file=sys.stderr) - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/hyprstat b/hyprstat new file mode 100755 index 0000000..c2544bc --- /dev/null +++ b/hyprstat @@ -0,0 +1,145 @@ +#!/usr/bin/env python + +import os +import os.path +import subprocess +import sys + +os.chdir(os.path.expanduser("~/.config/hypr")) +bars = "_▂▃▄▅▆▇█" +batteries = "󰂎󰁺󰁻󰁼󰁽󰁾󰁿󰂀󰂁󰂁󰁹" +batteries_charging = "󰢟󰢜󰂆󰂇󰂈󰢝󰂉󰢞󰂊󰂋󰂅" + + +def to_bar(x): + return bars[round((len(bars) - 1) * x / 100)] + + +def monitor_stats(): + """ + Monitors CPU and MEM usage and prints info in file. + """ + + try: + import psutil + except ModuleNotFoundError: + print("Stats requires psutil (run `pip install psutil`)", file=sys.stderr) + sys.exit(1) + + cpu_values = [0] * 10 + mem_values = [0] * 10 + temp_values = [0] * 10 + + min_temp = 45 + max_temp = 95 + + while True: + cpu_percent = psutil.cpu_percent(interval=2) + mem_percent = psutil.virtual_memory().percent + battery = psutil.sensors_battery() + all_temps = psutil.sensors_temperatures(fahrenheit=False) + + temps = all_temps.get("k10temp", None) + if temps is None: + temps = all_temps.get("coretemp") + + temp = max([x.current for x in temps if x.label != "Tctl"]) + + temp_percent = 100 * min(max([temp - min_temp, 0]) / (max_temp - min_temp), 1) + + for i in range(len(cpu_values) - 1): + cpu_values[i] = cpu_values[i + 1] + mem_values[i] = mem_values[i + 1] + temp_values[i] = temp_values[i + 1] + + cpu_values[-1] = cpu_percent + mem_values[-1] = mem_percent + temp_values[-1] = temp_percent + + # Notify when memory greater than 75% + if mem_percent >= 75 and not any(map(lambda x: x > 75, mem_values[:-1])): + subprocess.run( + [ + "notify-send", + "-u", + "critical", + "High memory usage", + "% 5.1f" % mem_percent + "% of RAM used", + ] + ) + + if battery is None: + battery_json = '{"text": "", "class": "hidden"}' + else: + battery_index = round(battery.percent / 10) + battery_icon = ( + batteries_charging[battery_index] + if battery.power_plugged + else batteries[battery_index] + ) + + if battery.power_plugged: + battery_class = "charging" + elif battery.percent < 30.0: + battery_class = "low" + elif battery.percent < 15.0: + battery_class = "critical" + else: + battery_class = None + + battery_text = ( + "% 4.0f" % battery.percent if battery.percent <= 99.95 else " 100" + ) + battery_json = f', "class": "{battery_class}"' if battery_class else "" + battery_json = ( + '{"text": "' + + battery_icon + + " " + + battery_text + + '%"' + + battery_json + + "}" + ) + + cpu_percent_text = "% 5.1f" % cpu_percent if cpu_percent <= 99.95 else " 100" + mem_percent_text = "% 5.1f" % mem_percent if mem_percent <= 99.95 else " 100" + + with open(".stat.txt", "w") as f: + f.write( + " " + + "".join([to_bar(x) for x in cpu_values]) + + " " + + cpu_percent_text + + "%\n" + + " " + + "".join([to_bar(x) for x in mem_values]) + + " " + + mem_percent_text + + "%\n" + + "  " + + "".join([to_bar(x) for x in temp_values]) + + " " + + "% 5.1f" % temp + + "°\n" + + battery_json + ) + + +def start_hyprlock(): + proc = subprocess.run(["pidof", "hyprlock"]) + if proc.returncode != 0: + # We need to start this in the background so that the screen turns off afterwards + subprocess.Popen(["hyprlock"], start_new_session=True) + + +def lock_session(): + if os.environ.get("HYPR_DISABLE_LOCK", None) is None: + subprocess.run(["loginctl", "lock-session"]) + + +def main(): + monitor_stats() + + +if __name__ == "__main__": + main() diff --git a/waybar/config b/waybar/config index e9ba58d..7df6cd8 100644 --- a/waybar/config +++ b/waybar/config @@ -21,6 +21,7 @@ "custom/cpu", "custom/mem", "custom/notification", + "custom/mic", "custom/battery", "tray", "clock" @@ -41,45 +42,9 @@ "c_yuzzit": "Y", "d_ai": "AI", "e_music": "󰎇", - "11": "1", - "12": "2", - "13": "3", - "14": "4", - "15": "5", - "16": "6", - "17": "7", - "18": "8", - "19": "9", - "20": "0", - "21": "1", - "22": "2", - "23": "3", - "24": "4", - "25": "5", - "26": "6", - "27": "7", - "28": "8", - "29": "9", - "30": "0", - "31": "1", - "32": "2", - "33": "3", - "34": "4", - "35": "5", - "36": "6", - "37": "7", - "38": "8", - "39": "9", - "40": "0", }, "on-click": "activate", "sort-by-number": true, - "persistent-workspaces": { - "eDP-1": [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], - "DP-3": [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], - "DP-2": [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], - "HDMI-A-1": [31, 32, 33, 34, 35, 36, 37, 38, 39, 40] - }, "show-special": true }, "clock": { @@ -175,5 +140,11 @@ "exec": "makoctl mode | grep '^do-not-disturb$' > /dev/null 2>&1 && echo '󱙍 ' || echo '󰍡 '", "on-click": "makoctl mode -t do-not-disturb", "restart-interval": 2 + }, + "custom/mic": { + "exec": "pactl get-source-mute alsa_input.usb-Generic_Blue_Microphones_LT_2206140951259D030044_111000-00.analog-stereo | grep -q yes && echo '  ' || echo '  '", + "on-click": "pactl set-source-mute alsa_input.usb-Generic_Blue_Microphones_LT_2206140951259D030044_111000-00.analog-stereo toggle", + "restart-interval": 1, + "tooltip": false } }