diff --git a/.config/hypr/config/keybinds.conf b/.config/hypr/config/keybinds.conf index 8620a23..dc59347 100644 --- a/.config/hypr/config/keybinds.conf +++ b/.config/hypr/config/keybinds.conf @@ -137,7 +137,7 @@ bindd = $mainMod ALT SHIFT, F1, Move active window to s~/.config/hypr/scripts/pe # ======= Screenshot ======= # Screenshot a window -bind = $mainMod, PRINT, exec, hyprshot -m window +bind = $mainMod, PRINT, exec, hyprshot -m window # Screenshot a monitor bind = , PRINT, exec, hyprshot -m output # Screenshot a region @@ -169,10 +169,7 @@ bind = $mainMod SHIFT, W, exec, killall -SIGUSR2 waybar # restart # Toggle Monitor Flip bind = $mainMod, F7, exec, ~/.config/hypr/scripts/rotate_current_screen.sh -# One-key flip: externals on/off bind = $mainMod, F8, exec, ~/.config/hypr/scripts/monitor-toggle.sh toggle-externals - -# Explicit profiles -bind = $mainMod, F8, exec, ~/.config/hypr/scripts/monitor-toggle.sh laptop -bind = $mainMod, F9, exec, ~/.config/hypr/scripts/monitor-toggle.sh dual +# bind = $mainMod, F8, exec, ~/.config/hypr/scripts/monitor-toggle.sh laptop +bind = $mainMod, F9, exec, ~/.config/hypr/scripts/monitor-toggle.sh dual bind = $mainMod, F10, exec, ~/.config/hypr/scripts/monitor-toggle.sh triple \ No newline at end of file diff --git a/.config/hypr/config/monitor.conf b/.config/hypr/config/monitor.conf index 438f994..87dab07 100644 --- a/.config/hypr/config/monitor.conf +++ b/.config/hypr/config/monitor.conf @@ -4,13 +4,11 @@ # Monitor wiki https://wiki.hyprland.org/0.45.0/Configuring/Monitors/ -monitor = desc:BOE NE156FHM-NX6, highres@highrr, 0x0, 1, vrr, 1 -# monitor = DP-2, highres@highrr, auto-left, 1, vrr, 0 +monitor = eDP-2, highres@highrr, 0x0, 1, vrr, 1 -monitor = HDMI-A-1, highres@highrr, auto-right, 1, transform, 1 -monitor = desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1 +#monitor = HDMI-A-1, highres@highrr, auto-right, 1, transform, 1 +monitor = desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1 monitor = desc:Samsung Electric Company S24F350 H4ZK111233, highres@highrr, auto-right, 1, transform, 1 -#monitor = desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1 # monitor = HDMI-A-1, highres@highrr, auto-right, 1, vrr, 0 diff --git a/.config/hypr/config/variables.conf b/.config/hypr/config/variables.conf index e4fe6c3..893a327 100644 --- a/.config/hypr/config/variables.conf +++ b/.config/hypr/config/variables.conf @@ -55,7 +55,7 @@ misc { enable_swallow = true swallow_regex = ^(cachy-browser|firefox|nautilus|nemo|thunar|btrfs-assistant.)$ focus_on_activate = true - vrr = 2 + vrr = 0 # Hypridle cmatrix fix: session_lock_xray = true middle_click_paste = false diff --git a/.config/hypr/config/windowrules.conf b/.config/hypr/config/windowrules.conf index fd08973..2324e46 100644 --- a/.config/hypr/config/windowrules.conf +++ b/.config/hypr/config/windowrules.conf @@ -273,6 +273,15 @@ windowrule { match:title = ^(Citation Dialog)$ } +windowrule { + name = libreoffice-opaque + match:class = ^(libreoffice.*)$ + + # active, inactive, fullscreen (all forced to 1.0) + opacity = 1.0 override 1.0 override 1.0 override +} + + # gnome calendar windowrule { name = windowrule-cal @@ -303,4 +312,4 @@ windowrule { match:class = ^(org.gnome.Calculator)$ float = on center = on -} \ No newline at end of file +} diff --git a/.config/hypr/config/workspaces.conf b/.config/hypr/config/workspaces.conf index 3f071a3..eef968d 100644 --- a/.config/hypr/config/workspaces.conf +++ b/.config/hypr/config/workspaces.conf @@ -6,7 +6,9 @@ workspace = 1, monitor:eDP-2 workspace = 2, monitor:eDP-2 workspace = 3, monitor:eDP-2 workspace = 4, monitor:eDP-2 +workspace = 5, monitor:eDP-2 +workspace = 6, monitor:eDP-2 +workspace = 7, monitor:eDP-2 -workspace = 7, monitor:desc:Samsung Electric Company S24F350 H4ZR302705 workspace = 8, monitor:DP-2 workspace = 9, monitor:HDMI-A-1 diff --git a/.config/hypr/monitors.conf b/.config/hypr/monitors.conf new file mode 100644 index 0000000..b3d791b --- /dev/null +++ b/.config/hypr/monitors.conf @@ -0,0 +1,6 @@ +# Generated by nwg-displays on 2026-02-04 at 14:45:07. Do not edit manually. + +monitor=desc:BOE NE156FHM-NX6,1920x1080@144.0,0x0,1.0 +monitor=desc:Samsung Electric Company S24F350 H4ZK111233,1920x1080@60.0,1920x0,1.0 +monitor=desc:Samsung Electric Company S24F350 H4ZK111233,transform,1 +monitor=desc:Samsung Electric Company S24F350 H4ZR302705,1920x1080@60.0,3000x0,1.0 diff --git a/.config/hypr/scripts/monitor-toggle.sh b/.config/hypr/scripts/monitor-toggle.sh index a59d72a..aa13645 100755 --- a/.config/hypr/scripts/monitor-toggle.sh +++ b/.config/hypr/scripts/monitor-toggle.sh @@ -1,26 +1,25 @@ #!/usr/bin/env bash -# Quick display control for Hyprland -# Profiles: -# laptop -> keep eDP-2, disable all externals -# dual -> eDP-2 + primary external -# triple -> eDP-2 + both externals -# Toggles: -# toggle-externals -> if any external enabled => laptop; else => triple (or dual if only one preset) -# dpms-toggle-focused -> blank/unblank the currently focused monitor (does NOT change layout) - set -euo pipefail PRIMARY="eDP-2" +RIGHT_EXTERNAL_NAME="DP-2" +MIDDLE_EXTERNAL_NAME="HDMI-A-1" -# --- exact enable lines (copy from hyprland.conf) --- +# Exact enable lines (from your config) EXT1='desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1' EXT2='desc:Samsung Electric Company S24F350 H4ZK111233, highres@highrr, auto-right, 1, transform, 1' -# Optional fallback by connector (harmless if not present) -EXT_FALLBACK='HDMI-A-1, highres@highrr, auto-right, 1' -# Which external is the "main" one you prefer for Dual: +# In dual mode, which one do we prefer? DUAL_MAIN="$EXT1" +# Optional: restart Waybar after layout changes (0=off, 1=on) +RESTART_WAYBAR=1 + +# Dock/MST settle timing (tune if needed) +SETTLE_SECS=1.0 +DPMS_KICK_RETRIES=2 +DPMS_KICK_SLEEP=0.35 + notify() { if command -v notify-send >/dev/null 2>&1; then notify-send -a "Hyprland" "$1" "${2:-}" @@ -35,62 +34,182 @@ hypr() { notify "hyprctl failed" "$out" exit 1 fi + printf '%s' "$out" } -jqok(){ command -v jq >/dev/null; } -monjson(){ hyprctl -j monitors 2>/dev/null || true; } +hypr_batch() { + local out + if ! out="$(hyprctl --batch "$1" 2>&1)"; then + notify "hyprctl --batch failed" "$out" + exit 1 + fi + printf '%s' "$out" +} -any_external_enabled() { - if jqok; then - monjson | jq -e --arg P "$PRIMARY" '.[] | select(.name!=$P and (.disabled!=true))' >/dev/null - else - hyprctl monitors | awk -v P="$PRIMARY" '/^Monitor /{gsub(/^Monitor[ \t]+/, "", $0); split($0,a," "); if (a[1]!=P){print; exit 0}}' | grep -q . +restart_waybar() { + [ "${RESTART_WAYBAR:-0}" -eq 1 ] || return 0 + if pgrep -x waybar >/dev/null 2>&1; then + pkill -SIGUSR2 waybar >/dev/null 2>&1 || true fi } -disable_all_externals() { +jqok() { command -v jq >/dev/null 2>&1; } +monjson() { hyprctl -j monitors 2>/dev/null || true; } + +sleep_s() { python - </dev/null || sleep 1 +import time +time.sleep(float("$1")) +PY +} + +# --- monitor discovery helpers --- + +enabled_monitor_names() { if jqok; then - mapfile -t names < <(monjson | jq -r --arg P "$PRIMARY" '.[] | select(.name!=$P) | .name') + monjson | jq -r '.[] | select(.disabled != true) | .name' else - mapfile -t names < <(hyprctl monitors | awk -v P="$PRIMARY" '/^Monitor /{gsub(/^Monitor[ \t]+/, "", $0); split($0,a," "); if (a[1]!=P) print a[1]}') + # Fallback is weaker; jq is strongly recommended + hyprctl monitors | awk ' + /^Monitor /{name=$2} + /disabled: false/{print name} + ' fi - for n in "${names[@]:-}"; do - hypr keyword monitor "$n, disable" +} + +focused_monitor_name() { + if jqok; then + monjson | jq -r '.[] | select(.focused==true) | .name // empty' + else + hyprctl monitors | awk ' + /^Monitor /{name=$2} + /focused: yes/{print name; exit} + ' + fi +} + +external_enabled() { + local n + while IFS= read -r n; do + [ "$n" != "$PRIMARY" ] && return 0 + done < <(enabled_monitor_names) + return 1 +} + +# We don’t try to parse desc lines; we just treat them as “enable rules” +enable_rule() { + local rule="$1" + [ -n "$rule" ] && printf 'keyword monitor %s; ' "$rule" +} + +disable_by_name() { + local name="$1" + [ -n "$name" ] && printf 'keyword monitor %s, disable; ' "$name" +} + +# Disable all *currently enabled* externals by name (minimal churn) +batch_disable_enabled_externals() { + local batch="" n="" + while IFS= read -r n; do + [ "$n" = "$PRIMARY" ] && continue + batch+="$(disable_by_name "$n")" + done < <(enabled_monitor_names) + printf '%s' "$batch" +} + +# Dock settle wait: give MST/alt-mode time to re-enumerate before we apply rules / kick DPMS +dock_settle() { + sleep_s "$SETTLE_SECS" +} + +# DPMS kick for all enabled externals (works well for dock hotplug weirdness) +kick_externals() { + local names=() n="" + if jqok; then + mapfile -t names < <(monjson | jq -r --arg P "$PRIMARY" ' + .[] | select(.name != $P and (.disabled != true)) | .name + ') + else + mapfile -t names < <(enabled_monitor_names | awk -v P="$PRIMARY" '$0!=P') + fi + + [ "${#names[@]}" -eq 0 ] && return 0 + + for _ in $(seq 1 "$DPMS_KICK_RETRIES"); do + for n in "${names[@]}"; do hypr dispatch dpms off "$n" >/dev/null 2>&1 || true; done + sleep_s "$DPMS_KICK_SLEEP" + for n in "${names[@]}"; do hypr dispatch dpms on "$n" >/dev/null 2>&1 || true; done + sleep_s "$DPMS_KICK_SLEEP" done } -enable_line() { - local line="$1" - [ -n "$line" ] && hypr keyword monitor "$line" +enforce_triple_order() { + # Keep HDMI between the laptop panel and DP-2, with DP-2 always on the right. + enabled_monitor_names | grep -qx "$MIDDLE_EXTERNAL_NAME" || return 0 + enabled_monitor_names | grep -qx "$RIGHT_EXTERNAL_NAME" || return 0 + + local py=0 px=0 pspan=1920 mspan=1920 mx rx + if jqok; then + read -r px py pspan < <(monjson | jq -r --arg N "$PRIMARY" ' + .[] | select(.name == $N) | + (.transform // 0) as $t | + "\(.x // 0) \(.y // 0) \(if (($t % 2) == 1) then (.height // 1080) else (.width // 1920) end)" + ') + read -r mspan < <(monjson | jq -r --arg N "$MIDDLE_EXTERNAL_NAME" ' + .[] | select(.name == $N) | + (.transform // 0) as $t | + "\(if (($t % 2) == 1) then (.height // 1080) else (.width // 1920) end)" + ') + fi + + mx=$((px + pspan)) + rx=$((mx + mspan)) + + hypr keyword monitor "$MIDDLE_EXTERNAL_NAME, highres@highrr, ${mx}x${py}, 1, transform, 1" >/dev/null 2>&1 || true + hypr keyword monitor "$RIGHT_EXTERNAL_NAME, highres@highrr, ${rx}x${py}, 1" >/dev/null 2>&1 || true +} + +apply_profile() { + local label="$1" + local batch="$2" + dock_settle + [ -n "$batch" ] && hypr_batch "$batch" >/dev/null + dock_settle + kick_externals + notify "Profile: $label" "" + restart_waybar } profile_laptop() { - disable_all_externals - notify "Profile: Laptop-only" "$PRIMARY active, externals disabled" + # Minimal: disable only currently enabled externals + local batch="" + batch+="$(batch_disable_enabled_externals)" + apply_profile "Laptop-only" "$batch" } profile_dual() { - # Ensure only one external is on - disable_all_externals - enable_line "$DUAL_MAIN" - notify "Profile: Dual" "Enabled main external with preferred transform" + # Disable enabled externals, then enable preferred main external rule + local batch="" + batch+="$(batch_disable_enabled_externals)" + batch+="$(enable_rule "$DUAL_MAIN")" + apply_profile "Dual" "$batch" } profile_triple() { - # Ensure both externals are on (and only those two) - disable_all_externals - enable_line "$EXT2" # ext2 first so it appears in the middle - enable_line "$EXT1" - # You can drop EXT_FALLBACK if you don't actually need it: - # enable_line "$EXT_FALLBACK" - notify "Profile: Triple" "Enabled both externals" + # Disable enabled externals, then enable both rules + # Order: EXT2 first so it tends to appear “middle” with auto-right + local batch="" + batch+="$(batch_disable_enabled_externals)" + batch+="$(enable_rule "$EXT2")" + batch+="$(enable_rule "$EXT1")" + apply_profile "Triple" "$batch" + dock_settle + enforce_triple_order } toggle_externals() { - if any_external_enabled; then + if external_enabled; then profile_laptop else - # Prefer triple if we have two presets, else dual if [ -n "$EXT1" ] && [ -n "$EXT2" ]; then profile_triple else @@ -100,32 +219,38 @@ toggle_externals() { } dpms_toggle_focused() { - # Blank/unblank the currently focused output, layout unchanged local name - if jqok; then - name="$(monjson | jq -r '.[] | select(.focused==true) | .name // empty')" - else - name="$(hyprctl monitors | awk '$1=="Monitor"{gsub(/^Monitor[ \t]+/,""); split($0,a," "); name=a[1]} /focused: yes/{print name; exit}')" - fi + name="$(focused_monitor_name)" [ -z "$name" ] && { notify "No focused monitor" ""; exit 2; } - hypr dispatch dpms toggle "$name" + hypr dispatch dpms toggle "$name" >/dev/null notify "DPMS toggle" "$name" } +status() { + echo "Enabled monitors:" + enabled_monitor_names | sed 's/^/ - /' + echo + echo "Focused: $(focused_monitor_name || true)" +} + case "${1:-}" in - laptop) profile_laptop ;; - dual) profile_dual ;; - triple) profile_triple ;; - toggle-externals) toggle_externals ;; - dpms-toggle-focused) dpms_toggle_focused ;; + laptop) profile_laptop ;; + dual) profile_dual ;; + triple) profile_triple ;; + toggle-externals) toggle_externals ;; + dpms-toggle-focused) dpms_toggle_focused ;; + kick-externals) dock_settle; kick_externals; notify "Kicked externals (DPMS)" "" ;; + status) status ;; *) cat < (dual/triple) + $(basename "$0") dpms-toggle-focused # blank/unblank focused output (layout unchanged) + $(basename "$0") kick-externals # DPMS off/on for enabled externals + $(basename "$0") status # print enabled + focused EOF exit 2 ;; diff --git a/.config/hypr/scripts/record-or-screenshot.sh b/.config/hypr/scripts/record-or-screenshot.sh index 8cfc58c..af5a268 100755 --- a/.config/hypr/scripts/record-or-screenshot.sh +++ b/.config/hypr/scripts/record-or-screenshot.sh @@ -57,16 +57,26 @@ fi # Get current outputs dynamically OUTPUTS=$(hyprctl monitors | grep "Monitor" | awk '{print $2}' | paste -sd " " -) -OPTIONS=$( - cat </dev/null 2>&1 & +} + # Screenshot or record depending on selection case "$SELECTION" in "screenshot selection") @@ -99,6 +114,7 @@ case "$SELECTION" in IMG="${SCREENSHOT_DIR}/${TIMESTAMP}.png" grim -g "$(slurp)" "$IMG" || { notify-send "Error" "Failed to take screenshot"; exit 1; } [ -x "$(command -v wl-copy)" ] && wl-copy < "$IMG" + open_in_satty "$IMG" notify-send "Screenshot Taken" "$IMG" ;; @@ -114,6 +130,7 @@ case "$SELECTION" in montage "${TEMP_FILES[@]}" -tile x1 -geometry +0+0 "$IMG" rm "${TEMP_FILES[@]}" [ -x "$(command -v wl-copy)" ] && wl-copy < "$IMG" + open_in_satty "$IMG" notify-send "Screenshot Taken" "$IMG" else notify-send "Error" "Not enough outputs to montage" @@ -153,6 +170,7 @@ case "$SELECTION" in OUT=$(echo "$SELECTION" | awk '{print $2}') grim -c -o "$OUT" "$IMG" || { notify-send "Error" "Failed to screenshot $OUT"; exit 1; } [ -x "$(command -v wl-copy)" ] && wl-copy < "$IMG" + open_in_satty "$IMG" notify-send "Screenshot Taken" "$IMG" ;; diff --git a/.config/waybar/config.jsonc b/.config/waybar/config.jsonc index def526f..9cea439 100644 --- a/.config/waybar/config.jsonc +++ b/.config/waybar/config.jsonc @@ -4,7 +4,7 @@ // ------------------------------------------------------------------------- "layer": "top", - + "swap-icon-label": true, "position": "top", "height": 36, @@ -407,10 +407,11 @@ "menu": "on-click", "menu-file": "~/.config/waybar/modules/power_menu.xml", "menu-actions": { - "shutdown": "shutdown", - "reboot": "reboot", "suspend": "systemctl suspend", "hibernate": "systemctl hibernate", + "logout": "hyprctl dispatch exit", + "shutdown": "shutdown", + "reboot": "reboot", }, }, diff --git a/.config/waybar/modules/power_menu.xml b/.config/waybar/modules/power_menu.xml index 5ec4417..a280e97 100644 --- a/.config/waybar/modules/power_menu.xml +++ b/.config/waybar/modules/power_menu.xml @@ -13,7 +13,12 @@ Hibernate - + + + Logout + + + Shutdown @@ -28,4 +33,3 @@ - diff --git a/.config/waybar/modules/power_profiles_menu.xml b/.config/waybar/modules/power_profiles_menu.xml index 36c4fcc..9f949be 100644 --- a/.config/waybar/modules/power_profiles_menu.xml +++ b/.config/waybar/modules/power_profiles_menu.xml @@ -8,12 +8,12 @@ - balanced (current) + balanced - power-saver + power-saver (current) diff --git a/.config/waybar/style.css b/.config/waybar/style.css index 5a80c8d..d097df9 100644 --- a/.config/waybar/style.css +++ b/.config/waybar/style.css @@ -234,11 +234,12 @@ window#waybar.empty #window { } menu { - border-radius: 15px; - background: rgba(22, 19, 32, 0); + border-radius: 5px; + border: 0px; + background: rgba(22, 19, 32, 0.5); color: #b5e8e0; padding: 5px; } menuitem { - border-radius: 15px; + border-radius: 5px; }