12

I am trying to run a script located in usr/local/bin/ when I connect an external monitor to my laptop. I have tried to add a new udev rule but that did not work. I created a new file in /etc/udev/rules.d called vga-monitor-connect.rules. The contents of the file were

SUBSYSTEM=="drm", ACTION=="change", RUN+="/usr/local/bin/panel-fix"

I took the line from this answer

After searching online I also tried the following rule

KERNEL=="card0", SUBSYSTEM=="drm", ENV{DISPLAY}=":0", ENV{XAUTHORITY}="/home/rumesh/.Xauthority", RUN+="/usr/local/bin/panel-fix"

However this didn't work either.

I have run the script manually and I can confirm that it works so it is not a problem with my script.

I also want to make it clear that I do not know much about udev so the rule I have used may be wrong. If anyone knows the proper rule for my problem please leave an answer.

My graphics card is an Intel GM965 integrated chipset

  • Do you specifically want to do it like this? A tiny background script would be a piece of cake. – Jacob Vlijm May 30 '15 at 15:36
  • @JacobVlijm How would the script be? Could you show an example. – Rumesh May 30 '15 at 15:44
  • Is it only to trigger if (any) second monitor is attached? – Jacob Vlijm May 30 '15 at 15:46
  • Yes I need to run my own script when I attach the second monitor – Rumesh May 30 '15 at 15:47
  • 1
    No problem, take your time and answer when you can :) – Rumesh May 30 '15 at 15:51
7

An alternative way to run a command if a screen is connected or disconnected

An alternative solution would be to run a tiny background script. Running the script below in the background, I could not measure any increase in processor load whatsoever.

It is an easy an convenient way to run a script, or any other command, whenever a second screen is connected or disconnected.

The example script

  • Simply checks every five seconds how many times the string " connected " occurs in the output of the command xrandr (mind the space after "connected" to prevent false matches with "disconnected"). Each occurrence represents a connected screen.
  • If the number of occurrences changes, either a screen was connected or disconnected. The change is "noticed" by the script and can be connected to a command, you can set in the head section of the script.

The script

#!/usr/bin/env python3
import subprocess
import time

#--- set both commands (connect / disconnect) below
connect_command = "gedit"
disconnect_command = ""
#---

def get(cmd): return subprocess.check_output(cmd).decode("utf-8")
# - to count the occurrenc of " connected "
def count_screens(xr): return xr.count(" connected ")
# - to run the connect / disconnect command(s)
def run_command(cmd): subprocess.Popen(["/bin/bash", "-c", cmd])

# first count
xr1 = None

while True:
    time.sleep(5)
    # second count
    xr2 = count_screens(get(["xrandr"]))
    # check if there is a change in the screen state
    if xr2 != xr1:
        print("change")
        if xr2 == 2:

            # command to run if connected (two screens)
            run_command(connect_command)
        elif xr2 == 1:
            # command to run if disconnected (one screen)
            # uncomment run_command(disconnect_command) to enable, then also comment out pass
            pass
            # run_command(disconnect_command)
    # set the second count as initial state for the next loop
    xr1 = xr2

How to use

  1. Copy the script into an empty file, save it as connect_screen.py
  2. In the head section, set the command to run on connect ( I set "gedit" as an example, mind the quotes). Also it is possible to set a command on disconnect, likewise. Else leave disconnect_command = "" as it is.

    If you do use a disconnect- command, also uncomment the line:

    run_command(disconnect_command)
    

    and comment out the line:

    pass
    

    As indicated in the script

  3. Test-run the script from a terminal, connect your screen and see if all works fine.
  4. If all works fine, add it to your startup applications: Dash > Startup Applications > Add the command:

    /bin/bash -c "sleep 15&&python3 /path/to/connect_screen.py"
    

    The sleep 15 is to make the desktop start up completely before the script starts to run. Just to make sure.


EDIT

How to run the script on start up in a "smart" way.

The break of sleep 15 should work in general, but since the start up time differs per system, It might take some experimenting to find the right break time. With a small addition, the script becomes "smart", and waits for the xrandr command to be successful before it starts the actual script. If you use the version below, you only need to add the command:

python3 /path/to/connect_screen.py

to your Startup Applications. Further usage is exactly the same as the version above.

The script

#!/usr/bin/env python3
import subprocess
import time

#--- set both commands (connect / disconnect) below
connect_command = "gedit"
disconnect_command = ""
#---

while True:
    time.sleep(5)
    try:
        subprocess.Popen(["xrandr"])
    except:
        pass
    else:
        break


# function to get the output of xrandr
def get(cmd): return subprocess.check_output(cmd).decode("utf-8")
# - to count the occurrenc of " connected "
def count_screens(xr): return xr.count(" connected ")
# - to run the connect / disconnect command(s)
def run_command(cmd): subprocess.Popen(["/bin/bash", "-c", cmd])

# first count
xr1 = None

while True:
    time.sleep(5)
    # second count
    xr2 = count_screens(get(["xrandr"]))
    # check if there is a change in the screen state
    if xr2 != xr1:
        if xr2 == 2:
            # command to run if connected (two screens)
            run_command(connect_command)
        elif xr2 == 1:
            # command to run if disconnected (one screen)
            # uncomment run_command(disconnect_command) to enable, then also comment out pass
            pass
            # run_command(disconnect_command)
    # set the second count as initial state for the next loop
    xr1 = xr2
  • 4
    You are giving a bicycle to a man with a broken fast car instead of fixing the car... – solsTiCe May 30 '15 at 20:29
  • 1
    @solsTiCe 1. This is no bicycle, but a perfectly functional option. Keep in mind that all notify-, event- or whatever driven actions exist of running some kind of a loop somehow. 2. I suggest you fix the Ferrari then. – Jacob Vlijm May 30 '15 at 20:38
  • 1
    @Rumesh running scripts in $PATH with file extension, and with preceding language is a not-so-elegant solution. The question is if you want to run it for one user or for all users. In the last case, another setup than the suggested is needed, but preferably not the one you are suggesting :). The sleep 15 might not be sufficient, but a more elegant solution could be to make the break "smart", let the script try/except to start up until the startup procedure is "ready" for the script to run. WOuld be a minor addition. Let me know. – Jacob Vlijm Jun 6 '15 at 16:54
  • 1
    @Rumesh scripts in $PATH should not have language extension and be executable, so run without python3 see lintian.debian.org/tags/script-with-language-extension.html – Jacob Vlijm Jun 6 '15 at 16:56
  • 1
    @JacobVlijm I had already set it to be executable before so I guess I can just use connect_screen – Rumesh Jun 7 '15 at 7:34
2

This can be achieved from following bash script as well.

#!/usr/bin/env bash

xrandr=$(xrandr)

con_monitors=$(echo $xrandr | grep -c " connected ")

    if [[ $con_monitors -gt 1 ]]; then
        # All the layouts are saved in "screenlayout" folder.
        # eg cmd. xrandr --output HDMI-1 --mode 2560x1440 --pos 0x0 --rotate normal --output DP-1 --off --output eDP-1 --primary --mode 1920x1080 --pos 283x1440 --rotate normal --output DP-2 --off
        for layout in ~/.screenlayout/*.sh; do
            ./layout
        done
    fi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.