[Spice-devel,win-qxl,2/2] Implement custom display resolution

Submitted by Marc-André Lureau on May 11, 2012, 4:36 p.m.

Details

Message ID 1336754204-10901-2-git-send-email-marcandre.lureau@gmail.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Marc-André Lureau May 11, 2012, 4:36 p.m.
---
 display/driver.c     |   32 +++++++++++++++
 include/qxl_driver.h |    8 +++-
 miniport/qxl.c       |  108 +++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 144 insertions(+), 4 deletions(-)

Patch hide | download patch | download mbox

diff --git a/display/driver.c b/display/driver.c
index 5c4578c..ef011ad 100644
--- a/display/driver.c
+++ b/display/driver.c
@@ -45,6 +45,7 @@ 
 
 static DRVFN drv_calls[] = {
     {INDEX_DrvDisableDriver, (PFN)DrvDisableDriver},
+    {INDEX_DrvEscape, (PFN)DrvEscape},
     {INDEX_DrvEnablePDEV, (PFN)DrvEnablePDEV},
     {INDEX_DrvDisablePDEV, (PFN)DrvDisablePDEV},
     {INDEX_DrvCompletePDEV, (PFN)DrvCompletePDEV},
@@ -252,6 +253,37 @@  BOOL DrvEnableDriver(ULONG engine_version, ULONG enable_data_size, PDRVENABLEDAT
     return TRUE;
 }
 
+ULONG DrvEscape(SURFOBJ *pso, ULONG iEsc, ULONG cjIn, PVOID pvIn,
+                ULONG cjOut, PVOID pvOut)
+{
+    PDev* pdev = pso ? (PDev*)pso->dhpdev : NULL;
+    int RetVal = -1;
+
+    switch (iEsc) {
+    case QXL_ESCAPE_SET_CUSTOM_DISPLAY: {
+        ULONG length;
+
+        DEBUG_PRINT((pdev, 1, "set custom display %p\n", pdev));
+        if (pdev == NULL)
+            break;
+
+        if (EngDeviceIoControl(pdev->driver, IOCTL_QXL_SET_CUSTOM_DISPLAY,
+                               pvIn, cjIn, NULL, 0, &length)) {
+            DEBUG_PRINT((pdev, 0, "%s: IOCTL_QXL_SET_CUSTOM_DISPLAY failed\n", __FUNCTION__));
+            break;
+        }
+        RetVal = 1;
+        break;
+    }
+    default:
+        DEBUG_PRINT((NULL, 1, "%s: unhandled escape code %d\n", __FUNCTION__, iEsc));
+        RetVal = 0;
+    }
+
+    DEBUG_PRINT((NULL, 1, "%s: end\n", __FUNCTION__));
+    return RetVal;
+}
+
 VOID DrvDisableDriver(VOID)
 {
     DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__));
diff --git a/include/qxl_driver.h b/include/qxl_driver.h
index af65916..eac6f5f 100644
--- a/include/qxl_driver.h
+++ b/include/qxl_driver.h
@@ -23,6 +23,7 @@ 
 #define _H_QXL_DRIVER
 
 #include <spice\qxl_dev.h>
+#include <spice\qxl_windows.h>
 
 #if (WINVER < 0x0501)
 #include "wdmhelper.h"
@@ -30,12 +31,16 @@ 
 
 enum {
     FIRST_AVIL_IOCTL_FUNC = 0x800,
-    QXL_GET_INFO_FUNC = FIRST_AVIL_IOCTL_FUNC
+    QXL_GET_INFO_FUNC = FIRST_AVIL_IOCTL_FUNC,
+    QXL_SET_CUSTOM_DISPLAY
 };
 
 #define IOCTL_QXL_GET_INFO \
     CTL_CODE(FILE_DEVICE_VIDEO, QXL_GET_INFO_FUNC, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
+#define IOCTL_QXL_SET_CUSTOM_DISPLAY \
+    CTL_CODE(FILE_DEVICE_VIDEO, QXL_SET_CUSTOM_DISPLAY, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
 #define QXL_DRIVER_INFO_VERSION 3
 
 typedef struct MemSlot {
@@ -116,6 +121,5 @@  typedef struct QXLDriverInfo {
     UINT64 fb_phys;
 } QXLDriverInfo;
 
-
 #endif
 
diff --git a/miniport/qxl.c b/miniport/qxl.c
index aefd780..6687652 100644
--- a/miniport/qxl.c
+++ b/miniport/qxl.c
@@ -83,6 +83,7 @@  typedef struct QXLExtension {
 
     ULONG current_mode;
     ULONG n_modes;
+    ULONG custom_mode;
     PVIDEO_MODE_INFORMATION modes;
 
     PEVENT display_event;
@@ -442,6 +443,69 @@  VP_STATUS Prob(QXLExtension *dev, VIDEO_PORT_CONFIG_INFO *conf_info,
 }
 
 #if defined(ALLOC_PRAGMA)
+void FillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB,
+                    ULONG maskR, ULONG maskG, ULONG maskB);
+#pragma alloc_text(PAGE, FillVidModeBPP)
+#endif
+
+/* Fills given video mode BPP related fields */
+void FillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB,
+                    ULONG maskR, ULONG maskG, ULONG maskB)
+{
+    pMode->NumberRedBits   = bitsR;
+    pMode->NumberGreenBits = bitsG;
+    pMode->NumberBlueBits  = bitsB;
+    pMode->RedMask         = maskR;
+    pMode->GreenMask       = maskG;
+    pMode->BlueMask        = maskB;
+}
+
+#if defined(ALLOC_PRAGMA)
+VP_STATUS FillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index, ULONG yoffset);
+#pragma alloc_text(PAGE, FillVidModeInfo)
+#endif
+/* Fills given video mode structure */
+VP_STATUS FillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index, ULONG yoffset)
+{
+    if (xres <= 0 || yres <= 0)
+        return ERROR_INVALID_DATA;
+
+    VideoPortZeroMemory(pMode, sizeof(VIDEO_MODE_INFORMATION));
+
+    /*Common entries*/
+    pMode->Length                       = sizeof(VIDEO_MODE_INFORMATION);
+    pMode->ModeIndex                    = index;
+    pMode->VisScreenWidth               = xres;
+    pMode->VisScreenHeight              = yres - yoffset;
+    pMode->ScreenStride                 = xres * ((bpp + 7) / 8);
+    pMode->NumberOfPlanes               = 1;
+    pMode->BitsPerPlane                 = bpp;
+    pMode->Frequency                    = 60;
+    pMode->XMillimeter                  = 320;
+    pMode->YMillimeter                  = 240;
+    pMode->VideoMemoryBitmapWidth       = xres;
+    pMode->VideoMemoryBitmapHeight      = yres - yoffset;
+    pMode->DriverSpecificAttributeFlags = 0;
+    pMode->AttributeFlags               = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
+
+    /*BPP related entries*/
+    switch (bpp)
+    {
+        case 16:
+            FillVidModeBPP(pMode, 5, 6, 5, 0xF800, 0x7E0, 0x1F);
+            break;
+        case 24:
+        case 32:
+            FillVidModeBPP(pMode, 8, 8, 8, 0xFF0000, 0xFF00, 0xFF);
+            break;
+        default:
+            return ERROR_INVALID_DATA;
+    }
+
+    return NO_ERROR;
+}
+
+#if defined(ALLOC_PRAGMA)
 VP_STATUS SetVideoModeInfo(QXLExtension *dev, PVIDEO_MODE_INFORMATION video_mode, QXLMode *qxl_mode);
 #pragma alloc_text(PAGE, SetVideoModeInfo)
 #endif
@@ -550,7 +614,7 @@  VP_STATUS InitModes(QXLExtension *dev)
         return ERROR_NOT_ENOUGH_MEMORY;
     }
 #endif
-    VideoPortZeroMemory(modes_info, sizeof(VIDEO_MODE_INFORMATION) * n_modes);
+    VideoPortZeroMemory(modes_info, sizeof(VIDEO_MODE_INFORMATION) * n_modes + 2);
     for (i = 0; i < n_modes; i++) {
         error = SetVideoModeInfo(dev, &modes_info[i], &modes->modes[i]);
         if (error != NO_ERROR) {
@@ -559,7 +623,15 @@  VP_STATUS InitModes(QXLExtension *dev)
             return error;
         }
     }
-    dev->n_modes = n_modes;
+
+    /* 2 dummy modes for custom display resolution */
+    dev->custom_mode = n_modes;
+    memcpy(&modes_info[n_modes], &modes_info[0], sizeof(VIDEO_MODE_INFORMATION));
+    modes_info[n_modes].ModeIndex = n_modes;
+    memcpy(&modes_info[n_modes + 1], &modes_info[0], sizeof(VIDEO_MODE_INFORMATION));
+    modes_info[n_modes + 1].ModeIndex = n_modes + 1;
+
+    dev->n_modes = n_modes + 2;
     dev->modes = modes_info;
     DEBUG_PRINT((dev, 0, "%s OK\n", __FUNCTION__));
     return NO_ERROR;
@@ -912,6 +984,20 @@  PVIDEO_MODE_INFORMATION FindMode(QXLExtension *dev_ext, ULONG mode)
     return NULL;
 }
 
+static VP_STATUS SetCustomDisplay(QXLExtension *dev_ext, QXLEscapeSetCustomDisplay *custom_display)
+{
+    /* alternate custom mode index */
+    if (dev_ext->custom_mode == (dev_ext->n_modes - 1))
+        dev_ext->custom_mode = dev_ext->n_modes - 2;
+    else
+        dev_ext->custom_mode = dev_ext->n_modes - 1;
+
+    return FillVidModeInfo(&dev_ext->modes[dev_ext->custom_mode],
+                           custom_display->xres, custom_display->yres,
+                           custom_display->bpp,
+                           dev_ext->custom_mode, 0);
+}
+
 BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
 {
     QXLExtension *dev_ext = dev_extension;
@@ -1102,6 +1188,24 @@  BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
             driver_info->dev_id = dev_ext->rom->id;
         }
         break;
+
+    case IOCTL_QXL_SET_CUSTOM_DISPLAY: {
+            QXLEscapeSetCustomDisplay *custom_display;
+            DEBUG_PRINT((dev_ext, 0, "%s: IOCTL_QXL_SET_CUSTOM_DISPLAY\n", __FUNCTION__));
+
+            if (packet->InputBufferLength < sizeof(QXLEscapeSetCustomDisplay)) {
+                error = ERROR_INSUFFICIENT_BUFFER;
+                goto err;
+            }
+
+            custom_display = packet->InputBuffer;
+            DEBUG_PRINT((dev_ext, 0, "%s: %dx%d@%d\n", __FUNCTION__,
+                         custom_display->xres, custom_display->yres,
+                         custom_display->bpp));
+            SetCustomDisplay(dev_ext, custom_display);
+        }
+        break;
+
     default:
         DEBUG_PRINT((dev_ext, 0, "%s: invalid command 0x%lx\n", __FUNCTION__, packet->IoControlCode));
         error = ERROR_INVALID_FUNCTION;

Comments

On Fri, May 11, 2012 at 06:36:44PM +0200, Marc-André Lureau wrote:

Looks good, some comments below.

> ---
>  display/driver.c     |   32 +++++++++++++++
>  include/qxl_driver.h |    8 +++-
>  miniport/qxl.c       |  108 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 144 insertions(+), 4 deletions(-)
> 
> diff --git a/display/driver.c b/display/driver.c
> index 5c4578c..ef011ad 100644
> --- a/display/driver.c
> +++ b/display/driver.c
> @@ -45,6 +45,7 @@
>  
>  static DRVFN drv_calls[] = {
>      {INDEX_DrvDisableDriver, (PFN)DrvDisableDriver},
> +    {INDEX_DrvEscape, (PFN)DrvEscape},
>      {INDEX_DrvEnablePDEV, (PFN)DrvEnablePDEV},
>      {INDEX_DrvDisablePDEV, (PFN)DrvDisablePDEV},
>      {INDEX_DrvCompletePDEV, (PFN)DrvCompletePDEV},
> @@ -252,6 +253,37 @@ BOOL DrvEnableDriver(ULONG engine_version, ULONG enable_data_size, PDRVENABLEDAT
>      return TRUE;
>  }
>  
> +ULONG DrvEscape(SURFOBJ *pso, ULONG iEsc, ULONG cjIn, PVOID pvIn,
> +                ULONG cjOut, PVOID pvOut)
> +{
> +    PDev* pdev = pso ? (PDev*)pso->dhpdev : NULL;
> +    int RetVal = -1;
> +
> +    switch (iEsc) {
> +    case QXL_ESCAPE_SET_CUSTOM_DISPLAY: {
> +        ULONG length;
> +
> +        DEBUG_PRINT((pdev, 1, "set custom display %p\n", pdev));
> +        if (pdev == NULL)
> +            break;
> +
> +        if (EngDeviceIoControl(pdev->driver, IOCTL_QXL_SET_CUSTOM_DISPLAY,
> +                               pvIn, cjIn, NULL, 0, &length)) {
> +            DEBUG_PRINT((pdev, 0, "%s: IOCTL_QXL_SET_CUSTOM_DISPLAY failed\n", __FUNCTION__));
> +            break;
> +        }
> +        RetVal = 1;
> +        break;
> +    }
> +    default:
> +        DEBUG_PRINT((NULL, 1, "%s: unhandled escape code %d\n", __FUNCTION__, iEsc));
> +        RetVal = 0;
> +    }
> +
> +    DEBUG_PRINT((NULL, 1, "%s: end\n", __FUNCTION__));
> +    return RetVal;
> +}
> +
>  VOID DrvDisableDriver(VOID)
>  {
>      DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__));
> diff --git a/include/qxl_driver.h b/include/qxl_driver.h
> index af65916..eac6f5f 100644
> --- a/include/qxl_driver.h
> +++ b/include/qxl_driver.h
> @@ -23,6 +23,7 @@
>  #define _H_QXL_DRIVER
>  
>  #include <spice\qxl_dev.h>
> +#include <spice\qxl_windows.h>
>  
>  #if (WINVER < 0x0501)
>  #include "wdmhelper.h"
> @@ -30,12 +31,16 @@
>  
>  enum {
>      FIRST_AVIL_IOCTL_FUNC = 0x800,
> -    QXL_GET_INFO_FUNC = FIRST_AVIL_IOCTL_FUNC
> +    QXL_GET_INFO_FUNC = FIRST_AVIL_IOCTL_FUNC,
> +    QXL_SET_CUSTOM_DISPLAY
>  };
>  
>  #define IOCTL_QXL_GET_INFO \
>      CTL_CODE(FILE_DEVICE_VIDEO, QXL_GET_INFO_FUNC, METHOD_BUFFERED, FILE_ANY_ACCESS)
>  
> +#define IOCTL_QXL_SET_CUSTOM_DISPLAY \
> +    CTL_CODE(FILE_DEVICE_VIDEO, QXL_SET_CUSTOM_DISPLAY, METHOD_BUFFERED, FILE_ANY_ACCESS)
> +
>  #define QXL_DRIVER_INFO_VERSION 3
>  
>  typedef struct MemSlot {
> @@ -116,6 +121,5 @@ typedef struct QXLDriverInfo {
>      UINT64 fb_phys;
>  } QXLDriverInfo;
>  
> -
>  #endif
>  
> diff --git a/miniport/qxl.c b/miniport/qxl.c
> index aefd780..6687652 100644
> --- a/miniport/qxl.c
> +++ b/miniport/qxl.c
> @@ -83,6 +83,7 @@ typedef struct QXLExtension {
>  
>      ULONG current_mode;
>      ULONG n_modes;
> +    ULONG custom_mode;
>      PVIDEO_MODE_INFORMATION modes;
>  
>      PEVENT display_event;
> @@ -442,6 +443,69 @@ VP_STATUS Prob(QXLExtension *dev, VIDEO_PORT_CONFIG_INFO *conf_info,
>  }
>  
>  #if defined(ALLOC_PRAGMA)
> +void FillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB,
> +                    ULONG maskR, ULONG maskG, ULONG maskB);
> +#pragma alloc_text(PAGE, FillVidModeBPP)
> +#endif
> +
> +/* Fills given video mode BPP related fields */
> +void FillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB,
> +                    ULONG maskR, ULONG maskG, ULONG maskB)
> +{
> +    pMode->NumberRedBits   = bitsR;
> +    pMode->NumberGreenBits = bitsG;
> +    pMode->NumberBlueBits  = bitsB;
> +    pMode->RedMask         = maskR;
> +    pMode->GreenMask       = maskG;
> +    pMode->BlueMask        = maskB;
> +}
> +
> +#if defined(ALLOC_PRAGMA)
> +VP_STATUS FillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index, ULONG yoffset);
> +#pragma alloc_text(PAGE, FillVidModeInfo)
> +#endif
> +/* Fills given video mode structure */
> +VP_STATUS FillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index, ULONG yoffset)

What is the yoffset good for? I see you pass 0 to it anyway.

> +{
> +    if (xres <= 0 || yres <= 0)
> +        return ERROR_INVALID_DATA;
> +
> +    VideoPortZeroMemory(pMode, sizeof(VIDEO_MODE_INFORMATION));
> +
> +    /*Common entries*/
> +    pMode->Length                       = sizeof(VIDEO_MODE_INFORMATION);
> +    pMode->ModeIndex                    = index;
> +    pMode->VisScreenWidth               = xres;
> +    pMode->VisScreenHeight              = yres - yoffset;
> +    pMode->ScreenStride                 = xres * ((bpp + 7) / 8);
> +    pMode->NumberOfPlanes               = 1;
> +    pMode->BitsPerPlane                 = bpp;
> +    pMode->Frequency                    = 60;
> +    pMode->XMillimeter                  = 320;
> +    pMode->YMillimeter                  = 240;
> +    pMode->VideoMemoryBitmapWidth       = xres;
> +    pMode->VideoMemoryBitmapHeight      = yres - yoffset;
> +    pMode->DriverSpecificAttributeFlags = 0;
> +    pMode->AttributeFlags               = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;

Why VIDEO_MODE_NO_OFF_SCREEN? we don't set that elsewhere? did you see a
difference with or without it? in SetVideoModeInfo only the first two
are used.

> +
> +    /*BPP related entries*/
> +    switch (bpp)
> +    {
> +        case 16:
> +            FillVidModeBPP(pMode, 5, 6, 5, 0xF800, 0x7E0, 0x1F);
> +            break;
> +        case 24:
> +        case 32:
> +            FillVidModeBPP(pMode, 8, 8, 8, 0xFF0000, 0xFF00, 0xFF);
> +            break;
> +        default:
> +            return ERROR_INVALID_DATA;
> +    }
> +
> +    return NO_ERROR;
> +}
> +
> +#if defined(ALLOC_PRAGMA)
>  VP_STATUS SetVideoModeInfo(QXLExtension *dev, PVIDEO_MODE_INFORMATION video_mode, QXLMode *qxl_mode);
>  #pragma alloc_text(PAGE, SetVideoModeInfo)
>  #endif
> @@ -550,7 +614,7 @@ VP_STATUS InitModes(QXLExtension *dev)
>          return ERROR_NOT_ENOUGH_MEMORY;
>      }
>  #endif
> -    VideoPortZeroMemory(modes_info, sizeof(VIDEO_MODE_INFORMATION) * n_modes);
> +    VideoPortZeroMemory(modes_info, sizeof(VIDEO_MODE_INFORMATION) * n_modes + 2);
>      for (i = 0; i < n_modes; i++) {
>          error = SetVideoModeInfo(dev, &modes_info[i], &modes->modes[i]);
>          if (error != NO_ERROR) {
> @@ -559,7 +623,15 @@ VP_STATUS InitModes(QXLExtension *dev)
>              return error;
>          }
>      }
> -    dev->n_modes = n_modes;
> +
> +    /* 2 dummy modes for custom display resolution */
> +    dev->custom_mode = n_modes;
> +    memcpy(&modes_info[n_modes], &modes_info[0], sizeof(VIDEO_MODE_INFORMATION));
> +    modes_info[n_modes].ModeIndex = n_modes;
> +    memcpy(&modes_info[n_modes + 1], &modes_info[0], sizeof(VIDEO_MODE_INFORMATION));
> +    modes_info[n_modes + 1].ModeIndex = n_modes + 1;

Why 2? Is it just nice to have the previous one also appear in display
settings (I see you ping pong between them)?

> +
> +    dev->n_modes = n_modes + 2;
>      dev->modes = modes_info;
>      DEBUG_PRINT((dev, 0, "%s OK\n", __FUNCTION__));
>      return NO_ERROR;
> @@ -912,6 +984,20 @@ PVIDEO_MODE_INFORMATION FindMode(QXLExtension *dev_ext, ULONG mode)
>      return NULL;
>  }
>  
> +static VP_STATUS SetCustomDisplay(QXLExtension *dev_ext, QXLEscapeSetCustomDisplay *custom_display)
> +{
> +    /* alternate custom mode index */
> +    if (dev_ext->custom_mode == (dev_ext->n_modes - 1))
> +        dev_ext->custom_mode = dev_ext->n_modes - 2;
> +    else
> +        dev_ext->custom_mode = dev_ext->n_modes - 1;
> +
> +    return FillVidModeInfo(&dev_ext->modes[dev_ext->custom_mode],
> +                           custom_display->xres, custom_display->yres,
> +                           custom_display->bpp,
> +                           dev_ext->custom_mode, 0);
> +}
> +
>  BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
>  {
>      QXLExtension *dev_ext = dev_extension;
> @@ -1102,6 +1188,24 @@ BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
>              driver_info->dev_id = dev_ext->rom->id;
>          }
>          break;
> +
> +    case IOCTL_QXL_SET_CUSTOM_DISPLAY: {
> +            QXLEscapeSetCustomDisplay *custom_display;
> +            DEBUG_PRINT((dev_ext, 0, "%s: IOCTL_QXL_SET_CUSTOM_DISPLAY\n", __FUNCTION__));
> +
> +            if (packet->InputBufferLength < sizeof(QXLEscapeSetCustomDisplay)) {
> +                error = ERROR_INSUFFICIENT_BUFFER;
> +                goto err;
> +            }
> +
> +            custom_display = packet->InputBuffer;
> +            DEBUG_PRINT((dev_ext, 0, "%s: %dx%d@%d\n", __FUNCTION__,
> +                         custom_display->xres, custom_display->yres,
> +                         custom_display->bpp));
> +            SetCustomDisplay(dev_ext, custom_display);
> +        }
> +        break;
> +
>      default:
>          DEBUG_PRINT((dev_ext, 0, "%s: invalid command 0x%lx\n", __FUNCTION__, packet->IoControlCode));
>          error = ERROR_INVALID_FUNCTION;
> -- 
> 1.7.10.1
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel