19

I've setup a VM that works great except for the auto resize functionality which seems to be inconsistent. I've got it randomly working a few times but it also randomly stops working. It's currently not working and I'm not sure what I need to do to get it to work reliably (like VirtualBox autoresizing). Please see below for my configuration.

setup

Host: Ubuntu 16.10
Guest: Ubuntu 16.04.2, et. al (I've tried with other Linux distros with similar results)
Guest: created using virt-manager
Guest: installed spice-vdagent
Guest: virt-manager > View > Scale Display > Auto resize VM with window (checked)

spice

user@ubuntu:~$ ps aux | grep spice
root       805  0.0  0.0  30568   260 ?        Ss   08:59   0:00 /usr/sbin/spice-vdagentd
user      1365  0.0  0.0  35124  2360 ?        Ss   08:59   0:00 /usr/bin/spice-vdagent
user      1933  0.0  0.0  21292  1020 pts/17   S+   09:00   0:00 grep --color=auto spice

qxl

user@ubuntu:~$ sudo lshw -c Video
  *-display               
       description: VGA compatible controller
       product: QXL paravirtual graphic card
       vendor: Red Hat, Inc.
       physical id: 2
       bus info: pci@0000:00:02.0
       version: 04
       width: 32 bits
       clock: 33MHz
       capabilities: vga_controller rom
       configuration: driver=qxl latency=0
       resources: irq:10 memory:f4000000-f7ffffff memory:f8000000-fbffffff memory:fc058000-fc059fff ioport:c040(size=32) memory:c0000-dffff

What am I missing to correctly enable auto resize with qemu/kvm?

howdoieven
  • 191
  • 1
  • 1
  • 5

4 Answers4

17

The gnome desktop, mutter, has some hotplug code that deals with the resize.

If you have any other desktop like xfce4 or KDE or a bare .xinitrc file running compiz like I do, you are pretty stuck, but I think I finally found an elegant and easy solution.

xrandr --output Virtual-0 --auto will pick up the size and apply it from vdagent. All that's need is a trigger.

Here is the trigger and how to handle it.

udev exposes the resize event as a drm device change that looks like this:
UDEV [10758.537471] change /devices/pci0000:00/0000:00:02.0 `drm/card0 (drm)

To have your desktop react to it create a udev rule and refer to a script to resize:
Rule in /etc/udev/rules.d/50-x-resize.rules:

ACTION=="change",KERNEL=="card0", SUBSYSTEM=="drm", RUN+="/usr/local/bin/x-resize" 

Script in /usr/local/bin/x-resize:

#! /bin/sh
PATH=/usr/bin
export DISPLAY=:0.0
xrandr --output "$(xrandr | awk '/ connected/{print $1; exit; }')" --auto

You may need to customize that shell script for your situation.

I found that on debian 10 and Ubuntu 20.04 I didn't even have to restart anything; it picked it up immediately.

Update: 2021-03-17

I recently found that I had to get permissions to allow that script to work. I changed it to this:

#! /bin/sh 
PATH=/usr/bin
desktopuser=$(/bin/ps -ef  | /bin/grep -oP '^\w+ (?=.*vdagent( |$))') || exit 0
export DISPLAY=:0
export XAUTHORITY=$(eval echo "~$desktopuser")/.Xauthority
xrandr --output $(xrandr | awk '/ connected/{print $1; exit; }') --auto
gsker
  • 181
  • 1
  • 5
  • 1
    It's a [known bug](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=858549#15) for some DEs. – Jon Bentley May 01 '20 at 13:51
  • This is great, but be carfeul, it made my XFCE VM fail to boot with a blinking cursor. The answer by Joe H. below works without such problems – keen Mar 16 '21 at 19:43
8

I'm using Debian 10 and icewm as guest VM. When spice-vdagent is running I can manually adjust the screen size with xrandr --output Virtual-1 --auto

A change in the display size can also be observed with xev:

joe@l1:~$ xev -root -event randr 

RRScreenChangeNotify event, serial 18, synthetic NO, window 0x3af,
    root 0x3af, timestamp 39153, config_timestamp 82137
    size_index 65535, subpixel_order SubPixelUnknown
    rotation RR_Rotate_0
    width 1320, height 949, mwidth 348, mheight 250

RRNotify event, serial 18, synthetic NO, window 0x3af,
    subtype XRROutputChangeNotifyEvent
    output Virtual-1, crtc 63, mode 1320x949 (1320x949)
    rotation RR_Rotate_0
    connection RR_Connected, subpixel_order SubPixelUnknown

Using that as a trigger seems less intrusive than working with udev, in particular when using different desktop environments or window managers in the same VM.

joe@l1:~$ cat /home/joe/.xsession
#!/bin/sh

if [ -x /usr/bin/spice-vdagent ] ; then
    /usr/bin/spice-vdagent
    /home/joe/.icewm/xrandr-loop &
fi
exec /usr/bin/icewm-session

joe@l1:~$ cat /home/joe/.icewm/xrandr-loop
#!/bin/sh

sleep 2

xrandr --output "$(xrandr | awk '/ connected/{print $1; exit; }')" --auto

xev -root -event randr | \
grep --line-buffered 'subtype XRROutputChangeNotifyEvent' | \
while read foo ; do \
    xrandr --output "$(xrandr | awk '/ connected/{print $1; exit; }')" --auto
done

joe@l1:~$ 
Joe H.
  • 81
  • 1
  • 2
1

go to the hardware section of your VM

Remove existing channel device if there is one and add the com.redhat.spice.0 spicevmc device

Darwin R.
  • 11
  • 1
1

I had a really hard time getting my guest VMs to support two monitors with a resolution greater than 1080p. As it turns out, this was because the default QXL memory buffer of 16 mib isn't big enough. You probably won't run into this issue if both your monitors are only 1080p. But if they are, then need to set heads=2 AND increase the memory buffer via the vgamem attribute (32 mib worked in my case).

On my systems, I had to to edit the libvirt XML configuration by hand to make this change. To do this, look for the vgamem attribute. It will probably have a default value of 16384 (aka 16 mib). To increase it, to say, 32 mib, change the attribute value to 32768. On my system, this modified XML fragment when from:

<video>
  <model type="qxl" ram="65536" vram="65536" vgamem="16384" heads="1" primary="yes"/>
  <address type="pci" domain="0x0000" bus="0x00" slot="0x01" function="0x0"/>
</video>

To:

<video>
  <model type="qxl" ram="65536" vram="65536" vgamem="32768" heads="2" primary="yes"/>
  <address type="pci" domain="0x0000" bus="0x00" slot="0x01" function="0x0"/>
</video>

If your not sure what values are actually being used, you can find out by looking at the qemu command line parameters. On a Unix/Linux system, start the guest, and then run ps -ef | grep qemu ... and look for the QXL device. It will probably look something like -device qxl-vga,id=video0,ram_size=67108864,vram_size=67108864,vram64_size_mb=0,vgamem_mb=32,max_outputs=2,bus=pcie.0,addr=0x1. The vgamem_mb value of 32 is what's important.

Once you have the QXL driver setup properly, changing the guest resolution should be easy. You can use the graphical display configurator, or you can use xrandr via the command line. In my case I chose to automate the process with the following script:

#!/bin/bash
if [ `xrandr | grep --extended-regexp --count "Virtual-0 connected|Virtual-1 connected"` == 2 ]; then
xrandr --newmode "1920x1200_60.00"  193.25  1920 2056 2256 2592  1200 1203 1209 1245 -hsync +vsync
xrandr --addmode Virtual-0 "1920x1200_60.00"
xrandr --addmode Virtual-1 "1920x1200_60.00"
xrandr --output Virtual-0 --mode "1920x1200_60.00" --panning 1920x1200+0+0 --output Virtual-1 --mode "1920x1200_60.00" --panning 1920x1200+1920+0
elif [ `xrandr | grep --extended-regexp --count "qxl-0 connected|qxl-1 connected"` == 2 ]; then
xrandr --newmode "1920x1200_60.00"  193.25  1920 2056 2256 2592  1200 1203 1209 1245 -hsync +vsync
xrandr --addmode qxl-0 "1920x1200_60.00"
xrandr --addmode qxl-1 "1920x1200_60.00"
xrandr --output qxl-0 --mode "1920x1200_60.00" --panning 1920x1200+0+0 --output qxl-1 --mode "1920x1200_60.00" --panning 1920x1200+1920+0
elif [ `xrandr | grep --extended-regexp --count "Virtual-0 connected"` == 1 ]; then
xrandr --newmode "1920x1200_60.00"  193.25  1920 2056 2256 2592  1200 1203 1209 1245 -hsync +vsync
xrandr --addmode Virtual-0 "1920x1200_60.00"
xrandr --output Virtual-0 --mode "1920x1200_60.00" --panning 1920x1200+0+0
else
xrandr --newmode "1920x1200_60.00"  193.25  1920 2056 2256 2592  1200 1203 1209 1245 -hsync +vsync
xrandr --addmode qxl-0 "1920x1200_60.00"
xrandr --output qxl-0 --mode "1920x1200_60.00" --panning 1920x1200+0+0
fi

Note this is for a 1920x1200 setup. It's also worth noting that depending on your display server, kernel version, and driver setup, etc, it's possible the display adapters will show up as either "qxl-#" or "Virtual-#".

To use the above script for a resolution other than 1920x1200, you'll need to use the appropriate modeline. To get the correct modeline, use the cvt command line tool. As an example, if what you wanted was the modeline for a 4k (aka 2160p) monitor, you would run cvt 3840 2160 60 (assuming 60hz is the refresh rate). This would give you the modeline:

# 3840x2160 59.98 Hz (CVT 8.29M9) hsync: 134.18 kHz; pclk: 712.75 MHz
Modeline "3840x2160_60.00"  712.75  3840 4160 4576 5312  2160 2163 2168 2237 -hsync +vsync

Now the take the value which appears after "Modeline":

"3840x2160_60.00"  712.75  3840 4160 4576 5312  2160 2163 2168 2237 -hsync +vsync

And replace the value:

"1920x1200_60.00"  193.25  1920 2056 2256 2592  1200 1203 1209 1245 -hsync +vsync

In the example script above.

A couple of notes. Namely the text inside the quotes is actually the "label" ... so you'll need to replace the references to "1920x1200_60.00" with the label for your new modeline. It's also worth noting that you can omit the refresh rate when calling cvt and it will guess ... but that guess could be wrong if your running it inside the guest system. To avoid this problem, you may want to call cvt on your host. In that case you could omit the refresh rate, and cvt will determine the right value by looking at your hardware.

ladar
  • 11
  • 2