Using Multiple Screens

People often ask how to configure X to use multiple monitors on awesome IRC channel. So here is a brief document that covers a few different drivers and methods of achieving that on GNU/Linux.

If you are interested in how to utilize multiple monitors after you have set them up then you can take a look at awesome 3 key bindings (look for ones that deal with screens, you can also do man awesome from a terminal) and if you want to control widgets (such as the system tray) for each screen then you may want to look at the Widgets in awesome page, especially the controlling widgets section

Basics
The easiest way is by using XRandR (X Resize and Rotate) extension which allows dynamic control over our outputs, resolutions, orientation... and adding new displays on-the-fly without reseting the X server. But not all drivers support it yet. Ones that do are: intel, ati (OpenSource driver), radeonhd (OpenSource driver), nv (nvidia 2D driver) and nouveau (OpenSource nvidia driver). I will also cover the nvidia driver, as that is probably the most common driver people use, using both TwinView and Xinerama (which can be used with other drivers too, i.e. fglrx).

Besides the fact that awesome has: Real multihead support (XRandR, Xinerama or Zaphod mode) with per screen desktops (tags); Awesome is distributed with a sample configuration file (/etc/xdg/awesome/rc.lua with v3.0 and above) which is already setup for multiple displays; regarding wiboxes, taskbars and widgets but also has keybindings which allow you to move clients between multiple screens and switch focus between them.

XRandR
First I will show you a few examples of using xrandr for dynamic and on-the-fly setup and (re)configuration.

With the first command we will query our hardware: $ xrandr -q

After checking the output, let's suppose that you have a laptop which panel is LVDS and an external VGA port which we will regard as VGA, we execute: $ xrandr --output VGA --mode 1280x1024 --right-of LVDS The above command is straightforward, your VGA monitor is initialised using 1280x1024 resolution and it's placement is Right Of your LVDS screen (let's say that LVDS is 1280x800). The important thing to remember is that you will then have one big screen whose size will then be 2560x1024. If the above command failed then you probably didn't take this into account when setting up your xorg.conf (you need to configure your Virtual size), more about this a few lines below.

It happens quite frequently that xrandr can not find the 'best' resolution for your external monitor. Let alone the correct refresh rate. When this happens, you need to create a modeline (gtf), then add it to the appropriate output. Here are a couple simple commands that would create a new 1280x800 at 68Hz refresh and attach it to the VGA output: $ xrandr --newmode $(gtf 1280 800 68 | grep Modeline | sed s/Modeline\ // | tr -d '"')   $ xrandr --addmode VGA 1280x800_68.00 For other uses of xrandr read the manual page. I will show just one other example, disabling your external display:     $ xrandr --output VGA --off Here is a simple script that somewhat simplifies the task of picking the correct refresh rate and then setting up your screens stupid.sh

Cycling through possible configurations
It is possible to cycle through different configurations with some key (for example XF86Display). Here is a snippet of code which uses notification to display the proposed configuration. Each time the key is pressed, another configuration is proposed depending on the active outputs. Once we have proposed all possible configurations, we propose to not change anything. To accept a configuration, the user should not switch to another configuration:


 * Only LVDS1
 * Only VGA1
 * LVDS1 + VGA1
 * VGA1 + LVDS1
 * No change
 * Only LVDS1

-- Get active outputs local function outputs local outputs = {} local xrandr = io.popen("xrandr -q") if xrandr then for line in xrandr:lines do	 output = line:match("^([%w-]+) connected ") if output then outputs[#outputs + 1] = output end end xrandr:close end

return outputs end

local function arrange(out) -- We need to enumerate all the way to combinate output. We assume -- we want only an horizontal layout. local choices = {} local previous = { {} } for i = 1, #out do     -- Find all permutation of length `i`: we take the permutation -- of length `i-1` and for each of them, we create new -- permutations by adding each output at the end of it if it is     -- not already present. local new = {} for _, p in pairs(previous) do	 for _, o in pairs(out) do	   if not awful.util.table.hasitem(p, o) then new[#new + 1] = awful.util.table.join(p, {o}) end end end choices = awful.util.table.join(choices, new) previous = new end

return choices end

-- Build available choices local function menu local menu = {} local out = outputs local choices = arrange(out)

for _, choice in pairs(choices) do     local cmd = "xrandr" -- Enabled outputs for i, o in pairs(choice) do cmd = cmd .. " --output " .. o .. " --auto" if i > 1 then cmd = cmd .. " --right-of " .. choice[i-1] end end -- Disabled outputs for _, o in pairs(out) do	 if not awful.util.table.hasitem(choice, o) then cmd = cmd .. " --output " .. o .. " --off" end end

local label = "" if #choice == 1 then label = 'Only ' .. choice[1] .. ' '     else for i, o in pairs(choice) do if i > 1 then label = label .. " + " end label = label .. ' ' .. o .. ' '	 end end

menu[#menu + 1] = { label, cmd, "/usr/share/icons/Tango/32x32/devices/display.png"} end

return menu end

-- Display xrandr notifications from choices local state = { iterator = nil, timer = nil, cid = nil } local function xrandr -- Stop any previous timer if state.timer then state.timer:stop state.timer = nil end

-- Build the list of choices if not state.iterator then state.iterator = awful.util.table.iterate(menu,					function return true end) end

-- Select one and display the appropriate notification local next = state.iterator local label, action, icon if not next then label, icon = "Keep the current configuration", "/usr/share/icons/Tango/32x32/devices/display.png" state.iterator = nil else label, action, icon = unpack(next) end state.cid = naughty.notify({ text = label,				icon = icon,				timeout = 4,				screen = mouse.screen, -- Important, not all screens may be visible				font = "Free Sans 18",				replaces_id = state.cid }).id

-- Setup the timer state.timer = timer { timeout = 4 } state.timer:connect_signal("timeout",			 function			     state.timer:stop			     state.timer = nil			     state.iterator = nil			     if action then				awful.util.spawn(action, false)			     end			  end) state.timer:start end

config.keys.global = awful.util.table.join(  config.keys.global,   awful.key({}, "XF86Display", xrandr))

Static configuration
If you want to do a static setup for your displays then you could follow the next example. You already have one Section "Monitor" for your first display (and for this example let's say that it's identifier is LCD-Monitor and our VGA will be CRT-Monitor), the first step is to add another one, with settings for your second display (we continue with the VGA example): # This is for a static setup of the external display #   Section "Monitor" Identifier  "CRT-Monitor" VendorName  "FUS" ModelName   "19P4" HorizSync   30-96 VertRefresh 50-160 Option      "DPMS" Option      "PreferredMode" "1280x1024" Option      "RightOf"       "LVDS" ##Option    "Position"      "1280 0" EndSection

Then in your Section "Device" you do the actual setup: # Graphics configuration #    Section "Device" Identifier  "intel" Driver      "intel" VendorName  "Intel Corporation" BoardName   "Mobile GM965/GL960 Integrated Graphics Controller" # VGA display options Option      "monitor-VGA"     "CRT-Monitor" ##Option    "MonitorLayout"   "CRT,LFP" ##Option    "MonitorLayout"   "NONE,CRT+LFP" EndSection

These are the most basic settings, some other that are worth mentioning are: CheckLid, DevicePresence and Clone. You can find all options with explanations listed in the manual page of the intel driver (to read it you can execute: man intel or replace intel with the name of the driver you use).

The last step involves setting up your Virtual size as I mentioned earlier. In your Section "Screen" subsection "Display" you need to configure the virtual size of your desktop (usually placed below the Modes line). So, following our earlier examples this is how Section "Screen" would look like: # Screen configuration #   Section "Screen" Identifier "LCD Screen" Device     "intel" Monitor    "LCD-Monitor" DefaultDepth 24 Subsection "Display" Depth      24 Modes      "1280x800" "1280x768" "1280x720" "1024x768" "800x600" "640x480" ViewPort   0 0 Virtual    2560 1024 EndSubsection EndSection

nvidia
So, nvidia does not support XRandR yet. What they did up to this point is use their own technology called TwinView which in it's simplest configuration is not so great for us in the classic sense, as awesome will see the screen as one big desktop (thus you will not be able to move clients between screens etc. as there is "no" other screen to move them to). But, I suppose there might be some use cases where this would be the desired behaviour, so I will cover that too. Then with a little tweaking we could get the same behaviour as usually expected. Our last example will be using Xinerama which will also have the same effect as our earlier examples.

TwinView
With TwinView you will only have one Section "Monitor", for the first display. Complete setup of the second display is done in Section "Device". The easiest way to do this is with nvidia-settings, and if that doesn't seem to work you can edit your Xorg.conf manually. So let's see how one such could look like (in this example I am using two identical CRT monitors): Section "Device" Identifier "Card0" Driver     "nvidia" VendorName "nVidia Corporation" BoardName  "NV34 [GeForce FX 5500]" #       Option      "ConnectedMonitor"     "CRT-0, CRT-1" ##Option   "IgnoreDisplayDevices" "TV-0" #       # This is the important part Option     "TwinView"            "true" # Possible options are: Right-Of, Left-Of, Above, Below, Clone Option     "TwinViewOrientation" "Right-Of" #       Option      "SecondMonitorHorizSync"   "30-96" Option     "SecondMonitorVertRefresh" "50-160" #       # Metamodes tell us when: # monitor A is using resolution X then monitor B will use resolution Y; second lower A reolution, second lower B resolution... Option     "MetaModes" "1280x1024, 1280x1024; 1024x768, 1024x768; 800x600, 800x600; 640x480, 640x480" # Example when monitor B is using a lower resolution #Option    "MetaModes" "1280x1024, 1024x768@1280x1024; 1024x768, 1024x768; 800x600, 800x600" EndSection

Now let's see a different example contributed by another user, where he got two displays to behave independantly under awesome using TwinView. Here we will shuffle the options a bit, delegate them to other sections, but don't let it confuse you. In this example we will be using two identical LCD monitors (referenced as DFP-0 and DFP-1): Section "Monitor" Identifier   "Monitor0" HorizSync    30-100 VertRefresh  60 ModeLine     "1680x1050" 147.1 1680 1784 1968 2256 1050 1051 1054 1087 +Hsync -Vsync EndSection Section "Monitor" Identifier   "Monitor1" HorizSync    30-100 VertRefresh  60 ModeLine     "1680x1050" 147.1 1680 1784 1968 2256 1050 1051 1054 1087 +Hsync -Vsync EndSection Section "Device" Identifier     "Card0" Driver         "nvidia" VendorName     "nVidia" BoardName      "NVIDIA GeForce 7800GS" Option         "AllowDDCCI"            "true" Option         "FlatPanelProperties"   "Scaling = aspect-scaled" Option         "RandRRotation"         "true" Option         "TripleBuffer"          "true" # Multiple display configuration ##Option       "ConnectedMonitor"          "DFP-0, DFP-1" Option         "PrimaryMonitor"            "DFP-0" Option         "UseDisplayDevice"          "DFP-0, DFP-1" # According to their docs, this is what makes the difference #  http://us.download.nvidia.com/XFree86/Linux-x86/180.29/README/chapter-13.html Option         "TwinViewOrientation"       "DFP-0  LeftOf DFP-1" Option         "TwinViewXineramaInfoOrder" "DFP-0, DFP-1" EndSection Section "Screen" Identifier   "Screen0" Device       "Card0" Monitor      "Monitor0" DefaultDepth 24 Subsection   "Display" Modes           "1680x1050" EndSubsection Option "MetaModes"  "DFP-0: 1680x1050 +0+0, DFP-1: 1680x1050 +1680+0;" Option "HorizSync"  "DFP-0: 30-100; DFP-1: 30-100" Option "VertRefresh" "DFP-0: 60; DFP-1: 60" Option "TwinView"   "true" EndSection Section "ServerLayout" Identifier   "TwinView Screen" Screen       0 "Screen0" 0 0 Option       "Xinerama"  "false" EndSection

For more information about TwinView (naming the devices (CRT, DFP, TV...), which pipes can be used at the same time and for everything else) refer to the latest nvidia README file which is distributed with their drivers but can also be read online: http://www.nvidia.com/object/unix.html

Xinerama
Xinerama is an X extension which enables multi-headed X applications and window managers to use two or more physical displays as one large virtual display. Note that you can use Xinerama in the same way with other drivers too (fglrx for example). When using Xinerama we will need to have a section for each Monitor, two Device sections, two Screen sections (each using it's own Device and Monitor) and we will setup orientation of our screens in the main ServerLayout section (where we will also enable Xinerama). This is the most basic example showing what I'm talking about (using two identical LCD displays): # Out Monitor sections #    Section "Monitor" Identifier  "Monitor0" VendorName  "Monitor Vendor" ModelName   "Monitor Model" EndSection Section "Monitor" Identifier  "Monitor1" VendorName  "Monitor Vendor" ModelName   "Monitor Model" EndSection # Our Device sections #     Section "Device" Identifier "Card0" Driver     "nvidia" VendorName "nVidia Corporation" BoardName  "G70 [GeForce 7800 GS]" Screen     0 EndSection Section "Device" Identifier "Card1" Driver     "nvidia" VendorName "nVidia Corporation" BoardName  "G70 [GeForce 7800 GS]" Screen     1 EndSection # Our Screen sections #    Section "Screen" Identifier   "Screen0" Device       "Card0" Monitor      "Monitor0" DefaultDepth 24 SubSection   "Display" Viewport 0 0 Depth    24 Modes    "1680x1050" EndSubSection EndSection Section "Screen" Identifier   "Screen1" Device       "Card1" Monitor      "Monitor1" DefaultDepth 24 SubSection   "Display" Viewport 0 0 Depth    24 Modes    "1680x1050" EndSubSection EndSection # Finally our ServerLayout that does the work #     Section "ServerLayout" Identifier    "Xinerama Screen" Screen        0  "Screen0" 0 0 Screen        1  "Screen1" RightOf "Screen0" Option        "Xinerama"   "true" EndSection

You will also have your Keyboard and Mouse configuration in this file, and reference them in your server layout etc. (if you are not already using HAL enabled X.org). You should already know all that. I'm only writing about displays. Speaking of which there are even more ways of using multiple displays, such as running two independant X sessions each on it's own monitor, there are also Zaphod and MergedFB, last to mention is that ATi, like nVidia, has it's own thing called BigDesktop (fglrx driver). Researching into those is up to you, reader. With that said I am wraping up this document, I hope it was clear enough and you have your huge desktop operational (and awesome controling it :).

Disper
A simplified utility: allowing command line switching of monitor configurations is http://willem.engen.nl/projects/disper/

This also helps work around the issue of the nvidia-settings confirmation dialog being hidden by awesome's restart when nvidia-settings changes the monitor mode.