Skip to content

Instantly share code, notes, and snippets.

@andrescera
Last active February 9, 2026 00:21
Show Gist options
  • Select an option

  • Save andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e to your computer and use it in GitHub Desktop.

Select an option

Save andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e to your computer and use it in GitHub Desktop.
brightness-ddc-hyprland: Multi-monitor DDC/CI brightness control for Arch Linux with Waybar

DDC/CI Multi-Monitor Brightness Control for Arch Linux

Robust brightness control for external monitors using DDC/CI on Hyprland with Waybar integration and Omarchy menu support.

Quick Install

curl -sL https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw/install.sh | bash

The installer will auto-detect your I2C buses, download all scripts, configure Hyprland keybindings and autostart, set up the Omarchy menu extension, and check i2c group permissions.

Features

  • Multi-monitor sync - Both monitors always stay at the same brightness level
  • Fast and responsive - Debounced hardware writes (150ms), instant UI feedback via cached state
  • Waybar integration - Visual indicator with scroll-to-adjust and percentage display
  • Interactive TUI menu - Click for detailed control with presets
  • Omarchy menu integration - Brightness option in Setup menu via extensions
  • Keyboard shortcuts - Standard brightness keys + Alt+F1/F2 alternatives
  • Race-condition safe - Global flock for rapid key presses
  • OSD notifications - SwayOSD overlay on brightness changes
  • Startup init - Automatically caches hardware brightness on login

Requirements

pacman -S ddcutil jq swayosd

Your monitors must support DDC/CI (most modern monitors do). Run ddcutil detect to verify.

Files Overview

File Location Purpose
brightness ~/bin/brightness Main brightness control script
monitor-brightness ~/bin/monitor-brightness Simple per-monitor brightness (legacy/utility)
brightness.sh ~/.config/waybar/scripts/ Waybar status indicator
brightness-menu.sh ~/.config/waybar/scripts/ Interactive TUI brightness menu
init-brightness.sh ~/.config/hypr/scripts/ Startup brightness cache init
omarchy-menu-extension.sh ~/.config/omarchy/extensions/menu.sh Omarchy menu brightness entry

Installation

1. Main brightness script

mkdir -p ~/bin
curl -o ~/bin/brightness https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw/brightness
chmod +x ~/bin/brightness

# Edit BUS_1 and BUS_2 to match your I2C buses
# Run `ddcutil detect` to find your bus numbers

2. Per-monitor brightness (optional utility)

curl -o ~/bin/monitor-brightness https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw/monitor-brightness
chmod +x ~/bin/monitor-brightness

3. Waybar scripts

mkdir -p ~/.config/waybar/scripts

curl -o ~/.config/waybar/scripts/brightness.sh https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw/brightness.sh
curl -o ~/.config/waybar/scripts/brightness-menu.sh https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw/brightness-menu.sh

chmod +x ~/.config/waybar/scripts/brightness.sh
chmod +x ~/.config/waybar/scripts/brightness-menu.sh

4. Startup init script

mkdir -p ~/.config/hypr/scripts
curl -o ~/.config/hypr/scripts/init-brightness.sh https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw/init-brightness.sh
chmod +x ~/.config/hypr/scripts/init-brightness.sh

Add to ~/.config/hypr/autostart.conf:

# Initialize monitor brightness cache
exec-once = ~/.config/hypr/scripts/init-brightness.sh

5. Waybar config

Add to ~/.config/waybar/config.jsonc in modules-right:

"custom/brightness": {
  "format": "{}",
  "exec": "~/.config/waybar/scripts/brightness.sh",
  "return-type": "json",
  "interval": 2,
  "signal": 9,
  "on-click": "xdg-terminal-exec --app-id=brightness-menu -e ~/.config/waybar/scripts/brightness-menu.sh",
  "on-scroll-up": "~/bin/brightness up-all && pkill -RTMIN+9 waybar",
  "on-scroll-down": "~/bin/brightness down-all && pkill -RTMIN+9 waybar",
  "tooltip": true
}

Add to ~/.config/waybar/style.css:

#custom-brightness {
  font-size: 12px;
  margin: 0 8px;
  padding: 0 4px;
}

#custom-brightness:hover {
  background-color: rgba(255, 255, 255, 0.1);
  border-radius: 3px;
}

Then restart Waybar: omarchy-restart-waybar

6. Hyprland keybindings

Add to ~/.config/hypr/bindings.conf:

# Unbind default Omarchy brightness bindings (for laptop backlight)
unbind = , XF86MonBrightnessUp
unbind = , XF86MonBrightnessDown
unbind = ALT, XF86MonBrightnessUp
unbind = ALT, XF86MonBrightnessDown

# Bind to our DDC monitor brightness control (ALL monitors)
bindel = , XF86MonBrightnessUp, exec, ~/bin/brightness up-all
bindel = , XF86MonBrightnessDown, exec, ~/bin/brightness down-all

# Alt+F1/F2 alternative bindings (ALL monitors)
bindel = ALT, F2, exec, ~/bin/brightness up-all
bindel = ALT, F1, exec, ~/bin/brightness down-all

7. Omarchy menu integration (optional)

To add a Brightness entry to the Omarchy Setup menu:

mkdir -p ~/.config/omarchy/extensions
curl -o ~/.config/omarchy/extensions/menu.sh https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw/omarchy-menu-extension.sh

This adds a Brightness option under Omarchy Menu > Setup that opens the interactive TUI.

Usage

brightness up              # Increase all monitors by 10%
brightness up-all          # Same as above (alias)
brightness down            # Decrease all monitors by 10%
brightness down-all        # Same as above (alias)
brightness set 50          # Set all monitors to 50%
brightness set-all 50      # Same as above (alias)
brightness status          # Show state + hardware values for all monitors
brightness init            # Read hardware and cache current brightness
brightness sync            # Sync both monitors to cached value

Waybar: Scroll to adjust, click to open the interactive menu.

Configuration

I2C Bus Numbers

The main brightness script uses hardcoded I2C bus numbers for speed. Find yours with:

ddcutil detect

Then update BUS_1 and BUS_2 at the top of ~/bin/brightness.

Monitor Mapping (waybar indicator)

The brightness.sh waybar script uses a monitor map for state file lookups. Update the monitors associative array if your monitor names differ:

local -A monitors=(["DP-2"]="1" ["DP-3"]="2")

Troubleshooting

# Monitors not detected
ddcutil detect

# Permission issues
sudo usermod -aG i2c $USER
# Then log out and back in

# State out of sync with hardware
brightness sync

# Clear all cached state
rm -rf /run/user/$(id -u)/brightness/

# Check current hardware values directly
ddcutil --bus 4 getvcp 10
ddcutil --bus 5 getvcp 10

License

MIT

#!/bin/bash
# Brightness control for external DDC/CI monitors
# Both monitors always sync to the same level
set -euo pipefail
readonly STEP=10
readonly STATE_DIR="/run/user/$(id -u)/brightness"
readonly LOCK_FILE="$STATE_DIR/global.lock"
readonly STATE_FILE="$STATE_DIR/brightness.state"
readonly APPLY_PID_FILE="$STATE_DIR/apply.pid"
# I2C bus numbers for monitors (from ddcutil detect)
readonly BUS_1=4
readonly BUS_2=5
mkdir -p "$STATE_DIR"
# Get brightness from first monitor
get_hw_brightness() {
ddcutil --bus "$BUS_1" --terse getvcp 10 2>/dev/null | awk '{print $4}' || echo "50"
}
# Get current brightness (cached or hardware)
get_brightness() {
if [[ -f "$STATE_FILE" ]]; then
cat "$STATE_FILE"
else
get_hw_brightness
fi
}
# Apply to both monitors sequentially (avoids i2c bus contention)
apply_to_hardware() {
local value="$1"
ddcutil --bus "$BUS_1" --noverify --sleep-multiplier 0.1 setvcp 10 "$value" &>/dev/null || true
ddcutil --bus "$BUS_2" --noverify --sleep-multiplier 0.1 setvcp 10 "$value" &>/dev/null || true
}
# Show OSD
show_osd() {
local percent="$1"
local monitor
monitor=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused == true).name' 2>/dev/null || echo "DP-2")
pkill -f "swayosd-client" 2>/dev/null || true
swayosd-client --monitor "$monitor" --custom-icon display-brightness \
--custom-progress-text "Brightness: ${percent}%" \
--custom-progress "$(awk "BEGIN {printf \"%.2f\", $percent/100}")" 2>/dev/null &
}
# Kill any pending apply process
kill_pending_apply() {
if [[ -f "$APPLY_PID_FILE" ]]; then
local pid
pid=$(cat "$APPLY_PID_FILE" 2>/dev/null) || return 0
kill "$pid" 2>/dev/null || true
rm -f "$APPLY_PID_FILE"
fi
}
# Change brightness
change_brightness() {
local action="$1"
exec 200>"$LOCK_FILE"
flock 200
local current new
current=$(get_brightness)
case "$action" in
up) new=$((current + STEP)); [[ $new -gt 100 ]] && new=100 ;;
down) new=$((current - STEP)); [[ $new -lt 0 ]] && new=0 ;;
esac
echo "$new" > "$STATE_FILE"
show_osd "$new"
pkill -RTMIN+9 waybar 2>/dev/null || true
# Kill any pending apply, start new one
kill_pending_apply
(
sleep 0.15
# Read final value
local final
final=$(cat "$STATE_FILE" 2>/dev/null) || final=$new
apply_to_hardware "$final"
rm -f "$APPLY_PID_FILE"
) &
echo $! > "$APPLY_PID_FILE"
flock -u 200
echo "$new"
}
# Set specific value
set_brightness() {
local value="$1"
[[ $value -lt 0 ]] && value=0
[[ $value -gt 100 ]] && value=100
exec 200>"$LOCK_FILE"
flock 200
echo "$value" > "$STATE_FILE"
show_osd "$value"
pkill -RTMIN+9 waybar 2>/dev/null || true
apply_to_hardware "$value"
flock -u 200
echo "Set brightness to $value%"
}
# Status
show_status() {
echo "State: $(cat "$STATE_FILE" 2>/dev/null || echo 'none')%"
echo "DP-2: $(ddcutil --bus $BUS_1 --terse getvcp 10 2>/dev/null | awk '{print $4}')%"
echo "DP-3: $(ddcutil --bus $BUS_2 --terse getvcp 10 2>/dev/null | awk '{print $4}')%"
}
# Init
init_brightness() {
local val
val=$(get_hw_brightness)
echo "$val" > "$STATE_FILE"
echo "Initialized to $val%"
}
# Sync monitors
sync_monitors() {
local val
val=$(cat "$STATE_FILE" 2>/dev/null) || val=$(get_hw_brightness)
echo "$val" > "$STATE_FILE"
apply_to_hardware "$val"
echo "Synced to $val%"
}
case "${1:-}" in
up|up-all) change_brightness up ;;
down|down-all) change_brightness down ;;
get) get_brightness ;;
set|set-all) [[ -z "${2:-}" ]] && { echo "Usage: $0 set VALUE" >&2; exit 1; }; set_brightness "$2" ;;
status) show_status ;;
init) init_brightness ;;
sync) sync_monitors ;;
*) echo "Usage: $0 {up|down|get|set VALUE|status|init|sync}"; exit 1 ;;
esac
#!/bin/bash
# Interactive brightness control menu - Controls ALL monitors
clear
# Get all monitor brightnesses
get_all_brightness() {
local -A brightnesses
while IFS=: read -r monitor value; do
monitor=$(echo "$monitor" | xargs)
value=$(echo "$value" | grep -oP '\d+' || echo "50")
brightnesses["$monitor"]="$value"
done < <(~/bin/brightness status 2>/dev/null | grep -E "DP-")
# Return average for display
local total=0
local count=0
for mon in "${!brightnesses[@]}"; do
total=$((total + brightnesses[$mon]))
count=$((count + 1))
done
if [[ $count -gt 0 ]]; then
echo $((total / count))
else
echo 50
fi
}
# Color codes
BOLD='\033[1m'
CYAN='\033[36m'
GREEN='\033[32m'
YELLOW='\033[33m'
MAGENTA='\033[35m'
RESET='\033[0m'
# Draw a visual brightness bar
draw_bar() {
local value=$1
local width=50
local filled=$((value * width / 100))
local empty=$((width - filled))
echo -n "["
for ((i=0; i<filled; i++)); do echo -n ""; done
for ((i=0; i<empty; i++)); do echo -n ""; done
echo "]"
}
# Main menu
while true; do
clear
CURRENT=$(get_all_brightness)
echo -e "${BOLD}${CYAN}╔════════════════════════════════════════════════╗${RESET}"
echo -e "${BOLD}${CYAN}║ Multi-Monitor Brightness Control ║${RESET}"
echo -e "${BOLD}${CYAN}╚════════════════════════════════════════════════╝${RESET}\n"
echo -e "${MAGENTA}${BOLD}📺 Controlling: ALL MONITORS${RESET}"
echo -e "Average brightness: ${YELLOW}${CURRENT}%${RESET}\n"
draw_bar "$CURRENT"
echo ""
# Show individual monitor status
echo -e "\n${BOLD}Individual Status:${RESET}"
~/bin/brightness status | grep -E "DP-" | while IFS=: read -r mon val; do
echo -e " ${GREEN}${mon}${RESET}: ${val}"
done
echo -e "\n${BOLD}Quick Presets (ALL monitors):${RESET}"
echo " [1] 0% (Off)"
echo " [2] 25% (Low)"
echo " [3] 50% (Medium)"
echo " [4] 75% (High)"
echo " [5] 100% (Maximum)"
echo -e "\n${BOLD}Adjustments (ALL monitors):${RESET}"
echo " [+] Increase by 10%"
echo " [-] Decrease by 10%"
echo " [c] Custom value (0-100)"
echo -e "\n${BOLD}Other:${RESET}"
echo " [r] Refresh status"
echo " [q] Quit"
echo -ne "\n${BOLD}${CYAN}Choose an option:${RESET} "
read -n 1 choice
echo ""
case "$choice" in
1)
~/bin/brightness set-all 0 >/dev/null 2>&1
;;
2)
~/bin/brightness set-all 25 >/dev/null 2>&1
;;
3)
~/bin/brightness set-all 50 >/dev/null 2>&1
;;
4)
~/bin/brightness set-all 75 >/dev/null 2>&1
;;
5)
~/bin/brightness set-all 100 >/dev/null 2>&1
;;
+|=)
~/bin/brightness up-all >/dev/null 2>&1
;;
-)
~/bin/brightness down-all >/dev/null 2>&1
;;
c|C)
echo -ne "\n${CYAN}Enter brightness value (0-100) for ALL monitors:${RESET} "
read -r custom_value
if [[ "$custom_value" =~ ^[0-9]+$ ]] && [[ $custom_value -ge 0 ]] && [[ $custom_value -le 100 ]]; then
~/bin/brightness set-all "$custom_value" >/dev/null 2>&1
else
echo -e "${YELLOW}Invalid value. Press Enter to continue...${RESET}"
read
fi
;;
r|R)
# Just refresh by looping
;;
q|Q)
clear
exit 0
;;
*)
echo -e "${YELLOW}Invalid option. Press Enter to continue...${RESET}"
read
;;
esac
# Small delay to show the change
sleep 0.3
done
#!/bin/bash
# Waybar brightness indicator for DDC monitors - Shows average across all monitors
STATE_DIR="/run/user/$(id -u)/brightness"
# Get average brightness from all monitors
get_average_brightness() {
local total=0
local count=0
local -A monitors=(["DP-2"]="1" ["DP-3"]="2")
for monitor in "${!monitors[@]}"; do
local state_file="$STATE_DIR/${monitor}.state"
if [[ -f "$state_file" ]]; then
# Use cached state if recent
local state_age=$(( $(date +%s) - $(stat -c %Y "$state_file" 2>/dev/null || echo 0) ))
if [[ $state_age -lt 3 ]]; then
local val=$(cat "$state_file")
total=$((total + val))
count=$((count + 1))
fi
fi
done
# If no cached data, query from hardware
if [[ $count -eq 0 ]]; then
while IFS=: read -r mon val; do
val=$(echo "$val" | grep -oP '\d+' || echo "0")
if [[ $val -gt 0 ]]; then
total=$((total + val))
count=$((count + 1))
fi
done < <(~/bin/brightness status 2>/dev/null | grep -E "DP-")
fi
if [[ $count -gt 0 ]]; then
echo $((total / count))
else
echo 50
fi
}
BRIGHTNESS=$(get_average_brightness)
# Choose icon based on brightness level
if [[ $BRIGHTNESS -ge 80 ]]; then
ICON="󰃠" # High brightness
elif [[ $BRIGHTNESS -ge 40 ]]; then
ICON="󰃟" # Medium brightness
elif [[ $BRIGHTNESS -gt 0 ]]; then
ICON="󰃞" # Low brightness
else
ICON="󰃜" # Off/very low
fi
# Build tooltip with helpful info
TOOLTIP="💡 Average Brightness: ${BRIGHTNESS}%\n"
TOOLTIP+="📺 Controls: ALL MONITORS\n\n"
TOOLTIP+="🖱️ Scroll to adjust all (±10%)\n"
TOOLTIP+="🖱️ Click for detailed menu"
# Output JSON for Waybar
echo "{\"text\":\"${ICON} ${BRIGHTNESS}%\",\"tooltip\":\"${TOOLTIP}\",\"class\":\"brightness\",\"percentage\":${BRIGHTNESS}}"
#!/bin/bash
# Initialize monitor brightness cache at startup
sleep 2 # Wait for monitors to be ready
~/bin/brightness init
#!/bin/bash
# Install DDC/CI brightness control for Hyprland + Waybar + Omarchy
# Usage: curl -sL <raw-url>/install.sh | bash
set -euo pipefail
GIST="https://gist.githubusercontent.com/andrescera/d82b815d3ce7eed0feeb7b18f0c4d25e/raw"
BOLD='\033[1m'
CYAN='\033[36m'
GREEN='\033[32m'
YELLOW='\033[33m'
RED='\033[31m'
RESET='\033[0m'
info() { echo -e "${CYAN}::${RESET} $1"; }
ok() { echo -e "${GREEN}ok${RESET} $1"; }
warn() { echo -e "${YELLOW}!!${RESET} $1"; }
err() { echo -e "${RED}!!${RESET} $1"; }
fetch() {
local url="$1" dest="$2"
curl -fsSL "$url" -o "$dest"
}
# ── Preflight ──────────────────────────────────────────────
echo -e "\n${BOLD}${CYAN}DDC/CI Brightness Control — Installer${RESET}\n"
# Check dependencies
missing=()
for cmd in ddcutil jq swayosd-client hyprctl curl; do
command -v "$cmd" &>/dev/null || missing+=("$cmd")
done
if [[ ${#missing[@]} -gt 0 ]]; then
warn "Missing dependencies: ${missing[*]}"
read -rp "Install with pacman? [Y/n] " ans
if [[ "${ans,,}" != "n" ]]; then
# Map command names to package names
pkgs=()
for cmd in "${missing[@]}"; do
case "$cmd" in
swayosd-client) pkgs+=("swayosd-git") ;;
hyprctl) pkgs+=("hyprland") ;;
*) pkgs+=("$cmd") ;;
esac
done
sudo pacman -S --needed --noconfirm "${pkgs[@]}"
else
err "Cannot continue without dependencies"
exit 1
fi
fi
# ── Detect I2C buses ──────────────────────────────────────
info "Detecting monitors via ddcutil..."
detect_output=$(ddcutil detect 2>/dev/null || true)
buses=()
while IFS= read -r line; do
bus=$(echo "$line" | grep -oP '/dev/i2c-\K\d+')
[[ -n "$bus" ]] && buses+=("$bus")
done < <(echo "$detect_output" | grep "I2C bus:")
if [[ ${#buses[@]} -lt 2 ]]; then
warn "Found ${#buses[@]} DDC monitor(s). This setup expects 2."
warn "You may need to manually edit ~/bin/brightness after install."
if [[ ${#buses[@]} -eq 0 ]]; then
buses=(4 5)
warn "Using default buses (4, 5) as placeholders."
elif [[ ${#buses[@]} -eq 1 ]]; then
buses+=("${buses[0]}")
warn "Using bus ${buses[0]} for both as placeholder."
fi
fi
BUS_1="${buses[0]}"
BUS_2="${buses[1]}"
echo -e " Bus 1: ${GREEN}${BUS_1}${RESET}"
echo -e " Bus 2: ${GREEN}${BUS_2}${RESET}"
# ── Download scripts ──────────────────────────────────────
info "Downloading scripts..."
mkdir -p ~/bin
mkdir -p ~/.config/waybar/scripts
mkdir -p ~/.config/hypr/scripts
mkdir -p ~/.config/omarchy/extensions
fetch "$GIST/brightness" ~/bin/brightness
fetch "$GIST/monitor-brightness" ~/bin/monitor-brightness
fetch "$GIST/brightness.sh" ~/.config/waybar/scripts/brightness.sh
fetch "$GIST/brightness-menu.sh" ~/.config/waybar/scripts/brightness-menu.sh
fetch "$GIST/init-brightness.sh" ~/.config/hypr/scripts/init-brightness.sh
chmod +x ~/bin/brightness
chmod +x ~/bin/monitor-brightness
chmod +x ~/.config/waybar/scripts/brightness.sh
chmod +x ~/.config/waybar/scripts/brightness-menu.sh
chmod +x ~/.config/hypr/scripts/init-brightness.sh
ok "Scripts installed"
# ── Patch I2C buses ───────────────────────────────────────
info "Setting I2C buses in ~/bin/brightness..."
sed -i "s/^readonly BUS_1=.*/readonly BUS_1=${BUS_1}/" ~/bin/brightness
sed -i "s/^readonly BUS_2=.*/readonly BUS_2=${BUS_2}/" ~/bin/brightness
ok "Buses set to ${BUS_1} and ${BUS_2}"
# ── Hyprland autostart ───────────────────────────────────
AUTOSTART="$HOME/.config/hypr/autostart.conf"
if [[ -f "$AUTOSTART" ]]; then
if ! grep -q "init-brightness" "$AUTOSTART"; then
info "Adding init-brightness to autostart..."
echo -e "\n# Initialize monitor brightness cache\nexec-once = ~/.config/hypr/scripts/init-brightness.sh" >> "$AUTOSTART"
ok "Added to autostart.conf"
else
ok "autostart.conf already has init-brightness"
fi
else
warn "No autostart.conf found — add this line manually:"
echo " exec-once = ~/.config/hypr/scripts/init-brightness.sh"
fi
# ── Hyprland keybindings ─────────────────────────────────
BINDINGS="$HOME/.config/hypr/bindings.conf"
if [[ -f "$BINDINGS" ]]; then
if ! grep -q "bin/brightness" "$BINDINGS"; then
info "Adding brightness keybindings..."
cat >> "$BINDINGS" << 'KEYS'
# ============================================
# DisplayPort Monitor Brightness Control
# ============================================
# Unbind default Omarchy brightness bindings (for laptop backlight)
unbind = , XF86MonBrightnessUp
unbind = , XF86MonBrightnessDown
unbind = ALT, XF86MonBrightnessUp
unbind = ALT, XF86MonBrightnessDown
# Bind to DDC monitor brightness control (ALL monitors)
bindel = , XF86MonBrightnessUp, exec, ~/bin/brightness up-all
bindel = , XF86MonBrightnessDown, exec, ~/bin/brightness down-all
# Alt+F1/F2 alternative bindings (ALL monitors)
bindel = ALT, F2, exec, ~/bin/brightness up-all
bindel = ALT, F1, exec, ~/bin/brightness down-all
KEYS
ok "Added keybindings"
else
ok "Keybindings already configured"
fi
else
warn "No bindings.conf found — see README for keybinding setup"
fi
# ── Waybar module ─────────────────────────────────────────
WAYBAR_CFG="$HOME/.config/waybar/config.jsonc"
if [[ -f "$WAYBAR_CFG" ]] && ! grep -q "custom/brightness" "$WAYBAR_CFG"; then
warn "Waybar config exists but has no brightness module."
warn "Add this to modules-right and the module definition manually:"
echo ""
echo ' "custom/brightness": {'
echo ' "format": "{}",'
echo ' "exec": "~/.config/waybar/scripts/brightness.sh",'
echo ' "return-type": "json",'
echo ' "interval": 2,'
echo ' "signal": 9,'
echo ' "on-click": "xdg-terminal-exec --app-id=brightness-menu -e ~/.config/waybar/scripts/brightness-menu.sh",'
echo ' "on-scroll-up": "~/bin/brightness up-all && pkill -RTMIN+9 waybar",'
echo ' "on-scroll-down": "~/bin/brightness down-all && pkill -RTMIN+9 waybar",'
echo ' "tooltip": true'
echo ' }'
echo ""
warn "And add to style.css:"
echo ""
echo ' #custom-brightness { font-size: 12px; margin: 0 8px; padding: 0 4px; }'
echo ' #custom-brightness:hover { background-color: rgba(255,255,255,0.1); border-radius: 3px; }'
echo ""
else
ok "Waybar brightness module already present"
fi
# ── Omarchy menu extension ───────────────────────────────
MENU_EXT="$HOME/.config/omarchy/extensions/menu.sh"
if [[ -f "$MENU_EXT" ]]; then
if ! grep -q "Brightness" "$MENU_EXT"; then
warn "Menu extension exists but has no brightness entry."
warn "You may want to merge manually. See omarchy-menu-extension.sh in the gist."
else
ok "Omarchy menu extension already has brightness"
fi
else
info "Installing Omarchy menu extension..."
fetch "$GIST/omarchy-menu-extension.sh" "$MENU_EXT"
ok "Brightness added to Omarchy Setup menu"
fi
# ── i2c permissions ───────────────────────────────────────
if ! groups | grep -q i2c; then
warn "Your user is not in the 'i2c' group."
read -rp "Add now? (requires sudo, re-login needed) [Y/n] " ans
if [[ "${ans,,}" != "n" ]]; then
sudo usermod -aG i2c "$USER"
warn "Added to i2c group — log out and back in for it to take effect"
fi
else
ok "User is in i2c group"
fi
# ── Done ──────────────────────────────────────────────────
echo -e "\n${BOLD}${GREEN}Installation complete!${RESET}\n"
echo "Next steps:"
echo " 1. If Waybar module was not auto-configured, add it manually (see above)"
echo " 2. Restart Waybar: omarchy-restart-waybar"
echo " 3. Test: ~/bin/brightness status"
echo ""
#!/bin/bash
# Brightness control for DisplayPort monitors via DDC/CI
# Automatically controls the focused monitor
STEP=10
# Get focused monitor name from Hyprland (e.g., DP-2, DP-3)
FOCUSED=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused == true).name')
# Dynamically map Hyprland output to ddcutil display by matching DRM connector
get_display_for_output() {
local output="$1" # e.g., "DP-2"
local drm_connector="card1-${output}"
local display_num=$(ddcutil detect 2>/dev/null | grep -B5 "$drm_connector" | grep "^Display" | awk '{print $2}')
echo "$display_num"
}
DISPLAY=$(get_display_for_output "$FOCUSED")
# Fallback to display 1 if detection fails
if [ -z "$DISPLAY" ]; then
DISPLAY=1
fi
case "$1" in
up|raise)
ddcutil --display "$DISPLAY" setvcp 10 + "$STEP" 2>/dev/null
;;
down|lower)
ddcutil --display "$DISPLAY" setvcp 10 - "$STEP" 2>/dev/null
;;
get)
ddcutil --display "$DISPLAY" getvcp 10 2>/dev/null
;;
get-display)
echo "Focused: $FOCUSED → Display: $DISPLAY"
;;
*)
echo "Usage: $0 {up|raise|down|lower|get|get-display}"
exit 1
;;
esac
#!/bin/bash
# Override setup menu to add Brightness option
show_setup_menu() {
local options=" Audio\n Wifi\n󰂯 Bluetooth\n󰍹 Brightness\n󱐋 Power Profile\n System Sleep\n󰍹 Monitors"
[ -f ~/.config/hypr/bindings.conf ] && options="$options\n Keybindings"
[ -f ~/.config/hypr/input.conf ] && options="$options\n Input"
options="$options\n󰱔 DNS\n Security\n Config"
case $(menu "Setup" "$options") in
*Audio*) omarchy-launch-audio ;;
*Wifi*) omarchy-launch-wifi ;;
*Bluetooth*) omarchy-launch-bluetooth ;;
*Brightness*) terminal bash -c ~/.config/waybar/scripts/brightness-menu.sh ;;
*Power*) show_setup_power_menu ;;
*System*) show_setup_system_menu ;;
*Monitors*) open_in_editor ~/.config/hypr/monitors.conf ;;
*Keybindings*) open_in_editor ~/.config/hypr/bindings.conf ;;
*Input*) open_in_editor ~/.config/hypr/input.conf ;;
*DNS*) present_terminal omarchy-setup-dns ;;
*Security*) show_setup_security_menu ;;
*Config*) show_setup_config_menu ;;
*) show_main_menu ;;
esac
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment