Setting up single-carded multiseat

Text on this page describes how to build multiseat for two users on PC with single graphics card with two outputs. If you do not know what multiseat is, please, have a look here first. If you use different operating system than Linux, this description probably won't be very useful for you (especially if you do not use some Unix-based OS).

How does it work

The solution that I use is following – an X server is launched and this server is using both outputs of the video card and creates a display spread across both monitors (definitely I will confuse something here – if you think that I mix up terms screen, monitor and display then I can assure you that you are right). After that, two nested X servers (Xgl servers) are launched, each of them occupying one monitor.

Sending events form keyboard and mouse is then done through XevdevServer that reads corresponding ports and via X events sends them to the right server (that is necesary because today we can assign devices to read input from only to the real X server – Xgl does not have the ability).

And above all this login manager is running (GDM in my case).

Before I forget – I use proprietary drivers by Ati, because with the opened ones the performance was much worse.

How stable and how quick is it?

Short answer: well, not very good. So, with stability I do not have any problems at all (during the period I use this solution the computer did not freeze once). The problem is rather with the speed. That is pretty obvious after all – if you put another (rendering) layer between X server and applications you can't expect anything top-notch.

As a consequence, I am not able to run KDE. More precisesly, it launches okay but when redrawing – toolbar, for example – you can see how the individual icons are redrawn so it is usable only when there is no other option.

Other popular applications and subjective view on their performance:

  • Firefox: no problems (unless you browse over a page with some crazy Flash; YouTube is alright by the way)
  • Thunderbird: no problems
  • OpenOffice.org: icon redrawal is noticebale but you can work with it pretty normally
  • GIMP: it is difficult to use brush-like tools (mouse pointer is lagging behind)
  • MPlayer, Xine: no problems

Games are almost out of question (they are either too jerky or even refuse to launch) but this doesn't bother me that much.

I hope that the performance would be eventually improved because the development of the drivers and Xgl is still in progress…

Set-up

Setting up X server

I am not going to describe setting up xorg.conf because I expect that someone who wants to try multiseat knows how to do this. As a result, you should get a working output on two monitors. If you manage to have different display on each monitor (for example :0.0 and :0.1) you are one problem lower (I was not able to do it so after the startup I have to move Xgl to the right position manually).

Do not forget to switch off all X server features around sleep of the monitors and similar things. The reason is that XevdevServer catches all keyboard/mouse events and thus X server thinks that user is doing nothing and with ease switch off the monitors (and with “good” settings the only way out is via reboot)…

Setting up multiseat properties

As I have already mentioned, the very important part is XevdevServer program which read pressed keys and via X events sends them to corresponding Xgl server. It is therefore very important to find out which special file in /dev/ directory represents the input device.

For this purpose, files /dev/input/even­tN are the best (N is the number of the device). However, these numbers can change between boots and thus it is better to work directly with physical address of the device that do not change. It is possible to read those addresses in file /proc/bus/input/de­vices which content is made up by several similar parts that look like this:

I: Bus=0011 Vendor=0002 Product=0005 Version=0000
N: Name="ImPS/2 Generic Wheel Mouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/class/input/input2
U: Uniq=
H: Handlers=mouse1 event2
B: EV=7
B: KEY=70000 0 0 0 0
B: REL=103

I do not exepect you would have troubles to identify your keyboard and mouse and to find out correct physical address (line Phys=). Aside from keyboards and mice a power button and remote control may also appear in this file.

If we know these addresses we can create a configuration file for multiseat where we put also information about screen sizes (these data would be read by a script about which I write several paragraphs below).

Store the configuration into /etc/mseat.conf, for example.

#seat 0 (left)
seat-0-screen-size=1280x1024
seat-0-screen-position=0,0
seat-0-keyboard=usb-0000:00:1d.1-1/input0
seat-0-mouse=usb-0000:00:1d.1-1/input1

#seat 1 (right)
seat-1-screen-size=1280x1024
seat-1-screen-position=1280,0
seat-1-keyboard=isa0060/serio0/input0
seat-1-mouse=isa0060/serio1/input0

Setting up GDM

Settings here are a little bit more funny (read as more difficult). We have to add Xgl server for each seat and also launch XevdevServer. Thus I have created a script that does all the neccessary things.

Here is a list of “tasks” that has to be made by the script:

  • convert physical adresses of keyboard and mouse to filenames in directory /dev/input
  • find out display number with X server running and launch Xgl server on it with the correct size
  • move Xgl to the correct position (Xgl does not know the -geometry parameter)
  • launch XevdevServer with correct parameters

Maybe, I shall explain what I mean by those correct parameters for XevdevServer. Firstly and secondly those are paths to files representing keyboard and mouse input. Then it is a display number – actually there can be more than one of these because XevdevServer can switch between them – thus it is possible to launch four login managers on one monitor (and switch between them with Win Num+). Moreover, in new XevdevServer version (2.0.0) you can assign a configuration file where you can redefine key mappings and also special actions for some of them (such as this display switching or even a kill of Xgl).

Furthermore, script has to be pretty quick. When the script is slow – and do not launch Xgl in time – GDM thinks that something went wrong and all subprocesses of that displays shots down and tries to launch them again (and if you create a lock you are in a really nice mess). Additionally, if this happens for several times, GDM then waits 2 minutes before next attempts.

Luckily, I was able to tune the script to pretty good speed (by postponing some of the tasks: Xgl is launched as soon as possible and XevdevServer is started a bit later) and GDM do not kill anything now.

Because the whole configuration is read from a mseat.conf file, there is need to use just one argument to the script – the seat identifier. And this is part of my /etc/gdm/custom­.conf:

[server-Std]
name=Standard
handled=false
command=/usr/bin/X -ac
flexible=false

[server-Seat0]
name=Seat Alpha
command=/usr/local/sbin/mseat-Xstart -seat 0
flexible=false

[server-Seat1]
name=Seat Bravo
command=/usr/local/sbin/mseat-Xstart -seat 1
flexible=false

Notice forbidding launching of the manager at the main X server (handled=false).

This is – what goes with login manager – all. For practical reasons it is desirable to do several other settings (e. g. set up screensaver, disable power off item etc.).

Script for launching Xgl and XevdevServer

#!/bin/sh


myDisplay="$1"; shift

lockDir="/var/lock/mseat";

mkdir /tmp/mseat 2>/dev/null
date >>/tmp/mseat/log.$mseatUniqueId
logmsg() {
  dt=`date '+%m-%d %H:%M:%S'`;
  echo "[mseat $dt]: " "$@" >>/tmp/mseat/log.$mseatUniqueId
}

usage() {
 echo "Usage:" >/dev/stderr
 echo "  $0 -seat # [-other-parameters-for-Xgl]" >/dev/stderr
 exit 1;
}

getConfig() {
 sed -n 's/^seat-'"$1-$2"'=\(.*\)/\1/p' </etc/mseat.conf | tail -n 1
}

logmsg "$@";
logmsg "Running as $USER";

backgroundServer=/usr/bin/X
xevdevServer=/usr/sbin/xevdevserver

while [ $# -gt 0 ]; do
    if [ "$1" = "-seat" ]; then
        seat="$2";
        shift;
    elif [ "$1" = "-displayScreen" ]; then
        DISPLAY="${DISPLAY}.$2";
        elif [ "$1" = "-usingserver" ]; then
                backgroundServer="$2";
                shift;
    else
        if ! echo "$1" | grep '^vt' >/dev/null; then
            args="$args '$1'";
        fi
     fi
    shift
 done
eval set -- $args
logmsg "$@"

[ -z "$seat" ] && { echo "Seat number missing" >/dev/stderr; usage; }


# gonna find display number to connect to
DISPLAY=:`ps -ef | sed -n "s:.*$backgroundServer \:\([^ ]*\).*:\1:p" | head -n 1`;
[ "$DISPLAY" = ":" ] && DISPLAY=:0

export MSEATID=$seat

grep "^seat-$seat" /etc/mseat.conf >/dev/null || { echo "Unknown seat number ($seat)" >/dev/stderr; exit; };

screenSize=`getConfig $seat "screen-size"`;

XServerPid=$$

logmsg "Screen is $screenSize big, Xgl will have PID $XServerPid"

# lock-begin
#i=0;
#until mkdir "$lockDir"; do
#    sleep $(( $RANDOM % 5 + 1 ));
#    i=$(( $i + 1 ));
#    if [ $i -gt 20 ]; then
#       exit 2;
#     fi
#  done

#xwininfo -root -tree | grep Xgl | sed 's#^\([ \t]*\)\([0-9xA-Fa-f]*\).*#\2#' | sort >/tmp/mseat.before

logmsg "Starting background jobs to take action later..."


# __  __     _                   _ _   _
# \ \/ /__ _| |  _ __   ___  ___(_) |_(_) ___  _ __
#  \  // _` | | | '_ \ / _ \/ __| | __| |/ _ \| '_ \
#  /  \ (_| | | | |_) | (_) \__ \ | |_| | (_) | | | |
# /_/\_\__, |_| | .__/ \___/|___/_|\__|_|\___/|_| |_|
#      |___/    |_|
#
(
    logmsg "Neste X server positioning"
    #kill -s CONT $XServerPid || { rmdir "$lockDir"; exit; }
    export DISPLAY
    winid='';
    tries=0;
    while [ -z "$winid" ]; do
        sleep 1;
        winid=`xwininfo -root -tree | grep "Xephyr on $myDisplay" | sed 's#^\([ \t]*\)\([0-9xA-Fa-f]*\).*#\2#'`;
        tries=$(( $tries + 1 ));
        [ "$tries" -gt 60 ] && break;
    done


    screenPosition=`getConfig $seat "screen-position"`;
    screenSize=`echo $screenSize | tr 'x' ','`;

    logmsg "Shifting to $screenPosition...";
    wmctrl -i -r "$winid" -e "0,$screenPosition,$screenSize"
) &




#                    _
# __  _______   ____| | _____   _____  ___ _ ____   _____ _ __
# \ \/ / _ \ \ / / _` |/ _ \ \ / / __|/ _ \ '__\ \ / / _ \ '__|
#  >  <  __/\ V / (_| |  __/\ V /\__ \  __/ |   \ V /  __/ |
# /_/\_\___| \_/ \__,_|\___| \_/ |___/\___|_|    \_/ \___|_|
#
(
    logmsg "xevdevserver start-up"

    getInputEventNumber() {
        if echo "$1" | grep '^/dev/' >/dev/null; then
            ev="$1";
          else
            phys=`echo "$1" | sed 's#/#\\\\/#g'`;
            ev=`sed -n '/Phys='"$phys"'/,/^$/{s#.*Handlers.*\(event[0-9]*\).*#\1#p}' </proc/bus/input/devices`;
            ev="/dev/input/$ev";
        fi
        echo "$ev";
    }

    keyboard=`getConfig $seat "keyboard"`;
    mouse=`getConfig $seat "mouse"`;

        # no keyboard or mouse, probably will be used remotely
        [ -z "$keyboard$mouse" ] && exit;



    keyboard=`getInputEventNumber "$keyboard"`;
    mouse=`getInputEventNumber "$mouse"`;

    logmsg "Devices are $mouse (mouse) and $keyboard (keyboard)"

    i=0;
    while true; do
        #nohup /usr/local/sbin/xevdevserver -d "$newDisplay" -m $mouse -k $keyboard -s $XServerPid >/dev/null
        #/usr/local/sbin/startsched 10 /usr/local/sbin/xevdevserver -d "$newDisplay" -m $mouse -k $keyboard -s $XServerPid
        /usr/local/sbin/startsched 10 $xevdevServer -d "$myDisplay" -m $mouse -k $keyboard -c /etc/xevdevserver.cfg
        #-c /home/vojta/bin/xevdev.cfg
        #/usr/local/sbin/xevdevserver -d "$newDisplay" -m $mouse -k $keyboard -s $XServerPid
        sleep 1;
        kill -s CONT $XServerPid || exit;
        sleep 1;
        i=$(( $i + 1 ));
        logmsg "Failed to start xevdevserver, having another try ($i)";
    done

)&

logmsg "Launching Xephyr NOW!"
export DISPLAY
export MSEATNUMBER=$seat
#export LD_PRELOAD=/usr/lib/libGL.so
#exec /usr/local/sbin/startsched 1 /usr/bin/Xgl "$newDisplay" +extension XTEST -arinterval 10 -accel glx -accel xv -softcursor -screen "$screenSize" -s 5 v "$@"
exec Xephyr "$myDisplay" +extension XTEST +extension GLX +extension DAMAGE +extension DOUBLE-BUFFER +extension Composite -arinterval 10 -softCursor -zap -screen "$screenSize" -s 5 v "$@"

#exec /usr/bin/Xgl "$newDisplay" +extension XTEST -arinterval 10 -ac -accel glx -accel xv -softcursor -screen "$screenSize" "$@"

logmsg "Exec failed, who knows why :-("

Page was last changed on August 31, 2009 at 1:52 pm

Created by Vojtech HorkyXHTML & CSS valid •