Sunday, December 9, 2007

95. switching monitors

2007-12-01
95. It works!

Web page:
http://www.thinkwiki.org/wiki/Sample_Fn-F7_script

a. configure virtual screen size
Add a "Virtual" statement to your /etc/X11/xorg.conf, the total resolution should be large enough to fit all your screens
in the configuration you want, for example if you have 1600x1200 monitor to the left of your internal 1024x768 monitor,
your total max resolution is 2624x1200

Section "Screen"
Identifier "Screen0"
Device "Videocard0"
DefaultDepth 24
SubSection "Display"
Viewport 0 0
Depth 24
Virtual 2624 1200
EndSubSection
EndSection

b. configure acpi
Modify:
/etc/acpi/events/ibm-videobtn

# /etc/acpi/events/ibm-videobtn
# This is called when the user presses the video button. It is currently
# a placeholder.
event=ibm/hotkey HKEY 00000080 00001007
action=/usr/local/sbin/thinkpad-fn-f7
# action=/bin/true

c. You may need to enable ibm-hotkeys
/etc/rc.local
#!/bin/bash
# enable ibm-hotkeys (specifically Fn2, Fn7)
# 12 bit mask, little end is F1 default 0x080c = F12+F4+F3
echo enable,0x084e > /proc/acpi/ibm/hotkey

and after suspending to disk RAM
/etc/acpi/resume.d/91-ibm-hotkey-enable.sh

#!/bin/bash
# enable ibm-hotkeys (specifically Fn2, Fn7)
# 12 bit mask, little end is F1 default 0x080c = F12+F4+F3
echo enable,0x084e > /proc/acpi/ibm/hotkey

chmod 755 91-ibm-hotkey-enable.sh

d. identify output devices

Note the names of your output devices as you will have to change EXTERNAL_OUTPUT and INTERNAL_OUTPUT
to what xrandr shows, for example VGA and LVDS in this case:

$ xrandr -q
VGA connected 1600x1200+0+0 (normal left inverted right x axis y axis) 432mm x 324mm
...
LVDS connected (normal left inverted right x axis y axis)

e. create the bash script

Create /usr/local/sbin/thinkpad-fn-f7, you can set EXTERNAL_LOCATION to one of: left, right, above, or below.

#!/bin/bash

# External output may be "VGA" or "VGA-0" or "DVI-0"
EXTERNAL_OUTPUT="VGA"
INTERNAL_OUTPUT="LVDS"
EXTERNAL_LOCATION="left"

# Figure out which user and X11 display to work on
# TODO there has to be a better way to do this?
X_USER=$(w -h -s | grep ":[0-9]" | head -1 | awk '{print $1}')
export DISPLAY=$(w -h -s | grep ":[0-9]" | head -1 | awk '{print $3}')

# Switch to X user if necessary
if [ "$X_USER" != "$USER" ]; then
SU="su $X_USER -c"
fi

case "$EXTERNAL_LOCATION" in
left|LEFT)
EXTERNAL_LOCATION="--left-of $INTERNAL_OUTPUT"
;;
right|RIGHT)
EXTERNAL_LOCATION="--right-of $INTERNAL_OUTPUT"
;;
top|TOP|above|ABOVE)
EXTERNAL_LOCATION="--above $INTERNAL_OUTPUT"
;;
bottom|BOTTOM|below|BELOW)
EXTERNAL_LOCATION="--below $INTERNAL_OUTPUT"
;;
*)
EXTERNAL_LOCATION="--left-of $INTERNAL_OUTPUT"
;;
esac

# Figure out current state
INTERNAL_STATE=$($SU xrandr | grep ^$INTERNAL_OUTPUT | grep con | sed "s/.*connected //" | sed "s/(.*//")
EXTERNAL_STATE=$($SU xrandr | grep ^$EXTERNAL_OUTPUT | grep con | sed "s/.*connected //" | sed "s/(.*//")

if [ -z "$INTERNAL_STATE" ]; then
STATE="external"
elif [ -z "$EXTERNAL_STATE" ]; then
STATE="internal"
else
INTERNAL_STATE=$(echo $INTERNAL_STATE | sed "s/[0-9]*x[0-9]*//")
EXTERNAL_STATE=$(echo $EXTERNAL_STATE | sed "s/[0-9]*x[0-9]*//")
if [ "$INTERNAL_STATE" = "$EXTERNAL_STATE" ]; then
STATE="mirror"
else
STATE="both"
fi
fi

function screen_external(){
$SU "xrandr --output $INTERNAL_OUTPUT --off"
$SU "xrandr --output $EXTERNAL_OUTPUT --auto"
}

function screen_internal(){
$SU "xrandr --output $EXTERNAL_OUTPUT --off"
$SU "xrandr --output $INTERNAL_OUTPUT --auto"
}

function screen_mirror(){
$SU "xrandr --output $INTERNAL_OUTPUT --auto"
$SU "xrandr --output $EXTERNAL_OUTPUT --auto --same-as $INTERNAL_OUTPUT"
}

function screen_both(){
$SU "xrandr --output $INTERNAL_OUTPUT --auto"
$SU "xrandr --output $EXTERNAL_OUTPUT --auto $EXTERNAL_LOCATION"
}

function screen_toggle(){
case "$STATE" in
internal)
screen_mirror
;;
mirror)
screen_external
;;
external)
screen_both
;;
both)
screen_internal
;;
*)
screen_internal
;;
esac
}

# What should we do?
DO="$1"
if [ -z "$DO" ]; then
if [ $(basename $0) = "thinkpad-fn-f7" ]; then
DO="toggle"
fi
fi

case "$DO" in
toggle)
screen_toggle
;;
internal)
screen_internal
;;
external)
screen_external
;;
mirror)
screen_mirror
;;
both)
screen_both
;;
status)
echo "Current Fn-F7 state is: $STATE"
echo
echo "Attached monitors:"
$SU xrandr | grep "\Wconnected" | sed "s/^/ /"
;;
*)
echo "usage: $0 " >&2
echo >&2
echo " commands:" >&2
echo " status" >&2
echo " internal" >&2
echo " external" >&2
echo " mirror" >&2
echo " both" >&2
echo " toggle" >&2
echo >&2
;;
esac

chmod 755 thinkpad-fn-f7

$ sudo chmod 755 /usr/local/sbin/thinkpad-fn-f7
$ sudo service acpid restart
OR
$ sudo /etc/init.d/acpid restart

3 comments:

Magda said...

Actually it doesn't work on Ubuntu 8.04. The bigger monitor doesn't know the resolution and it detects 800x600. So practically you cannot work on the bigger monitor.

Magda said...

Try to add in /etc/modules acpi support:

in /etc/modules
#Add this line, to enable some thinkpad features
ibm_acpi

http://www.klabs.be/~fpiat/linux/debian/Etch_on_Thinkpad_X31.html

Now at least when you boot the computer with your second monitor connected to the laptop you can see the right something on your secondary (non-laptop) monitor

Magda said...

Since my secondary monitor supports 1280x1024 then I needed to add a line in my /etc/X11/xorg.conf

SubSection "Display"
Depth 24
# I added one more mode 1280x1024
# that I defined below
Modes "1280x1024" "1024x768@60" "800x600@60" "800x600@56" "640x480@60"
EndSubSection

Section "monitor" #
Identifier "monitor1"
Vendorname "Generic LCD Display"
Modelname "LCD Panel 1024x768"
Horizsync 31.5-48.0
Vertrefresh 56.0 - 65.0
modeline "640x480@60" 25.2 640 656 752 800 480 490 492 525 -vsync -hsync
modeline "800x600@56" 36.0 800 824 896 1024 600 601 603 625 +hsync +vsync
modeline "800x600@60" 40.0 800 840 968 1056 600 601 605 628 +hsync +vsync
modeline "1024x768@60" 65.0 1024 1048 1184 1344 768 771 777 806 -vsync -hsync
# I added this line (copied from my
# standalone Ubuntu workstation
# after obtaining from the xvidtune program
modeline "1280x1024" 135.00 1280 1296 1440 1688 1024 1025 1028 1066 +hsync +vsync
Gamma 1.0
EndSection

Now I have the right resolution although my original laptop monitor is blank.