삽질한 계기

현재 개발용 운영체제로 Linux를 사용하고 있다. Linux를 개발용으로 사용한 이용한 이유는 좀 많은데 간단하게 말해서 WSL의 내부에서 가상화를 지원하지 않기 때문이었다. 이 참에 리눅스 공부도 할 겸 리눅스를 사용해 보겠다는 당찬 포부와 함께 윈도우를 버리고 리눅스로 완전히 옮기려 했으나 MS와 한글의 사실상 표준 때문에 완전히 옮기진 못하고 듀얼부팅으로 사용하고 있다. 리눅스 배포판은 처음엔 대부분이 그렇듯이 우분투로 시작했고, 지금은 Arch linux에 정착해 생활하고 있다. Arch를 사용하는 이유도 존재하는데 이는 나중에 설명하는게 좋을 것 같다.

어찌됬든 Linux를 사용하게 되면 Windows에서는 느낄 수 없는 다양한 커스터마이징과 화를 돋구는 프로세스가 없는 등 여러 장점들이 존재하지만 한글 폰트, 오픈소스 드라이버 문제(특히 블루투스, 그래픽), 여러가지 기본적 기능 부재 등 여러가지 단점들도 존재한다. 오늘은 리눅스의 단점들중 하나를 극복한 방법을 작성하고자 한다.

문제

나는 사실 위의 단점들에 더해서 족쇄를 하나 더 달고 Linux를 사용하고 있는데 그것은 내가 dwm이라는 windows manager를 사용하고 있다는 것이다. Windows manager은 그 말대로 창을 관리해 주는 프로그램이다. 대부분 gnome같은 windows manager를 사용하고 있지만 나는 dwm이라는 가벼운 tiling wm을 사용하고 있어서 많은 기능을 서드파티와 직접 구현에 의존한다.
그 많은 기능들 중 하나에는 자동으로 화면을 연결하는 기능이었다. (autorandr같은 프로그램도 있지 않느냐 할 수 있지만 잘 동작하지 않았다🥲) 그래서 그 기능을 직접 구현해 보기로 했다.

해결 과정

프로그램 버전

Kernel: 6.3.9-arch1-1
RandR: 1.6
Xrandr: 1.5.2
feh: 3.1.0

현재 내가 사용하고 있는 환경이다. 아마 버전은 크게 상관 없을것 같다.


리눅스에서는 udev라는 장치 관리자가 존재한다. Udev rules를 설정하면 장치의 여러가지 상태에 따라 event를 발생시켜 원하는 동작을 수행하게 할 수 있다.
Udev rules는 /etc/udev/rules.d (administrator directory)에 존재하며 해당 내용에 따라 원하는 내용을 실행할 수 있다. Udev rules내부에 들어가는 값을 몇 가지만 간단히 설명하면 다음과 같다.

ACTION: event의 행위에 해당한다.
KERNEL: device의 이름에 해당한다.
SUBSYSTEM: device의 subsystem에 해당한다.
ENV{key}: 환경 변수를 설정한다. 후에 RUN을 통해 프로세스를 실행할 때의 환경변수를 말한다.
RUN: 실행시킬 command를 입력한다.

어디에 적을지 알았으니 이제 적을 내용을 찾으면 된다. 문제를 해결하기 위해선 어떤 모니터를 연결하고 해제할 때 어떤 커널에서 어떤 이벤트가 발생하는지 알아야 한다.
리눅스에서는 udevadm이라는 udev에 대한 다양한 기능을 제공하는 command가 존재한다. 이 중 monitor라는 기능을 사용하면 어떤 이벤트가 발생하는지 알 수 있는데 이것을 힌트로 rule을 작성하면 된다.

udevadm monitor -u

출력된 값들을 보면 remove, unbind, add, change 등이 ACTION이고, 그 뒤에 나오는 것이 device 경로이다.
이제 이벤트도 알았으니 해당 커널명을 알아야 한다. udev info {deivce 경로}을 입력하면 해당 device가 가지는 KERNEL명과 SUBSYSTEM 그리고 ATTR값 등을 알 수 있다. device 경로가 dev/dri/card0로 나올 텐데 모니터의 경우 각 디바이스 파일이 존재하지 않기 때문에 정상적인 결과이다.

udev info /dev/dri/card0

이제 필요한 값은 모두 알았으니 udev rule을 작성할 차례이다. 원하는 스크립트만 RUN에 추가해 주면 되는데 주의할 점이 있다. x server에 접근할 경우 특정 환경변수가 필요하고 .Xauthority가 필요하다는 것이다.

.Xauthority: x server에 접근하기 위한 crendential cookie

작성된 코드는 다음과 같다. /etc/udev/rules.d에 rule을 작성하면 되고, 번호-rule명.rules로 파일이름을 정하면 된다. 번호는 앞에 필요한 rule이 존재할 수 있으므로 뒷번호를 쓰는 것을 추천한다. (나는 99번을 썼다)

ACTION=="change", SUBSYSTEM=="drm", ENV{DISPLAY}=":0", ENV{XAUTHORITY}=".Xauthority 경로", ENV{SHELL}="/bin/bash", ENV{USER}="유저 명", ENV{USERNAME}="유저 명", ENV{HOME}="HOME 위치", ENV{PATH}="PATH 경로들", RUN+="/bin/bash -c script경로"

해당 코드는 자동으로 화면을 설정하는 코드인데 원하는대로 충분히 변경하면 된다. 나는 화면을 하나밖에 더 안써서 다음과 같이 작성했다. 작성할 때 유의할 점은 반드시 xrandr뒤에 &&을 넣어야 한다는 것이다. 안그러면 xrandr가 화면을 잡기 전에 배경화면을 설정해서 배경화면이 설정이 안된다.

export PRIMARY_DISPLAY="eDP1"  &&

CONNECTED_DISPLAYS=($(xrandr | xrandr | awk '{if ($2=="connected") print $1}')) &&
DISCONNECTED_DISPLAYS=($(xrandr | xrandr | awk '{if ($2=="disconnected") print $1}')) &&
if [[ ${#CONNECTED_DISPLAYS[@]} > 1 ]]
then
	SECONDARY_DISPLAY=${CONNECTED_DISPLAYS[1]}
	xrandr --output $PRIMARY_DISPLAY --primary --mode 1920x1080 --pos 0x0 --rotate normal --output $SECONDARY_DISPLAY --mode 1920x1080 --pos 1920x212 --rotate left
else
	xrandr --output DP1 --off --output DP2 --off --output HDMI1 --off --output HDMI2 --off --output VIRTUAL1 --off
fi &&
feh --bg-fill /usr/share/backgrounds/배경화면.png &

이제 화면을 연결하면 자동으로 화면이 잡히게 되었다!