autorandr and bash script to collect and setup monitor data for xrandr

Slug: xrandr

68421 characters 8626 words

#Why Use autorandr?

autorandr is a highly effective, Python-based utility specifically designed to automate and simplify the management of multiple display configurations on Linux systems. Its core strength lies in its ability to automatically detect which displays (like projectors, external monitors, or ultrawide screens) are currently connected to your system and then apply a pre-saved configuration (“profile”) that matches that specific hardware setup. This eliminates the need to manually execute commands with xrandr (a command-line tool for configuring display settings) every time you connect or disconnect a display.

This automation is particularly beneficial in your scenario as a professor using a Raspberry Pi 4B with openSUSE Tumbleweed. Imagine moving between different lecture halls, each potentially having a projector with a unique native resolution (1024x768, 1280x800, etc.), then returning to your office to connect to an ultrawide monitor (e.g., 3440x1440), and perhaps later connecting to a standard monitor in a departmental meeting room. Without autorandr, each transition would require manual reconfiguration. With autorandr, once you’ve saved a profile for each setup, the system adapts automatically upon connection. Furthermore, on a modern system like openSUSE Tumbleweed, autorandr integrates seamlessly with the systemd init system. This means it can leverage udev events (the system’s way of detecting hardware changes like plugging in a monitor) to trigger the profile switching automatically in the background, offering a truly hands-off experience after the initial setup.

Key Benefits Elaborated:

  • Automatic Detection & Application: autorandr intelligently identifies connected displays, often using their unique EDID information, and compares this against its library of saved profiles to find the best match, applying it instantly.
  • Ease of Use: The “save once, use anywhere” philosophy drastically reduces complexity. Instead of remembering complex xrandr commands or navigating display settings repeatedly, you perform the setup once per unique display combination and save it with a memorable name.
  • Flexibility: It excels at managing numerous distinct profiles. You can have profiles for single displays, dual displays in different arrangements (extended desktop, mirrored), displays with specific resolutions or refresh rates, covering virtually any common scenario you encounter.
  • Robustness: It gracefully handles situations where display EDID information might be missing, corrupt, or ambiguous – common issues with older projectors or certain adapters. Instead of failing outright or requiring manual intervention like raw xrandr might, autorandr can use other detected properties or fall back to a predefined default profile, ensuring you usually get a usable display state.

#Prerequisites (Raspberry Pi 4B Specific)

Before diving into autorandr installation, optimizing your Raspberry Pi 4B environment is crucial for smooth display operation, especially given its shared memory architecture:

  • GPU Memory (gpu_mem): The Raspberry Pi dynamically allocates system RAM between the CPU and the VideoCore GPU. Insufficient memory allocated to the GPU can lead to various graphical issues, such as visual glitches, screen tearing, an inability to drive displays at their native (especially high) resolutions (like 4K), or even completely blank screens. While the default allocation might be sufficient for basic desktop use, connecting multiple monitors or high-resolution displays often requires more. Check your current allocation and consider increasing it if you face issues. You can adjust this by editing the /boot/efi/extraconfig.txt file (the path might vary slightly depending on the exact Tumbleweed Pi image setup) and adding or modifying a line like gpu_mem=256 or gpu_mem=512 (allocating 256MB or 512MB respectively). A reboot is required for this change to be applied by the system.
  • Firmware & System Updates: The Raspberry Pi’s firmware and the Linux kernel’s graphics drivers (like V3D DRM) are continually updated to improve hardware compatibility, fix bugs, and enhance performance. These updates often include improved handling of display detection protocols like EDID. Keeping your openSUSE Tumbleweed system fully updated is the best way to ensure you have the latest fixes and broadest compatibility. Use the standard Tumbleweed update command, sudo zypper dup (which performs a full system upgrade). Regularly running this ensures you benefit from the latest improvements relevant to display handling.

#Installation on openSUSE Tumbleweed

You have several avenues to install autorandr on your system:

  1. Check Standard Repositories (Try First): OpenSUSE Tumbleweed might already include autorandr in its main repositories. You can check its availability and install it if found:
    # Check if the package exists
    zypper info autorandr
    # If available, install it
    sudo zypper refresh
    sudo zypper install autorandr

  2. Recommended Method (openSUSE Build Service - OBS): The autorandr author often maintains a more up-to-date version in a dedicated OBS repository. This is generally the preferred method if the package isn’t in the main repos or if you need the latest features/fixes:
    # Add the repository
    sudo zypper addrepo https://download.opensuse.org/repositories/home:phillipberndt/openSUSE_Tumbleweed/home:phillipberndt.repo
    # Refresh repository metadata
    sudo zypper refresh
    # Install autorandr from the new repo
    sudo zypper install autorandr

  3. Alternative Method (pip): You can install autorandr using Python’s package installer, pip. However, be aware that this method might install it only for the current user, might not integrate as seamlessly with system-wide services like systemd/udev, and might require manual handling of non-Python dependencies. It can also lead to conflicts if system packages also provide parts of the dependencies.
    # Ensure pip is installed, then install autorandr
    sudo zypper install python3-pip
    sudo pip install autorandr

  4. Alternative Method (From Source): For developers or those needing the absolute latest code, you can clone the autorandr Git repository and install it manually. This typically requires development tools like gcc and make, and you’ll need to manage dependencies yourself. Consult the README file in the repository for specific instructions.
    # Example (dependencies might vary)
    sudo zypper install git make python3-devel
    git clone https://github.com/phillipberndt/autorandr.git
    cd autorandr
    sudo make install

#Basic Configuration: Creating and Managing Profiles

The fundamental workflow for using autorandr revolves around capturing the state of your display setup for each unique configuration you use:

  1. Connect: Physically connect your Raspberry Pi to the specific combination of displays (e.g., a single classroom projector, your dual monitors at the office).
  2. Configure Manually (Once): Use your preferred method to arrange the displays exactly how you want them for this specific setup. This could be:
    • Desktop Environment Tools: Use the graphical display settings panel provided by your desktop environment (e.g., xfce4-display-settings in Xfce, the ‘Displays’ panel in GNOME Settings). Here you can typically enable/disable monitors, set resolutions, refresh rates, orientation, and define primary displays and relative positions in an extended desktop.
    • Manual xrandr Commands: For more fine-grained control or scripting, use xrandr directly in the terminal. For example: xrandr --output HDMI-1 --mode 1920x1080 --primary --output DisplayPort-1 --mode 2560x1440 --rotate left --right-of HDMI-1.
  3. Save Profile: Once the displays are configured correctly, save this entire state as an autorandr profile using a descriptive name. Choosing a consistent naming convention can be helpful, e.g., location-displaytype-resolution or setup_description.
    # Example for a specific lecture hall projector
    autorandr --save lecturehallB-projector-1280x800

    # Example for your office ultrawide setup
    autorandr --save office-ultrawide-3440x1440

    # Example for a standard 1080p monitor used for testing
    autorandr --save lab-monitor-1080p

    It’s a good idea to back up this directory periodically, especially if you have complex or finely-tuned profiles.

  4. List Profiles: To review the profiles you have saved:
    autorandr --list

#Applying Configurations

  • Manual Application: You can manually trigger autorandr to detect the currently connected displays and apply the best-matching profile from your saved library. This is useful for testing or if automation isn’t set up.
    autorandr --change

    Behind the scenes, autorandr --change performs several steps: it detects all connected displays and their properties (like EDID, which is data that allows a display to communicate its capabilities to the graphics card), compares this information against the data stored in each of your saved profiles, calculates a “match score” for each profile based on how well it fits the current hardware, and then automatically executes the xrandr commands stored within the highest-scoring profile (if the score exceeds a certain threshold). If no profile matches well enough, it might load the designated default profile or leave the configuration unchanged, depending on your setup.

For the most convenient experience, configure autorandr to react automatically whenever you connect or disconnect a display:

  • Using systemd Service: This is generally the most robust and manageable method on modern Linux systems like openSUSE Tumbleweed. The autorandr.service unit, when enabled, typically integrates with udev to monitor for display-related hardware events (specifically from the Direct Rendering Manager or DRM subsystem). Upon detecting such an event (e.g., an HDMI cable being plugged in), systemd activates the service, which in turn usually runs autorandr --change to apply the appropriate profile.
    # Enable the service to start on boot and start it immediately
    sudo systemctl enable --now autorandr.service
    # You can check its status later with: systemctl status autorandr.service

  • Using udev Rules: autorandr packages often include udev rules (e.g., in /lib/udev/rules.d/) that directly trigger autorandr --change when specific kernel events related to display hardware occur. While this works, managing services through systemd often provides better control, logging, and dependency management. If you installed manually or suspect the rules aren’t active, you might need to reload them:
    sudo udevadm control --reload-rules && sudo udevadm trigger

    With either automation method properly configured, plugging in a known projector or monitor should automatically result in your saved configuration being applied, although there might be a brief delay (a few seconds) while autorandr detects the display and applies the profile.

#Setting a Default Fallback Profile

It’s highly recommended to define a “fallback” or “default” profile. This profile will be automatically applied by autorandr --change (and thus by the automated services) if you connect a display configuration that doesn’t closely match any of your specifically saved profiles. The primary purpose is to prevent being left with an unusable or awkward display state (like a very low resolution, incorrect mirroring, or only the laptop’s internal display active when an external one is connected but unrecognized).

  1. Choose and Save a Safe Profile: Configure your display(s) to a very common and widely supported resolution, like 1920x1080@60Hz or perhaps 1280x720@60Hz, which most monitors and projectors should handle without issue. Save this configuration:
    # Example: Configure for 1920x1080 manually first, then save
    autorandr --save fallback-1080p

  2. Set as Default: Tell autorandr to use this profile as the default:
    autorandr --default fallback-1080p

    Now, when connecting an unknown display, autorandr will attempt to apply this safe configuration, maximizing the chances of getting a usable picture.

#Advanced Usage

autorandr offers features beyond basic profile switching:

  • Wildcard EDID Matching: Sometimes you might have several projectors or monitors of the same model series. Their EDIDs might be very similar but differ slightly (e.g., in serial number fields). To create a single profile that matches all of them, you can edit the config file within the profile directory (e.g., ~/.config/autorandr/classroom-projectors/config). Find the line(s) specifying the EDID for the relevant output(s) and replace the differing parts (or less critical parts) with an asterisk (*).
    Example snippet from ~/.config/autorandr/some_profile/config:*
    output HDMI-1
    # edid 00ffffffffffff001e6d[…] # Original specific EDID
    edid 00ffffffffffff001e6d* # Matches any EDID starting with this prefix

    Caution: Be careful not to make the wildcard too broad, or it might incorrectly match unintended displays.

  • Hook Scripts: You can automate actions that should occur whenever a specific profile is loaded or unloaded. Create executable scripts (e.g., using bash or python) named preswitch (runs before switching to this profile), postswitch (runs after switching to this profile), predetect (runs before detection), or postdetect (runs after detection) inside a profile’s directory (~/.config/autorandr/<profile_name>/) or the global config directory (~/.config/autorandr/). Common uses for postswitch include:
    • Setting a specific desktop wallpaper: feh --bg-scale /path/to/wallpaper.jpg
    • Restarting desktop panels if they don’t resize correctly: xfce4-panel -r
    • Changing the default audio output sink.
      Remember to make the scripts executable: chmod +x ~/.config/autorandr/<profile_name>/postswitch.
  • Forcing Matches (–match-edid): In rare troubleshooting scenarios where display properties other than EDID might be causing incorrect profile matching, you can experiment with options like autorandr --change --match-edid to force matching based primarily or solely on the EDID information. Consult man autorandr for details.

#Troubleshooting

If autorandr doesn’t behave as expected:

  • Check Detected Profiles & Scores: See what autorandr currently detects and how well it matches known profiles. The output shows detected profiles and their calculated match scores.
    autorandr --detected

  • Use Debug Mode: This provides highly detailed output about the detection process, including EDIDs read, profiles considered, matching scores, and the exact xrandr commands being generated and executed. This is invaluable for diagnosing why a specific profile isn’t being selected.
    autorandr --change --debug

  • Check System Logs: Look for errors or warnings related to the graphics driver (DRM) or EDID processing in the system journal. This can reveal underlying hardware or driver issues.
    # View live logs (press Ctrl+C to stop)
    journalctl -f | grep -i -E “drm|edid|autorandr”
    # View logs from the current boot
    journalctl -b | grep -i -E “drm|edid|autorandr”

  • Ensure System Updates: Reiterate the importance of sudo zypper dup to ensure you have the latest kernel, graphics drivers, and potentially autorandr fixes.
  • Check Physical Connections: Sometimes the simplest solution is overlooked. Ensure HDMI or DisplayPort cables are securely plugged in at both the Raspberry Pi and the display ends. Try a different cable if problems persist.

#Alternative: Hardware HDMI EDID Emulator

For displays that consistently cause problems due to missing, corrupt, or non-standard EDID information, a hardware HDMI EDID Emulator (also known as an EDID ghost or dummy plug) can be a viable workaround. This small device plugs into an HDMI port on the Pi and contains a chip pre-programmed with standard EDID data (e.g., for a 1080p monitor). The Raspberry Pi’s operating system reads the EDID from the emulator instead of the actual connected display. This effectively forces the Pi to “see” a standard display, often resolving issues with problematic hardware like some older projectors or KVM switches. While it provides consistency, it lacks the dynamic flexibility of autorandr – it forces one specific configuration regardless of what display (if any) is actually connected downstream. It’s a targeted solution for specific problematic hardware, not a replacement for general-purpose profile management.

#Resources

By carefully configuring autorandr and leveraging its automation features, you can significantly streamline the process of using your Raspberry Pi 4B with diverse display setups, making your transitions between classroom, office, and other locations much smoother and less prone to technical interruptions.

#bash script to collect monitor data on Debian

• Installs all required tools (including optional edid-decode).
• Detects every DRM connector (VGA, HDMI, DP, DVI, etc.), whether EDID-capable or not.
• Enumerates connected and disconnected outputs via xrandr.
• Logs EDID (sysfs, get-edid, DDC/CI), general hardware info (hwinfo, inxi, lshw), and udev data.
• Provides a summary and guidance on extracting modeline parameters.
• Offers an optional CVT-based modeline generator/applicator.

Save as ~/monitor-info.sh, then:

chmod +x ~/monitor-info.sh sudo ~/monitor-info.sh

Script:

#!/usr/bin/env bash # # monitor-info.sh # Collect comprehensive monitor info for xrandr configuration on Debian Bullseye+. # Usage: sudo bash ~/monitor-info.sh # set -euo pipefail # ------------------------------------------------------------ # Function: ensure_root # Abort if not running as root. # ------------------------------------------------------------ ensure_root() { if [[ $EUID -ne 0 ]]; then echo "ERROR: Must be run as root." >&2 exit 1 fi } # ------------------------------------------------------------ # Function: install_tools # Installs required packages in one apt command. # ------------------------------------------------------------ install_tools() { apt update apt install -y \ read-edid \ ddcutil \ hwinfo \ inxi \ lshw \ x11-xserver-utils \ edid-decode || true # optional } # ------------------------------------------------------------ # Function: setup_outdir # Creates a timestamped output directory. # ------------------------------------------------------------ setup_outdir() { OUTDIR="${HOME}/monitor-info-$(date +%Y%m%d-%H%M%S)" mkdir -p "$OUTDIR" echo "Output directory: $OUTDIR" } # ------------------------------------------------------------ # Function: log_cmd # Runs a command, logs stdout/stderr to a file, does not exit on error. # Usage: log_cmd logfile command [args...] # ------------------------------------------------------------ log_cmd() { local logfile="$1"; shift { echo "===== $(date '+%F %T') : $* =====" "$@" 2>&1 || echo "(ERROR: '$*' failed with exit code $?)" echo } >>"$OUTDIR/$logfile" } # ------------------------------------------------------------ # Function: detect_connectors # Populates arrays: DRM_ALL (all connectors), DRM_EDID (with edid), # XRANDR_CONNECTED, XRANDR_ALL. # ------------------------------------------------------------ detect_connectors() { # DRM: all dirs under /sys/class/drm matching *-* for path in /sys/class/drm/*-*; do [[ -e "$path" ]] || continue local name=$(basename "$path") DRM_ALL+=("$name") [[ -r "$path/edid" ]] && DRM_EDID+=("$name") done # xrandr: all outputs and connected ones while IFS= read -r line; do local out=$(awk '/ connected/{print $1} / disconnected/{print $1}' <<<"$line") [[ -n "$out" ]] && XRANDR_ALL+=("$out") [[ $line == *" connected"* ]] && XRANDR_CONNECTED+=("$out") done < <(xrandr 2>/dev/null) # Summary print { echo "All DRM connectors: ${DRM_ALL[*]:-none}" echo "DRM connectors with EDID: ${DRM_EDID[*]:-none}" echo "xrandr outputs: ${XRANDR_ALL[*]:-none}" echo "Connected outputs: ${XRANDR_CONNECTED[*]:-none}" } | tee "$OUTDIR/summary.txt" } # ------------------------------------------------------------ # Function: collect_sysfs_edid # Parses EDID from sysfs for connectors in DRM_EDID. # ------------------------------------------------------------ collect_sysfs_edid() { for CON in "${DRM_EDID[@]}"; do log_cmd "edid_sysfs_${CON}.log" parse-edid <"/sys/class/drm/${CON}/edid" done } # ------------------------------------------------------------ # Function: collect_getedid # Parses EDID via get-edid (ISA bus fallback). # ------------------------------------------------------------ collect_getedid() { log_cmd "edid_getedid.log" get-edid | parse-edid } # ------------------------------------------------------------ # Function: collect_ddc # Runs ddcutil detect, get-edid, and capabilities once. # ------------------------------------------------------------ collect_ddc() { log_cmd "ddcutil_detect.log" ddcutil detect log_cmd "ddcutil_edid.log" ddcutil get-edid | parse-edid log_cmd "ddcutil_caps.log" ddcutil capabilities } # ------------------------------------------------------------ # Function: collect_general_info # Captures xrandr verbose, hwinfo, inxi, lshw. # ------------------------------------------------------------ collect_general_info() { log_cmd "xrandr_verbose.log" xrandr --verbose log_cmd "hwinfo_monitor.log" hwinfo --monitor log_cmd "inxi_Gxx.log" inxi -Gxx log_cmd "lshw_display.log" lshw -C display } # ------------------------------------------------------------ # Function: collect_udev # Gathers udevadm info for all DRM_ALL connectors. # ------------------------------------------------------------ collect_udev() { for CON in "${DRM_ALL[@]}"; do local path="/sys/class/drm/${CON}" log_cmd "udevadm_${CON}.log" udevadm info --query=all --path="$(readlink -f "$path")" done } # ------------------------------------------------------------ # Function: generate_cvt # Prompts user, generates and optionally applies a CVT modeline. # ------------------------------------------------------------ generate_cvt() { read -rp "Enter width height refresh (e.g. 1920 1080 60), or ENTER to skip: " W H R if [[ -n "$W$H$R" ]]; then local MODELINE MODELINE=$(cvt "$W" "$H" "$R" 2>/dev/null | tail -n1) if [[ -n "$MODELINE" ]]; then local NAME=$(awk '{print $2}' <<<"$MODELINE") echo "Modeline: $MODELINE" | tee -a "$OUTDIR/cvt_modeline.log" local TARGET=${XRANDR_CONNECTED[0]:-} if [[ -n "$TARGET" ]]; then xrandr --newmode $MODELINE \ && xrandr --addmode "$TARGET" "$NAME" \ && echo "Applied to $TARGET; use 'xrandr --output $TARGET --mode $NAME'" else echo "No connected output to apply mode; saved under cvt_modeline.log." fi else echo "ERROR: Failed to generate modeline." | tee -a "$OUTDIR/cvt_modeline.log" fi fi } # ------------------- Main Execution ------------------- ensure_root install_tools # Arrays for connectors declare -a DRM_ALL DRM_EDID XRANDR_ALL XRANDR_CONNECTED setup_outdir detect_connectors collect_sysfs_edid collect_getedid collect_ddc collect_general_info collect_udev generate_cvt echo echo "Done. Review logs and summary.txt in $OUTDIR." echo "To craft an xrandr modeline manually, inspect 'Detailed Timing Descriptors' in the EDID logs."

Next Steps:

  1. Open $OUTDIR/summary.txt to see detected connectors and outputs.
  2. Inspect edid_sysfs_*.log and edid_getedid.log for Detailed Timing Descriptors (pixel clock, hsync/vsync, porches).
  3. Enclose those values within this bash script: ``` #!/bin/bash

#Improved Temporary Xrandr configuration script

#These settings will only apply to the current X session and

#will not persist after a reboot or X server restart.

#Ensure this script is run from within an active X Window System session.

#

#This script now checks the connection status of HDMI-1 and DP-1

#before attempting to configure them, based on live ‘xrandr’ output.

#If DP-1 is the only connected monitor, it attempts to set screen DPI

#based on its physical dimensions.

#Check if xrandr command is available

if ! command -v xrandr &> /dev/null; then echo “Error: xrandr command not found. Please ensure it is installed and in your PATH.” exit 1 fi

#Check if bc command is available (for DPI calculations)

BC_AVAILABLE=false if ! command -v bc &> /dev/null; then echo “Warning: bc command not found. DPI calculation for DP-1 will be skipped.” echo “Please install ‘bc’ if you want to enable automatic DPI setting.” else BC_AVAILABLE=true fi

echo “Applying temporary xrandr settings…” echo “This script will check current display connection statuses.” echo “Verify your display identifiers (e.g., HDMI-1, DP-1) by running ‘xrandr’ in a terminal.”

#Define mode names and modelines

MODE_NAME_1=”1152x864_60.00” # For DP-1 MODELINE_1=”81.75 1152 1216 1336 1520 864 867 871 897 -hsync +vsync” MODE_1_WIDTH_PX=1152 MODE_1_HEIGHT_PX=864

MODE_NAME_2=”2560x1080_60.00” # For HDMI-1 MODELINE_2=”230.00 2560 2720 2992 3424 1080 1083 1093 1120 -hsync +vsync”

#Define output names

OUTPUT_DP=”DP-1” # DisplayPort output OUTPUT_HDMI=”HDMI-1” # HDMI output

#Physical dimensions for DP-1 (in mm)

DP_WIDTH_MM=419 DP_HEIGHT_MM=236

#— Define New Modes (always attempt this) —

echo “” echo “Defining new mode: $MODE_NAME_1” xrandr –newmode “$MODE_NAME_1” $MODELINE_1 if [ $? -ne 0 ]; then echo “Warning: Could not define mode $MODE_NAME_1.” echo “Possible issues: mode already exists (use ‘xrandr’ to check), incorrect modeline parameters, or xrandr limitations.” fi

echo “Defining new mode: $MODE_NAME_2” xrandr –newmode “$MODE_NAME_2” $MODELINE_2 if [ $? -ne 0 ]; then echo “Warning: Could not define mode $MODE_NAME_2.” echo “Possible issues: mode already exists (use ‘xrandr’ to check), incorrect modeline parameters, or xrandr limitations.” fi

#— Check current connection status of displays —

echo “” echo “Checking current display connection status…” IS_DP_CONNECTED=false if xrandr | grep -q “^${OUTPUT_DP} connected”; then IS_DP_CONNECTED=true echo “- $OUTPUT_DP is detected as connected.” else echo “- $OUTPUT_DP is detected as disconnected.” fi

IS_HDMI_CONNECTED=false if xrandr | grep -q “^${OUTPUT_HDMI} connected”; then IS_HDMI_CONNECTED=true echo “- $OUTPUT_HDMI is detected as connected.” else echo “- $OUTPUT_HDMI is detected as disconnected.” fi

#— Initial Positioning Command (if both displays are connected) —

echo “” if $IS_HDMI_CONNECTED && $IS_DP_CONNECTED; then echo “Attempting to set $OUTPUT_HDMI –auto –above $OUTPUT_DP…” xrandr –output “$OUTPUT_HDMI” –auto –above “$OUTPUT_DP” if [ $? -ne 0 ]; then echo “Warning: Could not set $OUTPUT_HDMI –auto –above $OUTPUT_DP.” else echo “Successfully attempted to position $OUTPUT_HDMI above $OUTPUT_DP.” fi elif ! $IS_HDMI_CONNECTED && $IS_DP_CONNECTED; then echo “Skipping initial positioning: $OUTPUT_HDMI is disconnected. $OUTPUT_DP is primary.” elif $IS_HDMI_CONNECTED && ! $IS_DP_CONNECTED; then echo “Skipping initial positioning: $OUTPUT_DP is disconnected. $OUTPUT_HDMI is primary.” else echo “Skipping initial positioning: Both $OUTPUT_HDMI and $OUTPUT_DP appear to be disconnected.” fi

#— Configure DP Output ($OUTPUT_DP) —

echo “” if $IS_DP_CONNECTED; then echo “Configuring $OUTPUT_DP…” echo “Adding mode $MODE_NAME_1 to $OUTPUT_DP” xrandr –addmode “$OUTPUT_DP” “$MODE_NAME_1” if [ $? -ne 0 ]; then echo “Error adding mode $MODE_NAME_1 to $OUTPUT_DP.” echo “Ensure $OUTPUT_DP is truly connected, the mode was defined, and supports this mode.” else echo “Setting $OUTPUT_DP to mode $MODE_NAME_1” xrandr –output “$OUTPUT_DP” –mode “$MODE_NAME_1” if [ $? -ne 0 ]; then echo “Error setting $OUTPUT_DP to mode $MODE_NAME_1.” else # If DP-1 is the only connected monitor, try to set screen DPI if ! $IS_HDMI_CONNECTED && $BC_AVAILABLE; then echo “” echo “DP-1 ($OUTPUT_DP) appears to be the only connected display and ‘bc’ is available.” echo “Calculating DPI for screen based on DP-1 physical dimensions ($DP_WIDTH_MM mm x $DP_HEIGHT_MM mm) and mode $MODE_NAME_1 ($MODE_1_WIDTH_PX x $MODE_1_HEIGHT_PX).”

# Calculate DPI_H and DPI_V using bc for floating point arithmetic # scale=2 sets precision for bc output # Ensure dimensions are not zero to prevent division by zero, though hardcoded here if [ "$DP_WIDTH_MM" -gt 0 ] && [ "$DP_HEIGHT_MM" -gt 0 ]; then DPI_H_CALC=$(bc -l <<< "scale=2; $MODE_1_WIDTH_PX / ($DP_WIDTH_MM / 25.4)") DPI_V_CALC=$(bc -l <<< "scale=2; $MODE_1_HEIGHT_PX / ($DP_HEIGHT_MM / 25.4)") # Check if bc calculations were successful and returned valid numbers if [[ -n "$DPI_H_CALC" && -n "$DPI_V_CALC" && "$DPI_H_CALC" =~ ^[0-9]+([.][0-9]+)?$ && "$DPI_V_CALC" =~ ^[0-9]+([.][0-9]+)?$ ]]; then # Calculate average DPI and round to nearest integer AVG_DPI=$(printf "%.0f" "$(bc -l <<< "($DPI_H_CALC + $DPI_V_CALC) / 2")") echo "Calculated average DPI: $AVG_DPI (H: $DPI_H_CALC, V: $DPI_V_CALC)" echo "Attempting to set screen DPI to $AVG_DPI..." # Note: 'xrandr --dpi' typically applies to the entire X screen. xrandr --dpi "$AVG_DPI" if [ $? -ne 0 ]; then echo "Warning: Could not set screen DPI to $AVG_DPI. Your X server or driver might not support this or the value might be out of range." else echo "Screen DPI successfully set to $AVG_DPI. This may improve font rendering." fi else echo "Warning: DPI calculation failed or produced non-numeric results. Skipping DPI setting." echo "DPI_H_CALC raw output: '$DPI_H_CALC'" echo "DPI_V_CALC raw output: '$DPI_V_CALC'" fi else echo "Warning: DP-1 physical dimensions (DP_WIDTH_MM or DP_HEIGHT_MM) are zero. Cannot calculate DPI." fi elif ! $IS_HDMI_CONNECTED && ! $BC_AVAILABLE; then echo "DP-1 ($OUTPUT_DP) is the only display, but 'bc' is not available. Skipping DPI calculation." fi fi fi else echo "Skipping configuration for $OUTPUT_DP as it is disconnected." fi

#— Configure HDMI Output ($OUTPUT_HDMI) —

echo “” if $IS_HDMI_CONNECTED; then echo “Configuring $OUTPUT_HDMI…” echo “Adding mode $MODE_NAME_2 to $OUTPUT_HDMI” xrandr –addmode “$OUTPUT_HDMI” “$MODE_NAME_2” if [ $? -ne 0 ]; then echo “Error adding mode $MODE_NAME_2 to $OUTPUT_HDMI.” echo “Ensure $OUTPUT_HDMI is truly connected, the mode was defined, and supports this mode.” else echo “Setting $OUTPUT_HDMI to mode $MODE_NAME_2” xrandr –output “$OUTPUT_HDMI” –mode “$MODE_NAME_2” if [ $? -ne 0 ]; then echo “Error setting $OUTPUT_HDMI to mode $MODE_NAME_2.” fi fi else echo “Skipping configuration for $OUTPUT_HDMI as it is disconnected.” fi

echo “” echo “Temporary xrandr settings applied (or attempted for connected displays).” echo “Current screen configuration (relevant connected displays):” xrandr | grep “ connected”

echo “” echo “If you encounter issues, please check:” echo “1. Your display output names ($OUTPUT_DP, $OUTPUT_HDMI) are correct (use ‘xrandr’ to verify).” echo “2. Your displays are properly connected and powered on.” echo “3. The modelines are compatible with your hardware.” echo “4. Review any error messages above from xrandr.” echo “5. If DPI was set, verify with ‘xdpyinfo | grep resolution’.”

# setting up as a system service This process involves several stages: 1. **One-Time System Preparation:** Installing necessary software and performing initial configurations. 2. **Understanding and Using the Provided Scripts:** Detailing the roles of `monitor-info.sh` and the "Improved Temporary Xrandr configuration script." 3. **Configuring `autorandr`:** Manually defining display profiles for your different setups, incorporating logic from the provided scripts. 4. **Automating `autorandr`:** Ensuring `autorandr` applies these profiles automatically when an X session starts and responds to hardware changes. 5. **Advanced `autorandr` Features and Troubleshooting.** Here's a comprehensive, step-by-step guide: ## Phase 1: One-Time System Preparation You'll need to run these commands once as root to prepare your system. You can save this as a script (e.g., `debian_display_setup_prep.sh`) and execute it, or run commands individually. ```bash #!/bin/bash # # debian_display_setup_prep.sh # One-time preparation script for display management on Debian Bullseye. # Run this script as root: sudo bash debian_display_setup_prep.sh # set -euo pipefail echo ">>> Starting one-time system preparation for display management..." # Ensure running as root if [[ $EUID -ne 0 ]]; then echo "ERROR: This script must be run as root." >&2 exit 1 fi echo ">>> Updating package lists..." apt update echo ">>> Installing autorandr..." # autorandr is in Debian Bullseye repositories. # Debian's package typically includes necessary Python dependencies. # python3-pip is included as the blog post mentions it and it can be useful. apt install -y autorandr python3-pip echo ">>> Installing tools for display information gathering and configuration..." # These tools are used by monitor-info.sh or for manual xrandr setup. # 'bc' is needed for DPI calculations if you use that part of the example script. apt install -y read-edid ddcutil hwinfo inxi lshw x11-xserver-utils edid-decode bc echo ">>> System Updates Recommendation..." echo "It's highly recommended to keep your system updated." echo "You can do this by running: sudo apt update && sudo apt full-upgrade -y" # Consider uncommenting the following to perform a full upgrade now: # apt full-upgrade -y echo ">>> GPU Memory Configuration (Conditional - Primarily for Raspberry Pi or similar SBCs)..." # If your Debian Bullseye system is a Raspberry Pi, ensure sufficient GPU memory. # Edit /boot/config.txt (NOT /boot/efi/extraconfig.txt). # Example: Add or modify 'gpu_mem=256' or 'gpu_mem=512'. # A reboot is required for this change. # This step is generally not applicable for standard PCs with dedicated GPUs. echo "If using a Raspberry Pi, check/adjust GPU memory in /boot/config.txt if needed (e.g., gpu_mem=256)." echo ">>> Enabling and starting autorandr systemd services..." # The Debian autorandr package includes autorandr.service (for udev/DRM events like hotplug) # and often autorandr-resume.service (for system resume). # Enabling these provides robust, system-level display management. if systemctl list-unit-files | grep -q autorandr.service; then systemctl enable --now autorandr.service echo "autorandr.service has been enabled and started." # Also enable the resume service if it exists if systemctl list-unit-files | grep -q autorandr-resume.service; then systemctl enable --now autorandr-resume.service echo "autorandr-resume.service has been enabled and started." fi else echo "WARNING: autorandr.service not found. This is unexpected for the Debian Bullseye package." echo "Automatic hotplug detection via systemd might not function as described." fi echo ">>> One-time system preparation script finished." echo "Please REBOOT if you made changes like GPU memory configuration." echo "Next, understand the provided scripts and then manually configure autorandr profiles."

To use this script:

  1. Save it as debian_display_setup_prep.sh.
  2. Make it executable: chmod +x debian_display_setup_prep.sh.
  3. Run it as root: sudo ./debian_display_setup_prep.sh.
  4. Reboot if you made changes to critical system configurations like GPU memory.

#Phase 2: Understanding and Using the Provided Scripts

Your original query included two scripts from a blog post. Here’s how they fit into the setup:

#A. monitor-info.sh (Bash script to collect monitor data)

  • Purpose: This script is a diagnostic and information-gathering tool. It is not meant to run automatically every time an X session starts. Its purpose is to help you understand your display hardware, especially when configuring new or problematic monitors. The data it gathers (EDID, timings) is crucial for crafting manual xrandr commands if needed.
  • “Enclosing” this script:
    1. The “One-Time System Preparation Script” (Phase 1) already installed all the command-line tools monitor-info.sh uses.
    2. Below is the script content. Save it to a file in your home directory (e.g., ~/monitor-info.sh).
    3. Make it executable: chmod +x ~/monitor-info.sh.
    4. Run it manually with sudo ~/monitor-info.sh when you need to diagnose a display setup before creating an autorandr profile for it.
#!/usr/bin/env bash # # monitor-info.sh # Collect comprehensive monitor info for xrandr configuration on Debian Bullseye+. # Usage: sudo bash ~/monitor-info.sh # set -euo pipefail # ------------------------------------------------------------ # Function: ensure_root # Abort if not running as root. # ------------------------------------------------------------ ensure_root() { if [[ $EUID -ne 0 ]]; then echo "ERROR: Must be run as root." >&2 exit 1 fi } # ------------------------------------------------------------ # Function: install_tools_explicit_check # Checks if tools are installed (they should be by the prep script). # ------------------------------------------------------------ install_tools_explicit_check() { local missing_pkgs="" for pkg in read-edid ddcutil hwinfo inxi lshw x11-xserver-utils edid-decode; do if ! dpkg -s "$pkg" &> /dev/null; then missing_pkgs="$missing_pkgs $pkg" fi done if [[ -n "$missing_pkgs" ]]; then echo "WARNING: Some required packages are missing: $missing_pkgs" >&2 echo "Please run the main preparation script or 'sudo apt install $missing_pkgs'" >&2 # Optionally, exit here if you want to be strict: exit 1 fi } # ------------------------------------------------------------ # Function: setup_outdir # Creates a timestamped output directory. # ------------------------------------------------------------ setup_outdir() { # Try to create in user's home directory if sudo is used from a user context REAL_USER=$(logname 2>/dev/null || echo "$SUDO_USER") USER_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6) if [[ -n "$USER_HOME" && -d "$USER_HOME" ]]; then OUTDIR_BASE="$USER_HOME" else OUTDIR_BASE="/root" # Fallback to /root if user home not found fi OUTDIR="${OUTDIR_BASE}/monitor-info-$(date +%Y%m%d-%H%M%S)" mkdir -p "$OUTDIR" # If run as root, ensure the user can access it if OUTDIR_BASE was user's home if [[ -n "$SUDO_USER" && "$OUTDIR_BASE" == "$USER_HOME" ]]; then chown -R "$SUDO_USER:$SUDO_USER" "$OUTDIR" || true fi echo "Output directory: $OUTDIR" } # ------------------------------------------------------------ # Function: log_cmd # Runs a command, logs stdout/stderr to a file, does not exit on error. # Usage: log_cmd logfile command [args...] # ------------------------------------------------------------ log_cmd() { local logfile="$1"; shift { echo "===== $(date '+%F %T') : $* =====" "$@" 2>&1 || echo "(ERROR: '$*' failed with exit code $?)" echo } >>"$OUTDIR/$logfile" } # ------------------------------------------------------------ # Function: detect_connectors # Populates arrays: DRM_ALL (all connectors), DRM_EDID (with edid), # XRANDR_CONNECTED, XRANDR_ALL. # ------------------------------------------------------------ detect_connectors() { DRM_ALL=() DRM_EDID=() XRANDR_ALL=() XRANDR_CONNECTED=() # DRM: all dirs under /sys/class/drm matching card*-* (more specific) for card_path in /sys/class/drm/card*; do if [[ -d "$card_path" ]]; then for path in "$card_path"/*; do if [[ -d "$path" && $(basename "$path") =~ ^[A-Z]+-[0-9]+$|^[a-z]+-[0-9]+$ ]]; then # Matches like HDMI-A-1 or DP-1 local name=$(basename "$path") DRM_ALL+=("$name") [[ -r "$path/edid" && -s "$path/edid" ]] && DRM_EDID+=("$name") # Check if EDID file is readable and not empty fi done fi done # xrandr: all outputs and connected ones # Ensure X is running for xrandr, otherwise skip if command -v xrandr &> /dev/null && xhost >/dev/null 2>&1; then while IFS= read -r line; do local out=$(awk '/ connected/{print $1} / disconnected/{print $1}' <<<"$line") [[ -n "$out" ]] && XRANDR_ALL+=("$out") [[ $line == *" connected"* ]] && XRANDR_CONNECTED+=("$out") done < <(xrandr 2>/dev/null) else echo "NOTE: xrandr queries skipped (X server not accessible or xrandr not found)." | tee -a "$OUTDIR/summary.txt" fi # Summary print { echo "All DRM connectors found: ${DRM_ALL[*]:-none}" echo "DRM connectors with readable EDID: ${DRM_EDID[*]:-none}" echo "xrandr outputs (if X session active): ${XRANDR_ALL[*]:-none}" echo "Connected outputs via xrandr (if X session active): ${XRANDR_CONNECTED[*]:-none}" } | tee -a "$OUTDIR/summary.txt" } # ------------------------------------------------------------ # Function: collect_sysfs_edid # Parses EDID from sysfs for connectors in DRM_EDID. # ------------------------------------------------------------ collect_sysfs_edid() { if ! command -v parse-edid &> /dev/null && command -v edid-decode &> /dev/null; then echo "Skipping sysfs EDID parsing: neither parse-edid nor edid-decode found." return fi local parser_cmd parser_cmd=$(command -v edid-decode || command -v parse-edid) for CON_BASENAME in "${DRM_EDID[@]}"; do # Find full path for CON_BASENAME under /sys/class/drm/card* local edid_path="" for card_path in /sys/class/drm/card*; do if [[ -e "$card_path/$CON_BASENAME/edid" ]]; then edid_path="$card_path/$CON_BASENAME/edid" break fi done if [[ -n "$edid_path" && -r "$edid_path" ]]; then log_cmd "edid_sysfs_${CON_BASENAME}.log" "$parser_cmd" <"$edid_path" else echo "Could not read EDID for $CON_BASENAME from $edid_path" >> "$OUTDIR/summary.txt" fi done } # ------------------------------------------------------------ # Function: collect_getedid # Parses EDID via get-edid (ISA bus fallback, part of read-edid package). # ------------------------------------------------------------ collect_getedid() { if ! command -v get-edid &> /dev/null; then echo "Skipping get-edid: command not found." return fi if ! command -v parse-edid &> /dev/null && command -v edid-decode &> /dev/null; then echo "Skipping get-edid parsing: no EDID parser found." return fi local parser_cmd parser_cmd=$(command -v edid-decode || command -v parse-edid) # get-edid often needs I2C modules loaded, like i2c-dev # Ensure modules are loaded, if possible (might require reboot or manual modprobe) if ! lsmod | grep -q "i2c_dev"; then echo "INFO: i2c_dev module not loaded. get-edid might fail. Consider 'sudo modprobe i2c_dev'." fi log_cmd "edid_getedid.log" bash -c "get-edid 2>/dev/null | $parser_cmd" } # ------------------------------------------------------------ # Function: collect_ddc # Runs ddcutil detect and other commands. # ------------------------------------------------------------ collect_ddc() { if ! command -v ddcutil &> /dev/null; then echo "Skipping ddcutil: command not found." return fi # ddcutil often needs I2C modules loaded, like i2c-dev if ! lsmod | grep -q "i2c_dev"; then echo "INFO: i2c_dev module not loaded. ddcutil might fail. Consider 'sudo modprobe i2c_dev'." fi log_cmd "ddcutil_detect.log" ddcutil detect --verbose # The following might be too much or redundant if sysfs EDID works well # log_cmd "ddcutil_edid.log" ddcutil get-edid --verbose | edid-decode # or parse-edid # log_cmd "ddcutil_caps.log" ddcutil capabilities --verbose } # ------------------------------------------------------------ # Function: collect_general_info # Captures xrandr verbose, hwinfo, inxi, lshw. # ------------------------------------------------------------ collect_general_info() { if command -v xrandr &> /dev/null && xhost >/dev/null 2>&1; then log_cmd "xrandr_verbose.log" xrandr --verbose fi command -v hwinfo &> /dev/null && log_cmd "hwinfo_monitor.log" hwinfo --monitor --verbose command -v inxi &> /dev/null && log_cmd "inxi_Gxx.log" inxi -Gxx --display command -v lshw &> /dev/null && log_cmd "lshw_display.log" lshw -C display -sanitize } # ------------------------------------------------------------ # Function: collect_udev # Gathers udevadm info for all DRM_ALL connectors. # ------------------------------------------------------------ collect_udev() { for CON_BASENAME in "${DRM_ALL[@]}"; do local sys_path="" for card_path in /sys/class/drm/card*; do if [[ -d "$card_path/$CON_BASENAME" ]]; then sys_path="$card_path/$CON_BASENAME" break fi done if [[ -n "$sys_path" ]]; then log_cmd "udevadm_${CON_BASENAME}.log" udevadm info --query=all --path="$(readlink -f "$sys_path")" fi done } # ------------------------------------------------------------ # Function: generate_cvt_interactive # Prompts user, generates and optionally applies a CVT modeline. # (Note: applying modes should be done carefully and typically within an X session) # ------------------------------------------------------------ generate_cvt_interactive() { if ! command -v cvt &> /dev/null; then echo "Skipping CVT modeline generation: cvt command not found (part of x11-xserver-utils)." return fi if ! command -v xrandr &> /dev/null || ! xhost >/dev/null 2>&1; then echo "Skipping CVT modeline application: xrandr not available or X session not active." read -rp "Enter width height refresh (e.g. 1920 1080 60) to generate modeline, or ENTER to skip: " W H R if [[ -n "$W" && -n "$H" && -n "$R" ]]; then local MODELINE MODELINE=$(cvt "$W" "$H" "$R" 2>/dev/null | grep Modeline | sed 's/Modeline //') if [[ -n "$MODELINE" ]]; then echo "Generated Modeline: $MODELINE" | tee -a "$OUTDIR/cvt_modeline.log" else echo "ERROR: Failed to generate modeline with cvt $W $H $R." | tee -a "$OUTDIR/cvt_modeline.log" fi fi return fi read -rp "Enter width height refresh (e.g. 1920 1080 60), or ENTER to skip CVT modeline generation: " W H R if [[ -n "$W" && -n "$H" && -n "$R" ]]; then local MODELINE_FULL MODELINE_PARAMS NAME MODELINE_FULL=$(cvt "$W" "$H" "$R" 2>/dev/null | grep Modeline) if [[ -n "$MODELINE_FULL" ]]; then MODELINE_PARAMS=$(echo "$MODELINE_FULL" | sed 's/Modeline //; s/^"[^"]*" //') # Remove "Modeline" and the name part NAME=$(echo "$MODELINE_FULL" | awk '{print $2}' | tr -d '"') # Extract the mode name echo "Generated Modeline: $MODELINE_FULL" | tee -a "$OUTDIR/cvt_modeline.log" echo "Parameters for xrandr --newmode: $NAME $MODELINE_PARAMS" | tee -a "$OUTDIR/cvt_modeline.log" local TARGET=${XRANDR_CONNECTED[0]:-} # Use first connected output as example if [[ -n "$TARGET" ]]; then read -rp "Attempt to apply this to $TARGET (requires X session)? (y/N): " APPLY_CVT if [[ "$APPLY_CVT" =~ ^[Yy]$ ]]; then echo "Applying: xrandr --newmode $NAME $MODELINE_PARAMS" | tee -a "$OUTDIR/cvt_modeline.log" xrandr --newmode "$NAME" $MODELINE_PARAMS if [[ $? -eq 0 ]]; then echo "Applying: xrandr --addmode $TARGET $NAME" | tee -a "$OUTDIR/cvt_modeline.log" xrandr --addmode "$TARGET" "$NAME" if [[ $? -eq 0 ]]; then echo "To activate, run: xrandr --output $TARGET --mode $NAME" | tee -a "$OUTDIR/cvt_modeline.log" read -rp "Attempt to set mode $NAME on $TARGET now? (y/N): " SET_MODE if [[ "$SET_MODE" =~ ^[Yy]$ ]]; then xrandr --output "$TARGET" --mode "$NAME" echo "Mode set attempt completed." | tee -a "$OUTDIR/cvt_modeline.log" fi else echo "ERROR: xrandr --addmode failed." | tee -a "$OUTDIR/cvt_modeline.log" fi else echo "ERROR: xrandr --newmode failed (mode might already exist or be invalid)." | tee -a "$OUTDIR/cvt_modeline.log" fi fi else echo "No connected xrandr output detected to suggest application." | tee -a "$OUTDIR/cvt_modeline.log" fi else echo "ERROR: Failed to generate modeline with cvt $W $H $R." | tee -a "$OUTDIR/cvt_modeline.log" fi fi } # ------------------- Main Execution ------------------- ensure_root install_tools_explicit_check # Verify tools, prep script should have installed them # Arrays for connectors declare -a DRM_ALL DRM_EDID XRANDR_ALL XRANDR_CONNECTED setup_outdir detect_connectors # Call this after setup_outdir so summary.txt goes into $OUTDIR collect_sysfs_edid collect_getedid collect_ddc collect_general_info collect_udev generate_cvt_interactive echo echo "Done. Review logs and summary.txt in $OUTDIR." echo "To craft an xrandr modeline manually, inspect 'Detailed Timing Descriptors' or 'Established Timings' in the EDID logs (edid_sysfs_*.log or edid_getedid.log)."

#B. “Improved Temporary Xrandr configuration script”

  • Purpose: This script provides a template of xrandr commands for a specific dual-monitor setup (DP-1 and HDMI-1) including custom modelines and DPI settings.
  • “Enclosing” this script:
    1. It is not meant to be run automatically on every X session start if you are using autorandr as the primary display manager. Doing so could conflict with autorandr.
    2. Instead, its xrandr commands and logic should be used as a reference or template when you are manually configuring your displays once before saving an autorandr profile (Phase 3, Step 2).
    3. The DPI setting part is more dynamic and, if desired for a specific autorandr profile, should be placed into an autorandr hook script (see Phase 3, Step 7).
    4. Below is the script content for your reference during manual configuration:
#!/bin/bash # This is the "Improved Temporary Xrandr configuration script" from the blog post. # Use its xrandr commands as a TEMPLATE when manually setting up your displays # BEFORE saving an autorandr profile. # Do NOT run this script directly on every X startup if using autorandr. # The DPI calculation part can be adapted into an autorandr postswitch hook. # Check if xrandr command is available if ! command -v xrandr &> /dev/null; then echo "Error: xrandr command not found. Please ensure it is installed and in your PATH." exit 1 fi # Check if bc command is available (for DPI calculations) BC_AVAILABLE=false if ! command -v bc &> /dev/null; then echo "Warning: bc command not found. DPI calculation for DP-1 will be skipped." echo "Please install 'bc' if you want to enable automatic DPI setting." else BC_AVAILABLE=true fi echo "Applying temporary xrandr settings (REFERENCE SCRIPT)..." echo "This script will check current display connection statuses." echo "Verify your display identifiers (e.g., HDMI-1, DP-1) by running 'xrandr' in a terminal." # Define mode names and modelines MODE_NAME_1="1152x864_60.00" # For DP-1 MODELINE_1="81.75 1152 1216 1336 1520 864 867 871 897 -hsync +vsync" MODE_1_WIDTH_PX=1152 MODE_1_HEIGHT_PX=864 MODE_NAME_2="2560x1080_60.00" # For HDMI-1 MODELINE_2="230.00 2560 2720 2992 3424 1080 1083 1093 1120 -hsync +vsync" # Define output names OUTPUT_DP="DP-1" # DisplayPort output OUTPUT_HDMI="HDMI-1" # HDMI output # Physical dimensions for DP-1 (in mm) - FOR DPI CALCULATION DP_WIDTH_MM=419 DP_HEIGHT_MM=236 # --- Define New Modes (always attempt this if using these modes) --- echo "" echo "Defining new mode: $MODE_NAME_1" xrandr --newmode "$MODE_NAME_1" $MODELINE_1 if [ $? -ne 0 ]; then echo "Warning: Could not define mode $MODE_NAME_1." echo "Possible issues: mode already exists (use 'xrandr' to check), incorrect modeline parameters, or xrandr limitations." fi echo "Defining new mode: $MODE_NAME_2" xrandr --newmode "$MODE_NAME_2" $MODELINE_2 if [ $? -ne 0 ]; then echo "Warning: Could not define mode $MODE_NAME_2." fi # --- Check current connection status of displays --- echo "" echo "Checking current display connection status..." IS_DP_CONNECTED=false if xrandr | grep -q "^${OUTPUT_DP} connected"; then IS_DP_CONNECTED=true echo "- $OUTPUT_DP is detected as connected." else echo "- $OUTPUT_DP is detected as disconnected." fi IS_HDMI_CONNECTED=false if xrandr | grep -q "^${OUTPUT_HDMI} connected"; then IS_HDMI_CONNECTED=true echo "- $OUTPUT_HDMI is detected as connected." else echo "- $OUTPUT_HDMI is detected as disconnected." fi # --- Initial Positioning Command (if both displays are connected) --- # Example: xrandr --output HDMI-1 --auto --above DP-1 # This needs to be adapted to your desired layout. echo "" if $IS_HDMI_CONNECTED && $IS_DP_CONNECTED; then echo "Attempting to set $OUTPUT_HDMI --auto --above $OUTPUT_DP..." # Replace with your desired layout command, e.g.: # xrandr --output "$OUTPUT_HDMI" --mode "$MODE_NAME_2" --output "$OUTPUT_DP" --mode "$MODE_NAME_1" --primary --right-of "$OUTPUT_HDMI" xrandr --output "$OUTPUT_HDMI" --auto --above "$OUTPUT_DP" # Example from script if [ $? -ne 0 ]; then echo "Warning: Could not set initial positioning." else echo "Successfully attempted to position $OUTPUT_HDMI above $OUTPUT_DP." fi elif ! $IS_HDMI_CONNECTED && $IS_DP_CONNECTED; then echo "Skipping initial positioning: $OUTPUT_HDMI is disconnected. $OUTPUT_DP is primary." xrandr --output "$OUTPUT_DP" --mode "$MODE_NAME_1" --primary --auto elif $IS_HDMI_CONNECTED && ! $IS_DP_CONNECTED; then echo "Skipping initial positioning: $OUTPUT_DP is disconnected. $OUTPUT_HDMI is primary." xrandr --output "$OUTPUT_HDMI" --mode "$MODE_NAME_2" --primary --auto else echo "Skipping initial positioning: Both $OUTPUT_HDMI and $OUTPUT_DP appear to be disconnected." fi # --- Configure DP Output ($OUTPUT_DP) --- echo "" if $IS_DP_CONNECTED; then echo "Configuring $OUTPUT_DP..." echo "Adding mode $MODE_NAME_1 to $OUTPUT_DP" xrandr --addmode "$OUTPUT_DP" "$MODE_NAME_1" if [ $? -ne 0 ]; then echo "Error adding mode $MODE_NAME_1 to $OUTPUT_DP." else echo "Setting $OUTPUT_DP to mode $MODE_NAME_1" # This command would typically be part of the comprehensive layout command above # xrandr --output "$OUTPUT_DP" --mode "$MODE_NAME_1" # (Potentially redundant if already set in positioning) if [ $? -ne 0 ]; then echo "Error setting $OUTPUT_DP to mode $MODE_NAME_1." else # If DP-1 is the only connected monitor, try to set screen DPI if ! $IS_HDMI_CONNECTED && $BC_AVAILABLE; then echo "" echo "DP-1 ($OUTPUT_DP) appears to be the only connected display and 'bc' is available." echo "Calculating DPI for screen based on DP-1 physical dimensions ($DP_WIDTH_MM mm x $DP_HEIGHT_MM mm) and mode $MODE_NAME_1 ($MODE_1_WIDTH_PX x $MODE_1_HEIGHT_PX)." if [ "$DP_WIDTH_MM" -gt 0 ] && [ "$DP_HEIGHT_MM" -gt 0 ]; then DPI_H_CALC=$(bc -l <<< "scale=2; $MODE_1_WIDTH_PX / ($DP_WIDTH_MM / 25.4)") DPI_V_CALC=$(bc -l <<< "scale=2; $MODE_1_HEIGHT_PX / ($DP_HEIGHT_MM / 25.4)") if [[ -n "$DPI_H_CALC" && -n "$DPI_V_CALC" && "$DPI_H_CALC" =~ ^[0-9]+([.][0-9]+)?$ && "$DPI_V_CALC" =~ ^[0-9]+([.][0-9]+)?$ ]]; then AVG_DPI=$(printf "%.0f" "$(bc -l <<< "($DPI_H_CALC + $DPI_V_CALC) / 2")") echo "Calculated average DPI: $AVG_DPI (H: $DPI_H_CALC, V: $DPI_V_CALC)" echo "Attempting to set screen DPI to $AVG_DPI..." xrandr --dpi "$AVG_DPI" if [ $? -ne 0 ]; then echo "Warning: Could not set screen DPI to $AVG_DPI." else echo "Screen DPI successfully set to $AVG_DPI." fi else echo "Warning: DPI calculation failed or produced non-numeric results." fi else echo "Warning: DP-1 physical dimensions are zero. Cannot calculate DPI." fi elif ! $IS_HDMI_CONNECTED && ! $BC_AVAILABLE; then echo "DP-1 ($OUTPUT_DP) is the only display, but 'bc' is not available. Skipping DPI calculation." fi fi fi else echo "Skipping configuration for $OUTPUT_DP as it is disconnected." fi # --- Configure HDMI Output ($OUTPUT_HDMI) --- echo "" if $IS_HDMI_CONNECTED; then echo "Configuring $OUTPUT_HDMI..." echo "Adding mode $MODE_NAME_2 to $OUTPUT_HDMI" xrandr --addmode "$OUTPUT_HDMI" "$MODE_NAME_2" if [ $? -ne 0 ]; then echo "Error adding mode $MODE_NAME_2 to $OUTPUT_HDMI." else echo "Setting $OUTPUT_HDMI to mode $MODE_NAME_2" # This command would typically be part of the comprehensive layout command above # xrandr --output "$OUTPUT_HDMI" --mode "$MODE_NAME_2" # (Potentially redundant if already set in positioning) if [ $? -ne 0 ]; then echo "Error setting $OUTPUT_HDMI to mode $MODE_NAME_2." fi fi else echo "Skipping configuration for $OUTPUT_HDMI as it is disconnected." fi echo "" echo "Temporary xrandr settings (REFERENCE SCRIPT) applied (or attempted for connected displays)." echo "Current screen configuration (relevant connected displays):" xrandr | grep " connected"

#Phase 3: Configuring autorandr (Manual Steps for Each User and Setup)

autorandr works by saving your current display configuration into a “profile.” This needs to be done by the user (root or normal user) within their own X session for each unique display setup.

Steps to create profiles:

  1. Connect Displays: Attach your specific combination of monitors, projectors, etc.
  2. Manually Configure Displays: Use your desktop environment’s display settings tool or manual xrandr commands in a terminal to arrange your displays exactly as desired.
    • Refer to the xrandr commands in the “Improved Temporary Xrandr configuration script” above as a template for defining modelines (xrandr --newmode ...), adding modes (xrandr --addmode ...), and setting outputs (xrandr --output <name> --mode <mode> --pos <XxY> --primary --rotate <normal|left|right|inverted> --output <other_name> ...).
    • For example, to set up two monitors, HDMI-1 (2560x1080) to the left of DP-1 (1920x1080, primary):
      # (Assuming modelines for these resolutions are already supported or added via --newmode) xrandr --output HDMI-1 --mode 2560x1080 --pos 0x0 --rotate normal \ --output DP-1 --mode 1920x1080 --pos 2560x0 --rotate normal --primary
  3. Save the autorandr Profile: Once displays are perfectly configured, save this state:
    autorandr --save <profile_name>

    Use descriptive names (e.g., office_dual_monitors, lecture_projector_1024x768).

  4. Profile Locations and User Context:
    • Profiles are saved in ~/.config/autorandr/ (e.g., /root/.config/autorandr/ for root, /home/youruser/.config/autorandr/ for youruser).
    • For common profiles accessible by both root and normal users (if no user-specific one matches): You can manually copy or create profile directories in /etc/xdg/autorandr/. autorandr checks user-specific paths first. This is useful if root and a normal user often encounter the same display setups.
  5. Repeat for Other Setups: Repeat steps 1-3 for every different display configuration.
  6. List and Set Default Profile:
    autorandr --list # Review saved profiles # Configure a very safe, common display setup (e.g., single monitor 1920x1080) # Then save it and set it as default: autorandr --save fallback_safe_1080p autorandr --default fallback_safe_1080p

    This default profile is crucial if an unknown display setup is detected.

  7. Implementing DPI Settings via Hooks (Optional): If you want the DPI calculation from the “Improved Temporary Xrandr configuration script” to apply when a specific autorandr profile is loaded, create a postswitch hook script within that profile’s directory.
    • For a profile named my_specific_setup (directory: ~/.config/autorandr/my_specific_setup/ or /etc/xdg/autorandr/my_specific_setup/), create an executable script: .../my_specific_setup/postswitch

      #!/bin/sh # # postswitch hook for profile 'my_specific_setup' to set DPI. # IMPORTANT: Customize all variables below (OUTPUT_NAME, MODE_WIDTH_PX, etc.) # to match the specifics of THIS 'my_specific_setup' profile. # # For debugging hooks, you can use logger or write to a temp file: # echo "Running postswitch for my_specific_setup at $(date)" >> /tmp/autorandr_hook_debug.log if ! command -v bc > /dev/null 2>&1; then # echo "postswitch_dpi: bc command not found, skipping DPI." >> /tmp/autorandr_hook_debug.log exit 0 fi # --- START CUSTOMIZATION for 'my_specific_setup' profile --- OUTPUT_NAME="DP-1" # The actual output name (e.g., DP-1, HDMI-A-0) for this profile's main display MODE_WIDTH_PX=1152 # Horizontal resolution of OUTPUT_NAME in this profile MODE_HEIGHT_PX=864 # Vertical resolution of OUTPUT_NAME in this profile PHYS_WIDTH_MM=419 # Physical width in mm of the monitor connected to OUTPUT_NAME PHYS_HEIGHT_MM=236 # Physical height in mm of the monitor connected to OUTPUT_NAME # --- END CUSTOMIZATION --- # Check if the relevant display is configured as expected by autorandr for this profile # This grep is a basic check; more robust checks might be needed for complex setups. if xrandr | grep -q "^${OUTPUT_NAME} connected.*${MODE_WIDTH_PX}x${MODE_HEIGHT_PX}"; then if [ "$PHYS_WIDTH_MM" -gt 0 ] && [ "$PHYS_HEIGHT_MM" -gt 0 ]; then DPI_H_CALC=$(bc -l <<< "scale=2; $MODE_WIDTH_PX / ($PHYS_WIDTH_MM / 25.4)") DPI_V_CALC=$(bc -l <<< "scale=2; $MODE_HEIGHT_PX / ($PHYS_HEIGHT_MM / 25.4)") if [[ -n "$DPI_H_CALC" && -n "$DPI_V_CALC" && "$DPI_H_CALC" =~ ^[0-9]+([.][0-9]+)?$ && "$DPI_V_CALC" =~ ^[0-9]+([.][0-9]+)?$ ]]; then AVG_DPI=$(printf "%.0f" "$(bc -l <<< "($DPI_H_CALC + $DPI_V_CALC) / 2")") # echo "postswitch_dpi: Setting screen DPI to $AVG_DPI for $OUTPUT_NAME." >> /tmp/autorandr_hook_debug.log xrandr --dpi "$AVG_DPI" # else # echo "postswitch_dpi: DPI calculation failed." >> /tmp/autorandr_hook_debug.log fi # else # echo "postswitch_dpi: Physical dimensions for $OUTPUT_NAME are zero." >> /tmp/autorandr_hook_debug.log fi # else # echo "postswitch_dpi: Expected display $OUTPUT_NAME not found or not in expected mode." >> /tmp/autorandr_hook_debug.log fi exit 0
    • Make the hook script executable: chmod +x .../my_specific_setup/postswitch. Repeat for other profiles if needed, customizing variables each time.

#Phase 4: Automating autorandr

This ensures autorandr acts on display changes and at X session startup.

  1. Systemd Service (for hotplugging and system events): The autorandr.service (enabled in Phase 1) handles display changes detected by udev (e.g., plugging/unplugging monitors). This works system-wide, even outside or during an X session. It’s the primary mechanism for dynamic changes.

  2. X Session Startup Script (for startx): To explicitly trigger autorandr when an X session is initiated via startx (by root or any normal user), create: File: /etc/X11/Xsession.d/50-autorandr-load-profile

    #!/bin/sh # # /etc/X11/Xsession.d/50-autorandr-load-profile # Load autorandr profile on X session start (for startx users). # This script runs as the user starting the X session. # if command -v xrandr >/dev/null 2>&1 && command -v autorandr >/dev/null 2>&1; then # --change: detects displays, loads best profile. # --batch: implies --force, suppresses xrandr output unless error. Good for scripts. autorandr --change --batch # For debugging, you could log: # LOG_DIR="/tmp/autorandr_logs" # mkdir -p "$LOG_DIR" # echo "Xsession autorandr trigger for $(whoami) at $(date)" >> "$LOG_DIR/xsession_trigger.log" # autorandr --change --debug >> "$LOG_DIR/autorandr_xsession_$(whoami)_$(date +%Y%m%d-%H%M%S).log" 2>&1 fi exit 0
    • Activate: Save and make executable: sudo chmod +x /etc/X11/Xsession.d/50-autorandr-load-profile.
    • Interplay: The autorandr.service provides continuous monitoring. This Xsession script ensures that when startx is explicitly run, autorandr evaluates the display situation at that specific moment, respecting the current user’s profiles.

#Phase 5: Advanced autorandr Features and Troubleshooting

  • Advanced Features (from blog post):
    • Wildcard EDID Matching: Edit config file in profile dirs (e.g., ~/.config/autorandr/<profile>/config) to use * in EDID strings for flexibility with similar monitors.
    • Other Hook Scripts: preswitch, predetect, postdetect for more automation (e.g., restarting panels, changing audio sinks). Remember chmod +x for all hook scripts.
  • Troubleshooting (from blog post):
    • autorandr --detected: See current detection and scores.
    • autorandr --change --debug: Verbose output for manual diagnosis in an X session.
    • journalctl -f | grep -i -E "drm|edid|autorandr": Live system logs.
    • journalctl -b | grep -i -E "drm|edid|autorandr": Logs from current boot.
    • Ensure system is updated: sudo apt update && sudo apt full-upgrade -y.
    • Check physical cable connections.

By following these phases, you’ll have a robust system where autorandr manages your display configurations automatically when displays are connected/disconnected or when you start an X session with startx, leveraging the specific configurations and logic from the provided scripts. Remember to adapt profile names and hook script details to your exact hardware and preferences.

URL: https://ib.bsb.br/xrandr