====== Screen Toggle (Fn+F8) ======
The screen toggle functionality can be accessed by pressing the function key and F8. In [[wp>Windows]] this works "out-of-the-box", but in [[wp>Linux]] this turns out to be more of a problem. However, since you will have to configure every step of the process, you get much more control over what happens and can customize it to your liking. After you [[setup:misc#acpi|installed ACPI]] and got it to react to the [[setup:graphics:metakeys#setup|proper keys]] it is time to encapsulate the functionality in a ''screen_toggle.sh'' script. The script will rely on [[http://www.thinkwiki.org/wiki/Xorg_RandR_1.2|XRandR]] to do the actual screen switching and "all" it needs to do is keep track of the current setting and calling ''xrandr'' with the proper settings. However, nothing is easy when you have two GPUs in your netbook. While the Intel chipset works nicely with ''xrandr'' and you can see all connected devices as separate devices((Please be advised that only the [[wp>D-Sub]] port is available for the Intel GPU. The [[wp>HDMI]] port can be accessed only by the nVidia card.)), the binary driver from nVidia likes to keep things under wraps and therefore does not expose [[wp>TwinView]] settings to ''xrandr''. This forces you to have two separate ''screen_toggle.sh'' scripts and use each with its card. Once you have these two scripts you simply need to call them at the appropriate place in the ''asus.sh'' [[setup:graphics:metakeys#setup|ACPI file]].
===== Common Setup =====
Before you get to create these files though you need to make sure ''xrandr'' works when executed in a script, which was called from a non-interactive shell. You see ''xrandr'' depends on two global variables, ''XAUTHORITY'' pointing to a valid ''.Xauthority'' file and ''DISPLAY'' telling it which is the active display (usually '':0.0''), to work. These variables exist and are set in a normal shell within the X window system, however, the ACPI environment lacks them.
In order to solve this problem you need a ''xrandr_setup.sh'' script which, inelegantly, sets those variables within any script that includes it((Note that you will be **including** this file via the ''source'' command and not calling/executing the script - the execution would not set the variables properly.)). The values of both variables can be determined by querying the ''who'' command which lists all users that are currently logged in. The list will contain their user names and the displays that are active. A creative application of ''grep'' and ''sed'' yields the correct results.
#!/bin/bash
# /usr/local/src/xrandr_setup.sh
# Sets up the USER and DISPLAY variables to be used by xrandr
CURRENT_USER=$(who -s | grep "(:[0-9]\W" | head -1 | sed "s/ .*//g")
CURRENT_DISPLAY=$(who -s | grep "(:[0-9]" | head -1 | sed "s/.*(//g" | sed "s/)//g")
# Set up xrandr
export XAUTHORITY="/home/${CURRENT_USER}/.Xauthority"
export DISPLAY=${CURRENT_DISPLAY} #":0.0"
The ''.Xauthority'' is located in the user's home directory. Unfortunately, while most "normal" users' home is located in ''/home//'', the root's home directory is simply ''/root/''. To solve this discrepancy without a lot of ''if''/''else''-statements in the script file create a symbolic link named ''/home/root'' which will point to ''/root'':
ln -s /root/ /home/root
The best to way to handle the two different ''screen_toggle.sh'' scripts for the two GPUs is also via a symbolic link. ''asus.sh'' should always call ''/usr/local/src/screen_toggle.sh'' which will be symbolic link to either ''intel_screen_toggle.sh'' or ''nvidia_screen_toggle.sh''. Of course you can set the link the same way you set the ''xorg.conf'' symbolic link from the [[setup:graphics:xorg|X-server setup]] FIXME! (that actually does not have the symbolic link yet), i.e. in the ''init'' of the ''initramfs'' (detailed [[setup:misc:initramfs|here]]).
The script's general idea is as follows:
- Set up ''xrandr'' via ''xrandr_setup.sh''.
- Determine the internal and external states.
- Select one of four configuration states (''internal'', ''external'', ''twin'', ''clone'') based on the internal and external states.
- Call one of five functions that switch states (one for each configuration state plus a ''toggle()'' function) according to the parameter that was passed in.
The changes between the Intel and nVidia versions of the script have to do with detecting the internal and external states and switching between states.
===== Intel Screen Toggle =====
The Intel script is the "correct" one since ''xrandr'' can nicely detect the connected displays:
Screen 0: minimum 320 x 200, current 1024 x 600, maximum 1920 x 1800
VGA connected (normal left inverted right x axis y axis)
1920x1200 60.0 +
1680x1050 60.0
1400x1050 60.0 60.0
1280x1024 60.0 60.0
1440x900 60.0
1280x960 60.0 60.0
1024x768 60.0
800x600 60.3 56.2
640x480 60.0 59.9
LVDS connected 1024x600+0+0 (normal left inverted right x axis y axis) 222mm x 130mm
1024x600 60.0*+
800x600 85.1 72.2 75.0 60.3 56.2
640x480 85.0 72.8 75.0 59.9
720x400 85.0
640x400 85.1
640x350 85.1
You can then simply query ''xrandr'' to find out which monitors are connected (i.e. listed) and on (containing a ''*'' next to the resolution in use). Similarly in order to switch between the monitors you simply tell ''xrandr'' which one (''--output VGA'' or ''LVDS'') should do what (''--auto'' or a specific resolution). The clone configuration is achieved by turning both outputs on and passing ''--same-as LVDS'' to the external output. The twin configuration needs to know the position of the two outputs, in this case use ''--above LVDS''. While ''--left-of LVDS'' would be logical, the ''Virtual'' parameter in Intel's ''xorg.conf'' (defined [[setup:graphics:xorg#external_monitor|here]] FIXME! make link directly to nvidia conf) unfortunately must not be greater than 2048x2048, otherwise [[wp>DRI]] will not work on the Intel 945GME chipset. Since I am using a 24" monitor with a resolution of 1920x1200, having both monitors next to each other would require a larger-than-allowed virtual desktop width (1024+1920>2048). However, when the two desktop heights are added, the limit can be still met (600+1200<2048).
#!/bin/bash
# /usr/local/src/intel_screen_toggle.sh
# Toggles between internal, external, twin, and clone screen setups for the Intel GPU
# Set up xrandr
source /usr/local/src/xrandr_setup.sh
# Output may be "LVDS" or "VGA"
INTERNAL_OUTPUT="LVDS"
EXTERNAL_OUTPUT="VGA"
CLONE_FILE=/tmp/.clone
# Set the position for twin view
DO=${1}
if [[ "${DO}" == "twin" ]]; then
EXTERNAL_LOCATION=${2}
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}"
;;
*)
# Default has to be above because the max. virtual desktop size for Intel 945GME is 2048x2048 and 1920+1024>2048, but 1200+600<2048
EXTERNAL_LOCATION="--above ${INTERNAL_OUTPUT}"
;;
esac
# Figure out the current state
INTERNAL_STATE=$( xrandr | grep "^${INTERNAL_OUTPUT}" | grep "con" | sed "s/.*connected //" | sed "s/[+(].*//g")
EXTERNAL_STATE=$( xrandr | grep "^${EXTERNAL_OUTPUT}" | grep "con" | sed "s/.*connected //" | sed "s/[+(].*//g")
if [[ -z "${INTERNAL_STATE}" ]]; then
STATE="external"
elif [[ -z "${EXTERNAL_STATE}" ]]; then
STATE="internal"
else
if [[ -f ${CLONE_FILE} ]]; then
STATE="clone"
else
STATE="twin"
fi
fi
# External screen only
function screen_external()
{
xrandr --output ${INTERNAL_OUTPUT} --off
xrandr --output ${EXTERNAL_OUTPUT} --auto
rm -f ${CLONE_FILE}
}
# Internal screen only
function screen_internal()
{
xrandr --output ${INTERNAL_OUTPUT} --auto
xrandr --output ${EXTERNAL_OUTPUT} --off
rm -f ${CLONE_FILE}
}
# Clone view
function screen_clone()
{
xrandr --output ${INTERNAL_OUTPUT} --auto --output ${EXTERNAL_OUTPUT} --auto --same-as ${INTERNAL_OUTPUT}
touch ${CLONE_FILE}
}
# Twin view
function screen_twin()
{
xrandr --output ${INTERNAL_OUTPUT} --auto --output ${EXTERNAL_OUTPUT} --auto ${EXTERNAL_LOCATION}
rm -f ${CLONE_FILE}
}
# Toggle function
function screen_toggle()
{
case "${STATE}" in
internal)
screen_clone
;;
clone)
screen_twin
;;
twin)
screen_external
;;
external)
screen_internal
;;
*)
screen_internal
;;
esac
}
# What should we do?
if [[ -z "${DO}" ]]; then
if [[ $(basename ${0}) == "intel_screen_toggle.sh" ]]; then
DO="toggle"
fi
fi
case "${DO}" in
toggle)
screen_toggle
;;
internal)
screen_internal
;;
external)
screen_external
;;
clone)
screen_clone
;;
twin)
screen_twin
;;
state)
echo "${STATE}"
;;
status)
echo "Current Fn+F8 state is: ${STATE}"
echo
echo "Attached monitors:"
xrandr | grep "\Wconnected" | sed "s/^/ / "
;;
*)
echo "Usage: ${0} " >&2
echo >&2
echo " Commands:" >&2
echo " state" >&2
echo " status" >&2
echo " internal" >&2
echo " external" >&2
echo " clone" >&2
echo " twin" >&2
echo " toggle" >&2
echo >&2
;;
esac
===== nVidia Screen Toggle =====
nVidia's script is similar to Intel's, but it supports only three configurations: internal, external, and clone. Because the GPU's binary driver manages all TwinView states you can switch only between the ones it provides ... and the twin state as known from the Intel chipset is not among them. This is also the reason why ''xrandr'' does not list multiple outputs when the nVidia GPU is enabled:
Screen 0: minimum 1024 x 600, current 1024 x 600, maximum 1920 x 1200
default connected 1024x600+0+0 0mm x 0mm
1024x600 50.0*
1920x1200 51.0 52.0 50.0
You will not get the driver and ''xrandr'' to communicate until nVidia decides to incorporate ''xrandr'' in their driver, so you will have to do with a work around for now. The trick is to allow all configurations in the ''xorg.conf'' as ''metamodes'' (as you did [[setup:graphics:xorg#configuration|here]]) FIXME! make link directly to nvidia conf. ''xrandr'' will then be able to differentiate between the settings based on inaccurate frame rate values. As you could see in ''xrandr'''s output above, there are three frame rates, 50.0, 51.0, and 52.0. They all correspond to a different configurations, based on the ''xorg.conf''.
Option "metamodes" "DFP-0: 1024x600_60, DFP-1: NULL; DFP-0: NULL, DFP-1: nvidia-auto-select; DFP-0: 1024x600_60, DFP-1: nvidia-auto-select"
# translates to xrandr's 50.0 51.0 52.0
In order to switch between the configurations simply ask ''xrandr'' to select the appropriate refresh rate via ''-r -s ''. As you have no doubt realized, some refresh rates are assigned to both resolutions, which is why you also need to provide the size (''-s'') parameter. This is of course not ideal because it requires you to hard code the resolution of your external screen into the script. FIXME! add logic to script to remove this Similary this whole process alters the way the configuration states are ascertained, hence the separate script file.
#!/bin/bash
# /usr/local/src/nvidia_screen_toggle.sh
# Toggles between internal, external, and clone screen setups for the nVidia GPU
# Set up xrandr
source /usr/local/src/xrandr_setup.sh
# Set up xrandr constants
INTERNAL=50.0
EXTERNAL=51.0
CLONE=52.0
INTERNAL_RESOLUTION="1024x600"
EXTERNAL_RESOLUTION="1920x1200"
DO=${1}
# Figure out the current state
CURRENT_STATE=$( xrandr | grep "\*" | sed "s/.* \([^*]\{1,\}\)\*.*/\1/" )
if [[ ${CURRENT_STATE} == ${EXTERNAL} ]]; then
STATE="external"
elif [[ ${CURRENT_STATE} == ${INTERNAL} ]]; then
STATE="internal"
else
STATE="clone"
fi
# External screen only
function screen_external()
{
xrandr -r ${EXTERNAL} -s ${EXTERNAL_RESOLUTION}
}
# Internal screen only
function screen_internal()
{
xrandr -r ${INTERNAL} -s ${INTERNAL_RESOLUTION}
}
# Clone view
function screen_clone()
{
xrandr -r ${CLONE} -s ${EXTERNAL_RESOLUTION}
}
# Toggle function
function screen_toggle()
{
case "${STATE}" in
internal)
screen_clone
;;
clone)
screen_external
;;
external)
screen_internal
;;
*)
screen_internal
;;
esac
}
# What should we do?
if [[ -z "${DO}" ]]; then
if [[ $(basename ${0}) == "nvidia_screen_toggle.sh" ]]; then
DO="toggle"
fi
fi
case "${DO}" in
toggle)
screen_toggle
;;
internal)
screen_internal
;;
external)
screen_external
;;
clone)
screen_clone
;;
state)
echo "${STATE}"
;;
status)
echo "Current Fn+F8 state is: ${STATE}"
echo
echo "Attached monitors:"
xrandr
;;
*)
echo "Usage: ${0} " >&2
echo >&2
echo " Commands:" >&2
echo " state" >&2
echo " status" >&2
echo " internal" >&2
echo " external" >&2
echo " clone" >&2
echo " toggle" >&2
echo >&2
;;
esac