Using Multiple Screens

From awesome
Jump to: navigation, search

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[edit]

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[edit]

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[edit]

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 <span weight="bold">' .. choice[1] .. '</span>'
      else
	 for i, o in pairs(choice) do
	    if i > 1 then label = label .. " + " end
	    label = label .. '<span weight="bold">' .. o .. '</span>'
	 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[edit]

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[edit]

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[edit]

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[edit]

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[edit]

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.