1. Introduction

In this tutorial, we’ll learn how to use an Android device, such as a tablet or a phone, as the second display of our Linux machine.

We’ll set up the environment in xrandr and use a VNC connection in the Android device to see the display.

2. Creating a New Active Display in a Disconnected xrandr Interface

We can take advantage of one feature of xrandr and create a fake active display in one of the disconnected interfaces of our machine.

First, we need to retrieve a timing standard (also known as a time formula) for the screen of the Android device. Then, we create a new mode in xrandr with the previous timing standard and link it to a disconnected interface.

2.1. Retrieve a Timing Standard for xrandr

xrandr needs a timing standard. This refers to a way to specify the timings of the component video signal.

There are several timing standards. Two of the most famous ones are GTF (General Timing Formula, which is an older standard) and CVT (Coordinated Video Timings, which is a more recent standard with better support for higher resolutions).

There are Linux applications for each one of these standards. We need to provide to each application the resolution of our Android device (in the following snippets, the resolution is 2336×1080) and the refresh rate (which is set to 60Hz, but we can actually use any other value since we won’t use a real monitor).

For the GTF standard, there’s gtf :

$ gtf 2336 1080 60
# 2336x1080 @ 60.00 Hz (GTF) hsync: 67.08 kHz; pclk: 210.36 MHz
Modeline "2336x1080_60.00"  210.36  2336 2488 2736 3136  1080 1081 1084 1118  -HSync +Vsync

For the CVT standard, we may find cvt installed in our machines:

$ cvt 2336 1080 60
# 2336x1080 59.93 Hz (CVT) hsync: 67.12 kHz; pclk: 210.50 MHz
Modeline "2336x1080_60.00"  210.50  2336 2488 2736 3136  1080 1083 1093 1120 -hsync +vsync

Both applications return some parameters about the screen in the first line and the Modeline in the second line. The line starting with Modeline is the timing standard we’re interested in.

We can see that there are differences between the Modeline returned for each application. In principle, the differences between these results are only significant for CRT displays, but for newer LCD or LED panels, it shouldn’t matter.

Moreover, the two-timing standards should give the same image since most displays always have had sync circuits to adapt the pixel clock and refresh rate.

2.2. Mode Creation in xrandr Based on the Time Standard

Based on the Modeline from the previous subsection, we can create a mode in xrandr. Let’s start by inspecting xrandr‘s setup:

$ xrandr
Screen 0: minimum 8 x 8, current 1366 x 768, maximum 32767 x 32767 
LVDS1 connected primary 1366x768 (normal left inverted right x axis y axis) 
  1366x768 60.10* 
  1280x720 59.74 
  1024x768 60.00 
  800x600 60.32 56.25 
  640x360 59.84 59.32 60.00 
DP1 disconnected (normal left inverted right x axis y axis)
HDMI1 disconnected (normal left inverted right x axis y axis) 
VGA1 disconnected (normal left inverted right x axis y axis) 
VIRTUAL1 disconnected (normal left inverted right x axis y axis)

We can now add a new mode, using the –newmode flag combined with the Modeline obtained before:

$ xrandr --newmode "2336x1080_60.00" 210.50 2336 2488 2736 3136 1080 1083 1093 1120 -hsync +vsync

This results in a new mode appearing in the list of available modes in xrandr:

$ xrandr 
Screen 0: minimum 8 x 8, current 1366 x 768, maximum 32767 x 32767 
LVDS1 connected primary 1366x768 (normal left inverted right x axis y axis) 
  1366x768 60.10* 
  1280x720 59.74 
  1024x768 60.00 
  800x600 60.32 56.25 
  640x360 59.84 59.32 60.00 
DP1 disconnected (normal left inverted right x axis y axis)
HDMI1 disconnected (normal left inverted right x axis y axis) 
VGA1 disconnected (normal left inverted right x axis y axis) 
VIRTUAL1 disconnected (normal left inverted right x axis y axis) 
  2336x1080_60.00 (0x8af) 210.500MHz -HSync +VSync 
    h: width 2336 start 2488 end 2736 total 3136 skew 0 clock 67.12KHz 
    v: height 1080 start 1083 end 1093 total 1120 clock 59.93Hz

We see that this new mode appears under the VIRTUAL1 output, which is a purely virtual device.

We need now to associate this new mode with an output that we don’t use. Let’s use HDMI1 as such output:

$ xrandr --addmode HDMI1 2336x1080_60.00 

Now we can check it by running xrandr again:

$ xrandr 
Screen 0: minimum 8 x 8, current 1366 x 768, maximum 32767 x 32767 
LVDS1 connected primary 1366x768 (normal left inverted right x axis y axis) 
  1366x768 60.10* 
  1280x720 59.74 
  1024x768 60.00 
  800x600 60.32 56.25 
  640x360 59.84 59.32 60.00 
DP1 disconnected (normal left inverted right x axis y axis)
HDMI1 disconnected (normal left inverted right x axis y axis) 
  2336x1080_60.00  59.93
VGA1 disconnected (normal left inverted right x axis y axis) 
VIRTUAL1 disconnected (normal left inverted right x axis y axis)

We see that the mode appears now under the HDMI1 output instead of under the VIRTUAL1 output.

3. Setup a VNC Connection for the Newly Created Mode

Once we’ve created a fake mode under an unused connection, we can set the VNC connection up. Then, we’ll be able to see the screen of that display on our Android device.

3.1. Activate the New Mode in xrandr

The steps are similar to changing the monitor with xrandr. However, in this case, we’ll expand the desktop to the disconnected interface with the created mode. This is, HDMI1 and 2336x1080_60.00, respectively in our case:

$ xrandr --output HDMI1 --mode 2336x1080_60.00 --left-of LVDS1

We use –left-of to locate the new display at the left of the currently used monitor (LVDS1). Alternatively to –left-of we can use –right-of, –above, –below, or any other flag to locate the new display (such as the common –pos flag).

Let’s check the resulting configuration:

$ xrandr 
Screen 0: minimum 8 x 8, current 3702 x 1080, maximum 32767 x 32767 
LVDS1 connected primary 1366x768+2336+0 (normal left inverted right x axis y axis) 
  1366x768 60.10*+ 
  1280x720 59.74 
  1024x768 60.00 
  800x600 60.32 56.25 
  640x360 59.84 59.32 60.00 
DP1 disconnected (normal left inverted right x axis y axis)
HDMI1 disconnected 2336x1080+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
  2336x1080_60.00  59.93*
VGA1 disconnected (normal left inverted right x axis y axis) 
VIRTUAL1 disconnected (normal left inverted right x axis y axis)

We won’t be able to see anything on this virtual device, but our mouse can be moved into the location of the new display.

3.2. Connect the Android Device to Our Machine via VNC

From the previous snippet from xrandr, we see that the HDMI1 display is located at the 0+0 location and has a resolution of 2336×1080. The current display (LVDS1) is located at 2336+0 (just at the right of the HDMI1 display) and has a resolution of 1366×768.

Now we need to share the HDMI1 part of the display via VNC using x11vnc:

$ x11vnc -clip 2336x1080+0+0 -nocursorshape -nocursorpos

We just clipped the part of the screen corresponding to the HDMI1 output. We also used the –nocursorshape and –-nocursorpos so that the VNC viewer doesn’t deal with the cursor and the server directly paints it on the figure.

Time to move to our Android device.

We need a VNC viewer that supports showing the remote cursor. For example, we can use RealVNC Viewer app.

Once we set the VNC connection with the device IP and the port (which is shown in the x11vnc output but is by default the port 5900), we should see the shared screen on our device:

Display setup with x11vnc and two screens

The left part of the image (desktop 2, surrounded by a blue rectangle) is the output of the HDMI1 as seen in the Android device. The right part (desktops 1 and 3, surrounded by red) is the LVDS1 display, as seen on the screen monitor. Since we’re using xrandr to fake a full display, the screen from the Android device works as an actual monitor. We can drag windows between the two screens, and maximizing will only maximize the window in the current display.

Once we’ve closed the connection in the VNC viewer, we need to run x11vnc again to establish a new connection from the viewer to the VNC server.

4. Further Improvements

Let’s now consider some limitations and improvements that we can make to have a smoother experience.

Even if the connection is smooth and HD videos can be played, there is still a low framerate due to the VNC connection. One way to address this is to set the encoding to tight, assuming the VNC viewer allows for it.

All the devices from the network our device is connected to can reach the VNC port and see our monitor. Therefore, we may want to protect our VNC connection with a password.

We can run x11vnc requesting the use of a password:

$ x11vnc -usepw ...

This will use the password located in ~/.vnc/passwd or in ~/.vnc/passwdfile. If neither of these files exists, x11vnc will ask for a password, store it in ~/.vnc/passwd, and use it for this and subsequent connections.

We should also check whether VNC is running in encrypted mode. If we’re using this on a public or unsecure network, we may expose ourselves to risk.

Finally, we can try to speed up the connection and circumvent some of the problems mentioned if we connect the Android device to the VNC server via USB. For that, we need to have USB debugging enabled on our Android device and install adb on our Linux machine. Then, we launch the VNC server with x11vnc (let’s assume that it’s on port 5900), and we make this port accessible to the Android device through port 5901:

$ adb reverse tcp:5900 tcp:5901

In the Android VNC viewer, we need to point to the localhost with port 5901.

5. Conclusion

In this article, we configured xrandr to have an extra fake display to extend our desktop.

Then, set up a VNC server and shared the xrandr display via x11vnc.

Finally, we used this display with a VNC viewer installed on the Android device.