omarchy icon indicating copy to clipboard operation
omarchy copied to clipboard

Disable Internal (Laptop) Display on Lid Close

Open curtisspendlove opened this issue 4 months ago • 12 comments

My Framework 13 (2.8K Display upgrade) keeps the internal display awake even after the laptop lid is closed. This led to some fairly confusing behavior (including launching windows on the internal display--a LOT of them, since I just kept hitting the keybinds)

The following bindl directives have resolved this for me, in my testing it needs to be in hyprland.conf and not monitors.conf (binds? probably).

bindl=,switch:on:Lid Switch,exec,hyprctl keyword monitor "eDP-1, disable"
bindl=,switch:off:Lid Switch,exec,hyprctl keyword monitor "eDP-1, preferred, auto, auto"

I would like assistance testing this as I only have a single data point:

Framework 13 (I'll edit with stats in a bit) ASUS ProArt 5K Display

PLEASE try if you are experiencing this on any laptop and I'd love non-framework feedback as well. My theory is that this is a problem for all internal displays but I'm unsure.

Prospective PR (in a fork so I can clean it up before I push the branch to upstream): https://github.com/knightoftheoldcode/omarchy/pull/1

TIA!

Ref: #332

curtisspendlove avatar Jul 31 '25 21:07 curtisspendlove

Was affected (Using Dell XPS 13). Blindly copied the two lines. Problem solved! Thanks!

deas avatar Aug 01 '25 07:08 deas

Now that there is no turning back for me, I experimented a bit with my displays. Here is what I have (found) beyond the lid close switches off display.

My hardware is:

 PC: XPS 13 9310
│ ├: Intel Iris Xe Graphics @ 1.35 GHz [Integrated]
│ ├󱄄: 3840x2160 @ 60 Hz (as 1920x1080) in 27" [External]
│ ├󱄄: 3456x2160 @ 60 Hz (as 1297x811) in 13" [Built-in]

Using auto might have surprising effects when you close and open the lid.

After opening the lid, eDP-1 appeared right of DP-7, when it was left of it before. This is likely normal behavior, but might still be surprising.

The following hyprland config (getting myself some new glasses today):

env = GDK_SCALE, 2
monitor = eDP-1, preferred, auto-left, 2.666667
monitor = DP-7, preferred, auto, 2

bindl=,switch:on:Lid Switch,exec,hyprctl keyword monitor "eDP-1, disable"
bindl=,switch:off:Lid Switch,exec,hyprctl keyword monitor "eDP-1, preferred, auto-left, 2.666667"

Keeps positioning and resolution stable for me and things a pleasure to read.

deas avatar Aug 01 '25 09:08 deas

It can go in monitors.conf.

Did you not have to make lid switch changes in /etc/systemd/logind.conf ?

AndreRobitaille avatar Aug 01 '25 11:08 AndreRobitaille

I actually have these lines in monitors.conf. Made no changes to logind.conf.

deas avatar Aug 01 '25 11:08 deas

Thanks all!Great feedback. I figured it should be able to go into monitors.conf but it didn’t seem to work for me. And yes. It’s going to have to mirror your resolution and stuff probably. We will get better at auto detecting and recommending things. This is part of the research I want to do on it. I’m personally trying the auto-center-up option for my external above my laptop now that I’m down to one external from three. Thanks!!!On Aug 1, 2025, at 5:41 AM, Andreas Steffan @.***> wrote:deas left a comment (basecamp/omarchy#427) I actually have these lines in monitors.conf. Made no changes to logind.conf.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>

curtisspendlove avatar Aug 01 '25 16:08 curtisspendlove

I did a similar thing manually a couple weeks ago so it would behave like a MacBook (and probably Windows) in clamshell mode with all the workspaces usable on the monitor. It was very confusing to close the lid and see how hyprland handles it by default, which is nonsensical to me. I don't think the default is going to be what anybody expects to happen with separate workspaces per monitor even in clamshell mode.

dakotahp avatar Aug 07 '25 23:08 dakotahp

This was amazing thank you. Dell XPS 15 and this worked first try after spending hours trying to come up with a custom solution. It does seem to break if hypridle is triggered however.

meta-byte avatar Aug 24 '25 20:08 meta-byte

@curtisspendlove thanks! placed your directive in monitors.conf saved/quit and worked instantly in MacBookPro 2015. Made afternoon of hacking with my 9yo very enjoyable.

rmateu avatar Aug 30 '25 19:08 rmateu

It works amazing, I remove my acpid events. Just change the eDP-1 (to the your specific monitor name) that we can get using hyprctl monitors,

iamparmjeet avatar Sep 01 '25 11:09 iamparmjeet

Thank you very much @curtisspendlove, this worked perfectly on first try on my Framework 13 too.

daanlenaerts avatar Sep 02 '25 14:09 daanlenaerts

Thanks @curtisspendlove! I tried these directives in hyprland.conf and it worked perfectly for my Framework 13. Thanks!!!

paganotoni avatar Sep 07 '25 12:09 paganotoni

Some testing notes: If the laptop is restarted with closed lid the internal monitor stays on. Probably because the events of open/close are not triggered. If the lid is opened and closed the internal display gets correctly disabled.

paganotoni avatar Sep 07 '25 12:09 paganotoni

right now I'm having a weird issue with firefox on Dell XPS 15: when I open the lid, firefox becomes blurry.

here are my settings:

$main_monitor_id = eDP-1

# tried both
$main_monitor_settings = $main_monitor_id, 3840x2400@60, 0x0, 2.4
# $main_monitor_settings = $main_monitor_id, preferred, auto, 2.4

env = GDK_SCALE,2
monitor = $main_monitor_settings

bindl = , switch:on:Lid Switch, exec, hyprctl keyword monitor "$main_monitor_id, disable"
bindl = , switch:off:Lid Switch, exec, hyprctl keyword monitor "$main_monitor_settings"

if someone knows how to fix this - I'd appreciate your help.

netrusov avatar Sep 09 '25 09:09 netrusov

Thank you @curtisspendlove this is a life saver! (it worked for me inside monitors.conf btw)

Using Lenovo Yoga Pro 7 14ASP9

mikailbayram avatar Sep 28 '25 13:09 mikailbayram

On my Framework 13, the system doesn’t properly distinguish between being docked (connected to an external monitor) and the lid being closed. To work around this, I configured logind to ignore lid events and instead set up my own handling so I can decide whether the laptop should suspend or just disable the internal display when an external monitor is connected.

Here’s what I did in case anyone else runs into the same issue:

  1. Update /etc/systemd/logind.conf with the following:
HandleLidSwitch=ignore
HandleLidSwitchDocked=ignore
  1. Create a scripts directory:
mkdir -p ~/.config/hypr/scripts
  1. Add these two scripts:

~/.config/hypr/scripts/lid-close.sh

#!/usr/bin/env bash
set -euo pipefail

# Adjust this if your internal panel has a different name:
INTERNAL="eDP-1"

# Count connected EXTERNAL connectors (HDMI/DP/USB-C alt-mode). Exclude eDP/LVDS/DSI.
connected_externals() {
  local n=0
  while IFS= read -r status; do
    [[ "$status" == "connected" ]] && n=$((n+1))
  done < <(for s in /sys/class/drm/card*-HDMI-A-*/status \
                   /sys/class/drm/card*-DP-*/status \
                   /sys/class/drm/card*-USB-C-*/status; do
             [[ -e "$s" ]] && cat "$s"
           done 2>/dev/null)
  echo "$n"
}

EXT_N="$(connected_externals)"

if [[ "${EXT_N:-0}" -gt 0 ]]; then
  # External present: turn off internal, keep running on the external
  hyprctl keyword monitor "$INTERNAL,disable" >/dev/null
else
  # No external: suspend the system
  systemctl suspend
fi

~/.config/hypr/scripts/lid-open.sh

#!/usr/bin/env bash
set -euo pipefail

INTERNAL="eDP-1"

# Re-enable the laptop panel with preferred mode, automatic position, scale 2
hyprctl keyword monitor "$INTERNAL,preferred,auto-center-down,2" >/dev/null

Make them executable:

chmod +x ~/.config/hypr/scripts/lid-*.sh
  1. Finally, add these bindings to ~/.config/hypr/monitors.conf:
# On lid CLOSE: run our hybrid handler
bindl=,switch:on:Lid Switch,exec,~/.config/hypr/scripts/lid-close.sh
# On lid OPEN: re-enable the laptop panel
bindl=,switch:off:Lid Switch,exec,~/.config/hypr/scripts/lid-open.sh

ckr avatar Sep 28 '25 15:09 ckr

Using a Thinkpad T480 with a 2560X1440 external monitor, worked perfectly (monitors.conf), including not connecting the display on system restart. Thanks!

Michael-Steshenko avatar Sep 28 '25 23:09 Michael-Steshenko

I ended up doing .sh script that will monitor lid opening/closing and workspace migration from monitor to monitor

lid-monitor.sh

#!/bin/bash
# -----------------------------
# Configuration
# -----------------------------
LAPTOP_MONITOR="eDP-2"
EXTERNAL_MONITOR="HDMI-A-1"
LID_STATE_FILE="/proc/acpi/button/lid/LID0/state"
SLEEP_INTERVAL=1  # seconds between checks
# -----------------------------
# Helper function to check if external monitor is connected
# -----------------------------
is_external_connected() {
    hyprctl monitors | grep -q "$EXTERNAL_MONITOR"
}
get_state() {
    grep -oE 'open|closed' "$LID_STATE_FILE"
}

# Helper function to check if laptop monitor is enabled
is_laptop_enabled() {
    hyprctl monitors -j | jq -r '.[] | select(.name == "'"$LAPTOP_MONITOR"'") | .disabled' | grep -q "false"
}

PREV_STATE=$(get_state)

# -----------------------------
# Initial state check
# -----------------------------
if [[ "$PREV_STATE" == "closed" ]] && is_laptop_enabled; then
    echo "Initial state: Lid is closed and laptop monitor is enabled. Disabling laptop monitor..."
    
    if is_external_connected; then
        # Move workspaces from laptop monitor to external monitor
        hyprctl workspaces -j | jq -r ".[] | select(.monitor == \"$LAPTOP_MONITOR\") | .id" | while read -r ws; do
            hyprctl dispatch moveworkspacetomonitor $ws $EXTERNAL_MONITOR
        done
    fi
    
    # Disable laptop monitor completely
    hyprctl keyword monitor "$LAPTOP_MONITOR,disable"
fi

# -----------------------------
# Infinite loop
# -----------------------------
while true; do
    # Make sure the lid file exists
    if [[ ! -f "$LID_STATE_FILE" ]]; then
        echo "LID state file not found!"
        sleep 5
        continue
    fi
    # Read current lid state
    state=$(get_state)
    if [[ "$state" == "$PREV_STATE" ]]; then
        sleep $SLEEP_INTERVAL
        continue
    fi
    echo "Lid state changed: $PREV_STATE -> $state"
    PREV_STATE=$(get_state)
    
    if [[ "$state" == "closed" ]]; then
        if is_external_connected; then
            # Move workspaces from laptop monitor to external monitor
            hyprctl workspaces -j | jq -r ".[] | select(.monitor == \"$LAPTOP_MONITOR\") | .id" | while read -r ws; do
                hyprctl dispatch moveworkspacetomonitor $ws $EXTERNAL_MONITOR
            done
        fi
        # Disable laptop monitor completely
        hyprctl keyword monitor "$LAPTOP_MONITOR,disable"
        
    elif [[ "$state" == "open" ]]; then
        # Re-enable laptop monitor
        hyprctl keyword monitor "$LAPTOP_MONITOR,preferred,auto,1.25"
        
        if ! is_external_connected; then
            # Move all workspaces back to laptop monitor
            hyprctl workspaces -j | jq -r ".[] | select(.monitor == \"$EXTERNAL_MONITOR\") | .id" | while read -r ws; do
                hyprctl dispatch moveworkspacetomonitor $ws $LAPTOP_MONITOR
            done
        fi
    fi
    
    sleep $SLEEP_INTERVAL
done

and put this in hyrpland.con exec-once = ~/.config/hypr/lid-monitor.sh

geass94 avatar Oct 19 '25 18:10 geass94

@ryanrhughes I can add @geass94 in a PR, do some testing on my own framework 13 and post my progress here?

ElBrodino avatar Oct 30 '25 16:10 ElBrodino

I wish I found this topic earlier!

I went from the opposite by disabling internal monitor and setting up the external with a script just to move workspaces to the external monitor.

Thank you, works fine!

OleksiiPylypcuk-cnic avatar Oct 31 '25 00:10 OleksiiPylypcuk-cnic

@ryanrhughes I can add @geass94 in a PR, do some testing on my own framework 13 and post my progress here?

This needs some kind of auto-detection. My laptop has also Type-C DP available, for second external monitor. That means multiple scenarios can exist. Later I will try to modify this to auto detection and test it with two external monitors. But also that should consider users monitor configuration like refresh rate, scaling etc. from monitors config file, instead of having them hardcoded here.

geass94 avatar Oct 31 '25 06:10 geass94

I applied the suggested bindl lines, but there are cases that cause issues. I have a Framework 13" 2.8k display and an Apple Studio Display:

bindl=,switch:on:Lid Switch,exec,hyprctl keyword monitor "eDP-1, disable" bindl=,switch:off:Lid Switch,exec,hyprctl keyword monitor "eDP-1, preferred, auto, auto"

Problems arise when I suspend the laptop: after an entire night in suspend, and the laptop is disconnected from the external display, Hyprland crashes with a weird screen where it suggests typing Ctrl + Alt + F3 to get a terminal prompt. Gemini suggested a possible cause: _What's happening is a race condition. When you open the lid, two things are happening at almost the same time:

  1. The System Resumes: The laptop's firmware and systemd are waking the system from suspend. This involves re-initializing hardware, including the graphics drivers.
  2. The bindl Fires: Your Hyprland config immediately sees the switch:off signal from the lid and instantly runs hyprctl keyword monitor "eDP-1, preferred, auto, auto".

Hyprland crashes because your bindl command is telling it to reconfigure a monitor before the system's graphics drivers are fully awake and ready to be controlled. It's like trying to talk to someone who is still asleep—you get a crash instead of an answer._

I'm trying addind a delay on wake up:

bindl=,switch:off:Lid Switch,exec,sh -c 'sleep 1 && hyprctl keyword monitor "eDP-1, preferred, auto, auto"'

This is just a workaround, though. Solving this lid-closing problem is more complex than expected. @curtisspendlove , did you experience anything like that?

massimosgrelli avatar Oct 31 '25 10:10 massimosgrelli

Now that there is no turning back for me, I experimented a bit with my displays. Here is what I have (found) beyond the lid close switches off display.

My hardware is:

 PC: XPS 13 9310
│ ├: Intel Iris Xe Graphics @ 1.35 GHz [Integrated]
│ ├󱄄: 3840x2160 @ 60 Hz (as 1920x1080) in 27" [External]
│ ├󱄄: 3456x2160 @ 60 Hz (as 1297x811) in 13" [Built-in]

Using auto might have surprising effects when you close and open the lid.

After opening the lid, eDP-1 appeared right of DP-7, when it was left of it before. This is likely normal behavior, but might still be surprising.

The following hyprland config (getting myself some new glasses today):

env = GDK_SCALE, 2
monitor = eDP-1, preferred, auto-left, 2.666667
monitor = DP-7, preferred, auto, 2

bindl=,switch:on:Lid Switch,exec,hyprctl keyword monitor "eDP-1, disable"
bindl=,switch:off:Lid Switch,exec,hyprctl keyword monitor "eDP-1, preferred, auto-left, 2.666667"

Keeps positioning and resolution stable for me and things a pleasure to read.

This worked perfectly on my Thinkpad X1 Carbon 13th Gen! Thank you!

cutzenfriend avatar Nov 27 '25 08:11 cutzenfriend