text: /*
   * Copyright © 2017 Keith Packard
   *
   * Permission to use, copy, modify, distribute, and sell this software and its
   * documentation for any purpose is hereby granted without fee, provided that
   * the above copyright notice appear in all copies and that both that copyright
   * notice and this permission notice appear in supporting documentation, and
   * that the name of the copyright holders not be used in advertising or
   * publicity pertaining to distribution of the software without specific,
   * written prior permission.  The copyright holders make no representations
   * about the suitability of this software for any purpose.  It is provided "as
   * is" without express or implied warranty.
   *
   * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
   * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
   * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
   * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
   * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
   * OF THIS SOFTWARE.
   */
  
  #include "util/macros.h"
  #include <stdlib.h>
  #include <stdio.h>
  #include <unistd.h>
  #include <errno.h>
  #include <string.h>
  #include <fcntl.h>
  #include <poll.h>
  #include <stdbool.h>
  #include <math.h>
  #include <xf86drm.h>
  #include <xf86drmMode.h>
  #include "drm-uapi/drm_fourcc.h"
  #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
  #include <xcb/randr.h>
  #include <X11/Xlib-xcb.h>
  #endif
  #include "util/hash_table.h"
  #include "util/list.h"
  
  #include "vk_util.h"
  #include "wsi_common_private.h"
  #include "wsi_common_display.h"
  #include "wsi_common_queue.h"
  
  #if 0
  #define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)
  #define wsi_display_debug_code(...)     __VA_ARGS__
  #else
  #define wsi_display_debug(...)
  #define wsi_display_debug_code(...)
  #endif
  
  /* These have lifetime equal to the instance, so they effectively
   * never go away. This means we must keep track of them separately
   * from all other resources.
   */
  typedef struct wsi_display_mode {
     struct list_head             list;
     struct wsi_display_connector *connector;
     bool                         valid; /* was found in most recent poll */
     bool                         preferred;
     uint32_t                     clock; /* in kHz */
     uint16_t                     hdisplay, hsync_start, hsync_end, htotal, hskew;
     uint16_t                     vdisplay, vsync_start, vsync_end, vtotal, vscan;
     uint32_t                     flags;
  } wsi_display_mode;
  
  typedef struct wsi_display_connector {
     struct list_head             list;
     struct wsi_display           *wsi;
     uint32_t                     id;
     uint32_t                     crtc_id;
     char                         *name;
     bool                         connected;
     bool                         active;
     struct list_head             display_modes;
     wsi_display_mode             *current_mode;
     drmModeModeInfo              current_drm_mode;
     uint32_t                     dpms_property;
  #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
     xcb_randr_output_t           output;
  #endif
  } wsi_display_connector;
  
  struct wsi_display {
     struct wsi_interface         base;
  
     const VkAllocationCallbacks  *alloc;
  
     int                          fd;
  
     pthread_mutex_t              wait_mutex;
     pthread_cond_t               wait_cond;
     pthread_t                    wait_thread;
  
     struct list_head             connectors; /* list of all discovered connectors */
  };
  
  #define wsi_for_each_display_mode(_mode, _conn)                 \
     list_for_each_entry_safe(struct wsi_display_mode, _mode,     \
                              &(_conn)->display_modes, list)
  
  #define wsi_for_each_connector(_conn, _dev)                             \
     list_for_each_entry_safe(struct wsi_display_connector, _conn,        \
                              &(_dev)->connectors, list)
  
  enum wsi_image_state {
     WSI_IMAGE_IDLE,
     WSI_IMAGE_DRAWING,
     WSI_IMAGE_QUEUED,
     WSI_IMAGE_FLIPPING,
     WSI_IMAGE_DISPLAYING
  };
  
  struct wsi_display_image {
     struct wsi_image             base;
     struct wsi_display_swapchain *chain;
     enum wsi_image_state         state;
     uint32_t                     fb_id;
     uint32_t                     buffer[4];
     uint64_t                     flip_sequence;
  };
  
  struct wsi_display_swapchain {
     struct wsi_swapchain         base;
     struct wsi_display           *wsi;
     VkIcdSurfaceDisplay          *surface;
     uint64_t                     flip_sequence;
     VkResult                     status;
     struct wsi_display_image     images[0];
  };
  
  struct wsi_display_fence {
     struct wsi_fence             base;
     bool                         event_received;
     bool                         destroyed;
     uint64_t                     sequence;
  };
  
  static uint64_t fence_sequence;
  
  ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR)
  ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR)
  
  static bool
  wsi_display_mode_matches_drm(wsi_display_mode *wsi,
                               drmModeModeInfoPtr drm)
  {
     return wsi->clock == drm->clock &&
        wsi->hdisplay == drm->hdisplay &&
        wsi->hsync_start == drm->hsync_start &&
        wsi->hsync_end == drm->hsync_end &&
        wsi->htotal == drm->htotal &&
        wsi->hskew == drm->hskew &&
        wsi->vdisplay == drm->vdisplay &&
        wsi->vsync_start == drm->vsync_start &&
        wsi->vsync_end == drm->vsync_end &&
        wsi->vtotal == drm->vtotal &&
        MAX2(wsi->vscan, 1) == MAX2(drm->vscan, 1) &&
        wsi->flags == drm->flags;
  }
  
  static double
  wsi_display_mode_refresh(struct wsi_display_mode *wsi)
  {
     return (double) wsi->clock * 1000.0 / ((double) wsi->htotal *
                                            (double) wsi->vtotal *
                                            (double) MAX2(wsi->vscan, 1));
  }
  
  static uint64_t wsi_rel_to_abs_time(uint64_t rel_time)
  {
     uint64_t current_time = wsi_common_get_current_time();
  
     /* check for overflow */
     if (rel_time > UINT64_MAX - current_time)
        return UINT64_MAX;
  
     return current_time + rel_time;
  }
  
  static struct wsi_display_mode *
  wsi_display_find_drm_mode(struct wsi_device *wsi_device,
                            struct wsi_display_connector *connector,
                            drmModeModeInfoPtr mode)
  {
     wsi_for_each_display_mode(display_mode, connector) {
        if (wsi_display_mode_matches_drm(display_mode, mode))
           return display_mode;
     }
     return NULL;
  }
  
  static void
  wsi_display_invalidate_connector_modes(struct wsi_device *wsi_device,
                                         struct wsi_display_connector *connector)
  {
     wsi_for_each_display_mode(display_mode, connector) {
        display_mode->valid = false;
     }
  }
  
  static VkResult
  wsi_display_register_drm_mode(struct wsi_device *wsi_device,
                                struct wsi_display_connector *connector,
                                drmModeModeInfoPtr drm_mode)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_mode *display_mode =
        wsi_display_find_drm_mode(wsi_device, connector, drm_mode);
  
     if (display_mode) {
        display_mode->valid = true;
        return VK_SUCCESS;
     }
  
     display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),
                              8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
     if (!display_mode)
        return VK_ERROR_OUT_OF_HOST_MEMORY;
  
     display_mode->connector = connector;
     display_mode->valid = true;
     display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0;
     display_mode->clock = drm_mode->clock; /* kHz */
     display_mode->hdisplay = drm_mode->hdisplay;
     display_mode->hsync_start = drm_mode->hsync_start;
     display_mode->hsync_end = drm_mode->hsync_end;
     display_mode->htotal = drm_mode->htotal;
     display_mode->hskew = drm_mode->hskew;
     display_mode->vdisplay = drm_mode->vdisplay;
     display_mode->vsync_start = drm_mode->vsync_start;
     display_mode->vsync_end = drm_mode->vsync_end;
     display_mode->vtotal = drm_mode->vtotal;
     display_mode->vscan = drm_mode->vscan;
     display_mode->flags = drm_mode->flags;
  
     list_addtail(&display_mode->list, &connector->display_modes);
     return VK_SUCCESS;
  }
  
  /*
   * Update our information about a specific connector
   */
  
  static struct wsi_display_connector *
  wsi_display_find_connector(struct wsi_device *wsi_device,
                            uint32_t connector_id)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     wsi_for_each_connector(connector, wsi) {
        if (connector->id == connector_id)
           return connector;
     }
  
     return NULL;
  }
  
  static struct wsi_display_connector *
  wsi_display_alloc_connector(struct wsi_display *wsi,
                              uint32_t connector_id)
  {
     struct wsi_display_connector *connector =
        vk_zalloc(wsi->alloc, sizeof (struct wsi_display_connector),
                  8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
  
     connector->id = connector_id;
     connector->wsi = wsi;
     connector->active = false;
     /* XXX use EDID name */
     connector->name = "monitor";
     list_inithead(&connector->display_modes);
     return connector;
  }
  
  static struct wsi_display_connector *
  wsi_display_get_connector(struct wsi_device *wsi_device,
                            uint32_t connector_id)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     if (wsi->fd < 0)
        return NULL;
  
     drmModeConnectorPtr drm_connector =
        drmModeGetConnector(wsi->fd, connector_id);
  
     if (!drm_connector)
        return NULL;
  
     struct wsi_display_connector *connector =
        wsi_display_find_connector(wsi_device, connector_id);
  
     if (!connector) {
        connector = wsi_display_alloc_connector(wsi, connector_id);
        if (!connector) {
           drmModeFreeConnector(drm_connector);
           return NULL;
        }
        list_addtail(&connector->list, &wsi->connectors);
     }
  
     connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
  
     /* Look for a DPMS property if we haven't already found one */
     for (int p = 0; connector->dpms_property == 0 &&
             p < drm_connector->count_props; p++)
     {
        drmModePropertyPtr prop = drmModeGetProperty(wsi->fd,
                                                     drm_connector->props[p]);
        if (!prop)
           continue;
        if (prop->flags & DRM_MODE_PROP_ENUM) {
           if (!strcmp(prop->name, "DPMS"))
              connector->dpms_property = drm_connector->props[p];
        }
        drmModeFreeProperty(prop);
     }
  
     /* Mark all connector modes as invalid */
     wsi_display_invalidate_connector_modes(wsi_device, connector);
  
     /*
      * List current modes, adding new ones and marking existing ones as
      * valid
      */
     for (int m = 0; m < drm_connector->count_modes; m++) {
        VkResult result = wsi_display_register_drm_mode(wsi_device,
                                                        connector,
                                                        &drm_connector->modes[m]);
        if (result != VK_SUCCESS) {
           drmModeFreeConnector(drm_connector);
           return NULL;
        }
     }
  
     drmModeFreeConnector(drm_connector);
  
     return connector;
  }
  
  #define MM_PER_PIXEL     (1.0/96.0 * 25.4)
  
  static uint32_t
  mode_size(struct wsi_display_mode *mode)
  {
     /* fortunately, these are both uint16_t, so this is easy */
     return (uint32_t) mode->hdisplay * (uint32_t) mode->vdisplay;
  }
  
  static void
  wsi_display_fill_in_display_properties(struct wsi_device *wsi_device,
                                         struct wsi_display_connector *connector,
                                         VkDisplayProperties2KHR *properties2)
  {
     assert(properties2->sType == VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR);
     VkDisplayPropertiesKHR *properties = &properties2->displayProperties;
  
     properties->display = wsi_display_connector_to_handle(connector);
     properties->displayName = connector->name;
  
     /* Find the first preferred mode and assume that's the physical
      * resolution. If there isn't a preferred mode, find the largest mode and
      * use that.
      */
  
     struct wsi_display_mode *preferred_mode = NULL, *largest_mode = NULL;
     wsi_for_each_display_mode(display_mode, connector) {
        if (!display_mode->valid)
           continue;
        if (display_mode->preferred) {
           preferred_mode = display_mode;
           break;
        }
        if (largest_mode == NULL ||
            mode_size(display_mode) > mode_size(largest_mode))
        {
           largest_mode = display_mode;
        }
     }
  
     if (preferred_mode) {
        properties->physicalResolution.width = preferred_mode->hdisplay;
        properties->physicalResolution.height = preferred_mode->vdisplay;
     } else if (largest_mode) {
        properties->physicalResolution.width = largest_mode->hdisplay;
        properties->physicalResolution.height = largest_mode->vdisplay;
     } else {
        properties->physicalResolution.width = 1024;
        properties->physicalResolution.height = 768;
     }
  
     /* Make up physical size based on 96dpi */
     properties->physicalDimensions.width =
        floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5);
     properties->physicalDimensions.height =
        floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5);
  
     properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
     properties->planeReorderPossible = VK_FALSE;
     properties->persistentContent = VK_FALSE;
  }
  
  /*
   * Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display)
   */
  VkResult
  wsi_display_get_physical_device_display_properties(
     VkPhysicalDevice physical_device,
     struct wsi_device *wsi_device,
     uint32_t *property_count,
     VkDisplayPropertiesKHR *properties)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     if (properties == NULL) {
        return wsi_display_get_physical_device_display_properties2(
              physical_device, wsi_device, property_count, NULL);
     } else {
        /* If we're actually returning properties, allocate a temporary array of
         * VkDisplayProperties2KHR structs, call properties2 to fill them out,
         * and then copy them to the client.  This seems a bit expensive but
         * wsi_display_get_physical_device_display_properties2() calls
         * drmModeGetResources() which does an ioctl and then a bunch of
         * allocations so this should get lost in the noise.
         */
        VkDisplayProperties2KHR *props2 =
           vk_zalloc(wsi->alloc, sizeof(*props2) * *property_count, 8,
                     VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
        if (props2 == NULL)
           return VK_ERROR_OUT_OF_HOST_MEMORY;
  
        for (uint32_t i = 0; i < *property_count; i++)
           props2[i].sType = VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR;
  
        VkResult result = wsi_display_get_physical_device_display_properties2(
              physical_device, wsi_device, property_count, props2);
  
        if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
           for (uint32_t i = 0; i < *property_count; i++)
              properties[i] = props2[i].displayProperties;
        }
  
        vk_free(wsi->alloc, props2);
  
        return result;
     }
  }
  
  VkResult
  wsi_display_get_physical_device_display_properties2(
     VkPhysicalDevice physical_device,
     struct wsi_device *wsi_device,
     uint32_t *property_count,
     VkDisplayProperties2KHR *properties)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     if (wsi->fd < 0)
        goto bail;
  
     drmModeResPtr mode_res = drmModeGetResources(wsi->fd);
  
     if (!mode_res)
        goto bail;
  
     VK_OUTARRAY_MAKE(conn, properties, property_count);
  
     /* Get current information */
  
     for (int c = 0; c < mode_res->count_connectors; c++) {
        struct wsi_display_connector *connector =
           wsi_display_get_connector(wsi_device, mode_res->connectors[c]);
  
        if (!connector) {
           drmModeFreeResources(mode_res);
           return VK_ERROR_OUT_OF_HOST_MEMORY;
        }
  
        if (connector->connected) {
           vk_outarray_append(&conn, prop) {
              wsi_display_fill_in_display_properties(wsi_device,
                                                     connector,
                                                     prop);
           }
        }
     }
  
     drmModeFreeResources(mode_res);
  
     return vk_outarray_status(&conn);
  
  bail:
     *property_count = 0;
     return VK_SUCCESS;
  }
  
  /*
   * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display
   */
  static void
  wsi_display_fill_in_display_plane_properties(
     struct wsi_device *wsi_device,
     struct wsi_display_connector *connector,
     VkDisplayPlaneProperties2KHR *properties)
  {
     assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR);
     VkDisplayPlanePropertiesKHR *prop = &properties->displayPlaneProperties;
  
     if (connector && connector->active) {
        prop->currentDisplay = wsi_display_connector_to_handle(connector);
        prop->currentStackIndex = 0;
     } else {
        prop->currentDisplay = VK_NULL_HANDLE;
        prop->currentStackIndex = 0;
     }
  }
  
  VkResult
  wsi_display_get_physical_device_display_plane_properties(
     VkPhysicalDevice physical_device,
     struct wsi_device *wsi_device,
     uint32_t *property_count,
     VkDisplayPlanePropertiesKHR *properties)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     VK_OUTARRAY_MAKE(conn, properties, property_count);
  
     wsi_for_each_connector(connector, wsi) {
        vk_outarray_append(&conn, prop) {
           VkDisplayPlaneProperties2KHR prop2 = {
              .sType = VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR,
           };
           wsi_display_fill_in_display_plane_properties(wsi_device, connector,
                                                        &prop2);
           *prop = prop2.displayPlaneProperties;
        }
     }
     return vk_outarray_status(&conn);
  }
  
  VkResult
  wsi_display_get_physical_device_display_plane_properties2(
     VkPhysicalDevice physical_device,
     struct wsi_device *wsi_device,
     uint32_t *property_count,
     VkDisplayPlaneProperties2KHR *properties)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     VK_OUTARRAY_MAKE(conn, properties, property_count);
  
     wsi_for_each_connector(connector, wsi) {
        vk_outarray_append(&conn, prop) {
           wsi_display_fill_in_display_plane_properties(wsi_device, connector,
                                                        prop);
        }
     }
     return vk_outarray_status(&conn);
  }
  
  /*
   * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display)
   */
  
  VkResult
  wsi_display_get_display_plane_supported_displays(
     VkPhysicalDevice physical_device,
     struct wsi_device *wsi_device,
     uint32_t plane_index,
     uint32_t *display_count,
     VkDisplayKHR *displays)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     VK_OUTARRAY_MAKE(conn, displays, display_count);
  
     int c = 0;
  
     wsi_for_each_connector(connector, wsi) {
        if (c == plane_index && connector->connected) {
           vk_outarray_append(&conn, display) {
              *display = wsi_display_connector_to_handle(connector);
           }
        }
        c++;
     }
     return vk_outarray_status(&conn);
  }
  
  /*
   * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)
   */
  
  static void
  wsi_display_fill_in_display_mode_properties(
     struct wsi_device *wsi_device,
     struct wsi_display_mode *display_mode,
     VkDisplayModeProperties2KHR *properties)
  {
     assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR);
     VkDisplayModePropertiesKHR *prop = &properties->displayModeProperties;
  
     prop->displayMode = wsi_display_mode_to_handle(display_mode);
     prop->parameters.visibleRegion.width = display_mode->hdisplay;
     prop->parameters.visibleRegion.height = display_mode->vdisplay;
     prop->parameters.refreshRate =
        (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);
  }
  
  VkResult
  wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device,
                                          struct wsi_device *wsi_device,
                                          VkDisplayKHR display,
                                          uint32_t *property_count,
                                          VkDisplayModePropertiesKHR *properties)
  {
     struct wsi_display_connector *connector =
        wsi_display_connector_from_handle(display);
  
     VK_OUTARRAY_MAKE(conn, properties, property_count);
  
     wsi_for_each_display_mode(display_mode, connector) {
        if (!display_mode->valid)
           continue;
  
        vk_outarray_append(&conn, prop) {
           VkDisplayModeProperties2KHR prop2 = {
              .sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR,
           };
           wsi_display_fill_in_display_mode_properties(wsi_device,
                                                       display_mode, &prop2);
           *prop = prop2.displayModeProperties;
        }
     }
     return vk_outarray_status(&conn);
  }
  
  VkResult
  wsi_display_get_display_mode_properties2(VkPhysicalDevice physical_device,
                                           struct wsi_device *wsi_device,
                                           VkDisplayKHR display,
                                           uint32_t *property_count,
                                           VkDisplayModeProperties2KHR *properties)
  {
     struct wsi_display_connector *connector =
        wsi_display_connector_from_handle(display);
  
     VK_OUTARRAY_MAKE(conn, properties, property_count);
  
     wsi_for_each_display_mode(display_mode, connector) {
        if (!display_mode->valid)
           continue;
  
        vk_outarray_append(&conn, prop) {
           wsi_display_fill_in_display_mode_properties(wsi_device,
                                                       display_mode, prop);
        }
     }
     return vk_outarray_status(&conn);
  }
  
  static bool
  wsi_display_mode_matches_vk(wsi_display_mode *wsi,
                              const VkDisplayModeParametersKHR *vk)
  {
     return (vk->visibleRegion.width == wsi->hdisplay &&
             vk->visibleRegion.height == wsi->vdisplay &&
             fabs(wsi_display_mode_refresh(wsi) * 1000.0 - vk->refreshRate) < 10);
  }
  
  /*
   * Implement vkCreateDisplayModeKHR (VK_KHR_display)
   */
  VkResult
  wsi_display_create_display_mode(VkPhysicalDevice physical_device,
                                  struct wsi_device *wsi_device,
                                  VkDisplayKHR display,
                                  const VkDisplayModeCreateInfoKHR *create_info,
                                  const VkAllocationCallbacks *allocator,
                                  VkDisplayModeKHR *mode)
  {
     struct wsi_display_connector *connector =
        wsi_display_connector_from_handle(display);
  
     if (create_info->flags != 0)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     /* Check and see if the requested mode happens to match an existing one and
      * return that. This makes the conformance suite happy. Doing more than
      * this would involve embedding the CVT function into the driver, which seems
      * excessive.
      */
     wsi_for_each_display_mode(display_mode, connector) {
        if (display_mode->valid) {
           if (wsi_display_mode_matches_vk(display_mode, &create_info->parameters)) {
              *mode = wsi_display_mode_to_handle(display_mode);
              return VK_SUCCESS;
           }
        }
     }
     return VK_ERROR_INITIALIZATION_FAILED;
  }
  
  /*
   * Implement vkGetDisplayPlaneCapabilities
   */
  VkResult
  wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device,
                                     struct wsi_device *wsi_device,
                                     VkDisplayModeKHR mode_khr,
                                     uint32_t plane_index,
                                     VkDisplayPlaneCapabilitiesKHR *capabilities)
  {
     struct wsi_display_mode *mode = wsi_display_mode_from_handle(mode_khr);
  
     /* XXX use actual values */
     capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
     capabilities->minSrcPosition.x = 0;
     capabilities->minSrcPosition.y = 0;
     capabilities->maxSrcPosition.x = 0;
     capabilities->maxSrcPosition.y = 0;
     capabilities->minSrcExtent.width = mode->hdisplay;
     capabilities->minSrcExtent.height = mode->vdisplay;
     capabilities->maxSrcExtent.width = mode->hdisplay;
     capabilities->maxSrcExtent.height = mode->vdisplay;
     capabilities->minDstPosition.x = 0;
     capabilities->minDstPosition.y = 0;
     capabilities->maxDstPosition.x = 0;
     capabilities->maxDstPosition.y = 0;
     capabilities->minDstExtent.width = mode->hdisplay;
     capabilities->minDstExtent.height = mode->vdisplay;
     capabilities->maxDstExtent.width = mode->hdisplay;
     capabilities->maxDstExtent.height = mode->vdisplay;
     return VK_SUCCESS;
  }
  
  VkResult
  wsi_get_display_plane_capabilities2(
     VkPhysicalDevice physical_device,
     struct wsi_device *wsi_device,
     const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
     VkDisplayPlaneCapabilities2KHR *capabilities)
  {
     assert(capabilities->sType ==
            VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR);
  
     VkResult result =
        wsi_get_display_plane_capabilities(physical_device, wsi_device,
                                           pDisplayPlaneInfo->mode,
                                           pDisplayPlaneInfo->planeIndex,
                                           &capabilities->capabilities);
  
     vk_foreach_struct(ext, capabilities->pNext) {
        switch (ext->sType) {
        case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
           VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext;
           protected->supportsProtected = VK_FALSE;
           break;
        }
  
        default:
           /* Ignored */
           break;
        }
     }
  
     return result;
  }
  
  VkResult
  wsi_create_display_surface(VkInstance instance,
                             const VkAllocationCallbacks *allocator,
                             const VkDisplaySurfaceCreateInfoKHR *create_info,
                             VkSurfaceKHR *surface_khr)
  {
     VkIcdSurfaceDisplay *surface = vk_zalloc(allocator, sizeof *surface, 8,
                                              VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
  
     if (surface == NULL)
        return VK_ERROR_OUT_OF_HOST_MEMORY;
  
     surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY;
  
     surface->displayMode = create_info->displayMode;
     surface->planeIndex = create_info->planeIndex;
     surface->planeStackIndex = create_info->planeStackIndex;
     surface->transform = create_info->transform;
     surface->globalAlpha = create_info->globalAlpha;
     surface->alphaMode = create_info->alphaMode;
     surface->imageExtent = create_info->imageExtent;
  
     *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base);
     return VK_SUCCESS;
  }
  
  
  static VkResult
  wsi_display_surface_get_support(VkIcdSurfaceBase *surface,
                                  struct wsi_device *wsi_device,
                                  uint32_t queueFamilyIndex,
                                  VkBool32* pSupported)
  {
     *pSupported = VK_TRUE;
     return VK_SUCCESS;
  }
  
  static VkResult
  wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
                                       struct wsi_device *wsi_device,
                                       VkSurfaceCapabilitiesKHR* caps)
  {
     VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
     wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
  
     caps->currentExtent.width = mode->hdisplay;
     caps->currentExtent.height = mode->vdisplay;
  
     caps->minImageExtent = (VkExtent2D) { 1, 1 };
     caps->maxImageExtent = (VkExtent2D) {
        wsi_device->maxImageDimension2D,
        wsi_device->maxImageDimension2D,
     };
  
     caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
  
     caps->minImageCount = 2;
     caps->maxImageCount = 0;
  
     caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
     caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
     caps->maxImageArrayLayers = 1;
     caps->supportedUsageFlags =
        VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
        VK_IMAGE_USAGE_SAMPLED_BIT |
        VK_IMAGE_USAGE_TRANSFER_DST_BIT |
        VK_IMAGE_USAGE_STORAGE_BIT |
        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  
     return VK_SUCCESS;
  }
  
  static VkResult
  wsi_display_surface_get_surface_counters(
     VkIcdSurfaceBase *surface_base,
     VkSurfaceCounterFlagsEXT *counters)
  {
     *counters = VK_SURFACE_COUNTER_VBLANK_EXT;
     return VK_SUCCESS;
  }
  
  static VkResult
  wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
                                        struct wsi_device *wsi_device,
                                        const void *info_next,
                                        VkSurfaceCapabilities2KHR *caps)
  {
     assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
     VkResult result;
  
     result = wsi_display_surface_get_capabilities(icd_surface, wsi_device,
                                                   &caps->surfaceCapabilities);
     if (result != VK_SUCCESS)
        return result;
  
     struct wsi_surface_supported_counters *counters =
        vk_find_struct( caps->pNext, WSI_SURFACE_SUPPORTED_COUNTERS_MESA);
  
     if (counters) {
        result = wsi_display_surface_get_surface_counters(
           icd_surface,
           &counters->supported_surface_counters);
     }
  
     return result;
  }
  
  static const struct {
     VkFormat     format;
     uint32_t     drm_format;
  } available_surface_formats[] = {
     { .format = VK_FORMAT_B8G8R8A8_SRGB, .drm_format = DRM_FORMAT_XRGB8888 },
     { .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 },
  };
  
  static VkResult
  wsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface,
                                  struct wsi_device *wsi_device,
                                  uint32_t *surface_format_count,
                                  VkSurfaceFormatKHR *surface_formats)
  {
     VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
  
     for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
        vk_outarray_append(&out, f) {
           f->format = available_surface_formats[i].format;
           f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
        }
     }
  
     return vk_outarray_status(&out);
  }
  
  static VkResult
  wsi_display_surface_get_formats2(VkIcdSurfaceBase *surface,
                                   struct wsi_device *wsi_device,
                                   const void *info_next,
                                   uint32_t *surface_format_count,
                                   VkSurfaceFormat2KHR *surface_formats)
  {
     VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
  
     for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
        vk_outarray_append(&out, f) {
           assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
           f->surfaceFormat.format = available_surface_formats[i].format;
           f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
        }
     }
  
     return vk_outarray_status(&out);
  }
  
  static VkResult
  wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface,
                                        uint32_t *present_mode_count,
                                        VkPresentModeKHR *present_modes)
  {
     VK_OUTARRAY_MAKE(conn, present_modes, present_mode_count);
  
     vk_outarray_append(&conn, present) {
        *present = VK_PRESENT_MODE_FIFO_KHR;
     }
  
     return vk_outarray_status(&conn);
  }
  
  static VkResult
  wsi_display_surface_get_present_rectangles(VkIcdSurfaceBase *surface_base,
                                             struct wsi_device *wsi_device,
                                             uint32_t* pRectCount,
                                             VkRect2D* pRects)
  {
     VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
     wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
     VK_OUTARRAY_MAKE(out, pRects, pRectCount);
  
     if (wsi_device_matches_drm_fd(wsi_device, mode->connector->wsi->fd)) {
        vk_outarray_append(&out, rect) {
           *rect = (VkRect2D) {
              .offset = { 0, 0 },
              .extent = { mode->hdisplay, mode->vdisplay },
           };
        }
     }
  
     return vk_outarray_status(&out);
  }
  
  static void
  wsi_display_destroy_buffer(struct wsi_display *wsi,
                             uint32_t buffer)
  {
     (void) drmIoctl(wsi->fd, DRM_IOCTL_GEM_CLOSE,
                     &((struct drm_gem_close) { .handle = buffer }));
  }
  
  static VkResult
  wsi_display_image_init(VkDevice device_h,
                         struct wsi_swapchain *drv_chain,
                         const VkSwapchainCreateInfoKHR *create_info,
                         const VkAllocationCallbacks *allocator,
                         struct wsi_display_image *image)
  {
     struct wsi_display_swapchain *chain =
        (struct wsi_display_swapchain *) drv_chain;
     struct wsi_display *wsi = chain->wsi;
     uint32_t drm_format = 0;
  
     for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
        if (create_info->imageFormat == available_surface_formats[i].format) {
           drm_format = available_surface_formats[i].drm_format;
           break;
        }
     }
  
     /* the application provided an invalid format, bail */
     if (drm_format == 0)
        return VK_ERROR_DEVICE_LOST;
  
     VkResult result = wsi_create_native_image(&chain->base, create_info,
                                               0, NULL, NULL,
                                               &image->base);
     if (result != VK_SUCCESS)
        return result;
  
     memset(image->buffer, 0, sizeof (image->buffer));
  
     for (unsigned int i = 0; i < image->base.num_planes; i++) {
        int ret = drmPrimeFDToHandle(wsi->fd, image->base.fds[i],
                                     &image->buffer[i]);
  
        close(image->base.fds[i]);
        image->base.fds[i] = -1;
        if (ret < 0)
           goto fail_handle;
     }
  
     image->chain = chain;
     image->state = WSI_IMAGE_IDLE;
     image->fb_id = 0;
  
     int ret = drmModeAddFB2(wsi->fd,
                             create_info->imageExtent.width,
                             create_info->imageExtent.height,
                             drm_format,
                             image->buffer,
                             image->base.row_pitches,
                             image->base.offsets,
                             &image->fb_id, 0);
  
     if (ret)
        goto fail_fb;
  
     return VK_SUCCESS;
  
  fail_fb:
  fail_handle:
     for (unsigned int i = 0; i < image->base.num_planes; i++) {
        if (image->buffer[i])
           wsi_display_destroy_buffer(wsi, image->buffer[i]);
        if (image->base.fds[i] != -1) {
           close(image->base.fds[i]);
           image->base.fds[i] = -1;
        }
     }
  
     wsi_destroy_image(&chain->base, &image->base);
  
     return VK_ERROR_OUT_OF_HOST_MEMORY;
  }
  
  static void
  wsi_display_image_finish(struct wsi_swapchain *drv_chain,
                           const VkAllocationCallbacks *allocator,
                           struct wsi_display_image *image)
  {
     struct wsi_display_swapchain *chain =
        (struct wsi_display_swapchain *) drv_chain;
     struct wsi_display *wsi = chain->wsi;
  
     drmModeRmFB(wsi->fd, image->fb_id);
     for (unsigned int i = 0; i < image->base.num_planes; i++)
        wsi_display_destroy_buffer(wsi, image->buffer[i]);
     wsi_destroy_image(&chain->base, &image->base);
  }
  
  static VkResult
  wsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain,
                                const VkAllocationCallbacks *allocator)
  {
     struct wsi_display_swapchain *chain =
        (struct wsi_display_swapchain *) drv_chain;
  
     for (uint32_t i = 0; i < chain->base.image_count; i++)
        wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);
  
     wsi_swapchain_finish(&chain->base);
     vk_free(allocator, chain);
     return VK_SUCCESS;
  }
  
  static struct wsi_image *
  wsi_display_get_wsi_image(struct wsi_swapchain *drv_chain,
                            uint32_t image_index)
  {
     struct wsi_display_swapchain *chain =
        (struct wsi_display_swapchain *) drv_chain;
  
     return &chain->images[image_index].base;
  }
  
  static void
  wsi_display_idle_old_displaying(struct wsi_display_image *active_image)
  {
     struct wsi_display_swapchain *chain = active_image->chain;
  
     wsi_display_debug("idle everyone but %ld\n",
                       active_image - &(chain->images[0]));
     for (uint32_t i = 0; i < chain->base.image_count; i++)
        if (chain->images[i].state == WSI_IMAGE_DISPLAYING &&
            &chain->images[i] != active_image)
        {
           wsi_display_debug("idle %d\n", i);
           chain->images[i].state = WSI_IMAGE_IDLE;
        }
  }
  
  static VkResult
  _wsi_display_queue_next(struct wsi_swapchain *drv_chain);
  
  static void
  wsi_display_page_flip_handler2(int fd,
                                 unsigned int frame,
                                 unsigned int sec,
                                 unsigned int usec,
                                 uint32_t crtc_id,
                                 void *data)
  {
     struct wsi_display_image *image = data;
     struct wsi_display_swapchain *chain = image->chain;
  
     wsi_display_debug("image %ld displayed at %d\n",
                       image - &(image->chain->images[0]), frame);
     image->state = WSI_IMAGE_DISPLAYING;
     wsi_display_idle_old_displaying(image);
     VkResult result = _wsi_display_queue_next(&(chain->base));
     if (result != VK_SUCCESS)
        chain->status = result;
  }
  
  static void wsi_display_fence_event_handler(struct wsi_display_fence *fence);
  
  static void wsi_display_page_flip_handler(int fd,
                                            unsigned int frame,
                                            unsigned int sec,
                                            unsigned int usec,
                                            void *data)
  {
     wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data);
  }
  
  static void wsi_display_vblank_handler(int fd, unsigned int frame,
                                         unsigned int sec, unsigned int usec,
                                         void *data)
  {
     struct wsi_display_fence *fence = data;
  
     wsi_display_fence_event_handler(fence);
  }
  
  static void wsi_display_sequence_handler(int fd, uint64_t frame,
                                           uint64_t nsec, uint64_t user_data)
  {
     struct wsi_display_fence *fence =
        (struct wsi_display_fence *) (uintptr_t) user_data;
  
     wsi_display_fence_event_handler(fence);
  }
  
  static drmEventContext event_context = {
     .version = DRM_EVENT_CONTEXT_VERSION,
     .page_flip_handler = wsi_display_page_flip_handler,
  #if DRM_EVENT_CONTEXT_VERSION >= 3
     .page_flip_handler2 = wsi_display_page_flip_handler2,
  #endif
     .vblank_handler = wsi_display_vblank_handler,
     .sequence_handler = wsi_display_sequence_handler,
  };
  
  static void *
  wsi_display_wait_thread(void *data)
  {
     struct wsi_display *wsi = data;
     struct pollfd pollfd = {
        .fd = wsi->fd,
        .events = POLLIN
     };
  
     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
     for (;;) {
        int ret = poll(&pollfd, 1, -1);
        if (ret > 0) {
           pthread_mutex_lock(&wsi->wait_mutex);
           (void) drmHandleEvent(wsi->fd, &event_context);
           pthread_mutex_unlock(&wsi->wait_mutex);
           pthread_cond_broadcast(&wsi->wait_cond);
        }
     }
     return NULL;
  }
  
  static int
  wsi_display_start_wait_thread(struct wsi_display *wsi)
  {
     if (!wsi->wait_thread) {
        int ret = pthread_create(&wsi->wait_thread, NULL,
                                 wsi_display_wait_thread, wsi);
        if (ret)
           return ret;
     }
     return 0;
  }
  
  /*
   * Wait for at least one event from the kernel to be processed.
   * Call with wait_mutex held
   */
  static int
  wsi_display_wait_for_event(struct wsi_display *wsi,
                             uint64_t timeout_ns)
  {
     int ret;
  
     ret = wsi_display_start_wait_thread(wsi);
  
     if (ret)
        return ret;
  
     struct timespec abs_timeout = {
        .tv_sec = timeout_ns / 1000000000ULL,
        .tv_nsec = timeout_ns % 1000000000ULL,
     };
  
     ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex,
                                  &abs_timeout);
  
     wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret);
     return ret;
  }
  
  static VkResult
  wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
                                 const VkAcquireNextImageInfoKHR *info,
                                 uint32_t *image_index)
  {
     struct wsi_display_swapchain *chain =
        (struct wsi_display_swapchain *)drv_chain;
     struct wsi_display *wsi = chain->wsi;
     int ret = 0;
     VkResult result = VK_SUCCESS;
  
     /* Bail early if the swapchain is broken */
     if (chain->status != VK_SUCCESS)
        return chain->status;
  
     uint64_t timeout = info->timeout;
     if (timeout != 0 && timeout != UINT64_MAX)
        timeout = wsi_rel_to_abs_time(timeout);
  
     pthread_mutex_lock(&wsi->wait_mutex);
     for (;;) {
        for (uint32_t i = 0; i < chain->base.image_count; i++) {
           if (chain->images[i].state == WSI_IMAGE_IDLE) {
              *image_index = i;
              wsi_display_debug("image %d available\n", i);
              chain->images[i].state = WSI_IMAGE_DRAWING;
              result = VK_SUCCESS;
              goto done;
           }
           wsi_display_debug("image %d state %d\n", i, chain->images[i].state);
        }
  
        if (ret == ETIMEDOUT) {
           result = VK_TIMEOUT;
           goto done;
        }
  
        ret = wsi_display_wait_for_event(wsi, timeout);
  
        if (ret && ret != ETIMEDOUT) {
           result = VK_ERROR_SURFACE_LOST_KHR;
           goto done;
        }
     }
  done:
     pthread_mutex_unlock(&wsi->wait_mutex);
  
     if (result != VK_SUCCESS)
        return result;
  
     return chain->status;
  }
  
  /*
   * Check whether there are any other connectors driven by this crtc
   */
  static bool
  wsi_display_crtc_solo(struct wsi_display *wsi,
                        drmModeResPtr mode_res,
                        drmModeConnectorPtr connector,
                        uint32_t crtc_id)
  {
     /* See if any other connectors share the same encoder */
     for (int c = 0; c < mode_res->count_connectors; c++) {
        if (mode_res->connectors[c] == connector->connector_id)
           continue;
  
        drmModeConnectorPtr other_connector =
           drmModeGetConnector(wsi->fd, mode_res->connectors[c]);
  
        if (other_connector) {
           bool match = (other_connector->encoder_id == connector->encoder_id);
           drmModeFreeConnector(other_connector);
           if (match)
              return false;
        }
     }
  
     /* See if any other encoders share the same crtc */
     for (int e = 0; e < mode_res->count_encoders; e++) {
        if (mode_res->encoders[e] == connector->encoder_id)
           continue;
  
        drmModeEncoderPtr other_encoder =
           drmModeGetEncoder(wsi->fd, mode_res->encoders[e]);
  
        if (other_encoder) {
           bool match = (other_encoder->crtc_id == crtc_id);
           drmModeFreeEncoder(other_encoder);
           if (match)
              return false;
        }
     }
     return true;
  }
  
  /*
   * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is
   * currently driving this connector and not any others. Settle for a CRTC
   * which is currently idle.
   */
  static uint32_t
  wsi_display_select_crtc(const struct wsi_display_connector *connector,
                          drmModeResPtr mode_res,
                          drmModeConnectorPtr drm_connector)
  {
     struct wsi_display *wsi = connector->wsi;
  
     /* See what CRTC is currently driving this connector */
     if (drm_connector->encoder_id) {
        drmModeEncoderPtr encoder =
           drmModeGetEncoder(wsi->fd, drm_connector->encoder_id);
  
        if (encoder) {
           uint32_t crtc_id = encoder->crtc_id;
           drmModeFreeEncoder(encoder);
           if (crtc_id) {
              if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id))
                 return crtc_id;
           }
        }
     }
     uint32_t crtc_id = 0;
     for (int c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
        drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->fd, mode_res->crtcs[c]);
        if (crtc && crtc->buffer_id == 0)
           crtc_id = crtc->crtc_id;
        drmModeFreeCrtc(crtc);
     }
     return crtc_id;
  }
  
  static VkResult
  wsi_display_setup_connector(wsi_display_connector *connector,
                              wsi_display_mode *display_mode)
  {
     struct wsi_display *wsi = connector->wsi;
  
     if (connector->current_mode == display_mode && connector->crtc_id)
        return VK_SUCCESS;
  
     VkResult result = VK_SUCCESS;
  
     drmModeResPtr mode_res = drmModeGetResources(wsi->fd);
     if (!mode_res) {
        if (errno == ENOMEM)
           result = VK_ERROR_OUT_OF_HOST_MEMORY;
        else
           result = VK_ERROR_SURFACE_LOST_KHR;
        goto bail;
     }
  
     drmModeConnectorPtr drm_connector =
        drmModeGetConnectorCurrent(wsi->fd, connector->id);
  
     if (!drm_connector) {
        if (errno == ENOMEM)
           result = VK_ERROR_OUT_OF_HOST_MEMORY;
        else
           result = VK_ERROR_SURFACE_LOST_KHR;
        goto bail_mode_res;
     }
  
     /* Pick a CRTC if we don't have one */
     if (!connector->crtc_id) {
        connector->crtc_id = wsi_display_select_crtc(connector,
                                                     mode_res, drm_connector);
        if (!connector->crtc_id) {
           result = VK_ERROR_SURFACE_LOST_KHR;
           goto bail_connector;
        }
     }
  
     if (connector->current_mode != display_mode) {
  
        /* Find the drm mode corresponding to the requested VkDisplayMode */
        drmModeModeInfoPtr drm_mode = NULL;
  
        for (int m = 0; m < drm_connector->count_modes; m++) {
           drm_mode = &drm_connector->modes[m];
           if (wsi_display_mode_matches_drm(display_mode, drm_mode))
              break;
           drm_mode = NULL;
        }
  
        if (!drm_mode) {
           result = VK_ERROR_SURFACE_LOST_KHR;
           goto bail_connector;
        }
  
        connector->current_mode = display_mode;
        connector->current_drm_mode = *drm_mode;
     }
  
  bail_connector:
     drmModeFreeConnector(drm_connector);
  bail_mode_res:
     drmModeFreeResources(mode_res);
  bail:
     return result;
  
  }
  
  static VkResult
  wsi_display_fence_wait(struct wsi_fence *fence_wsi, uint64_t timeout)
  {
     const struct wsi_device *wsi_device = fence_wsi->wsi_device;
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
  
     wsi_display_debug("%9lu wait fence %lu %ld\n",
                       pthread_self(), fence->sequence,
                       (int64_t) (timeout - wsi_common_get_current_time()));
     wsi_display_debug_code(uint64_t start_ns = wsi_common_get_current_time());
     pthread_mutex_lock(&wsi->wait_mutex);
  
     VkResult result;
     int ret = 0;
     for (;;) {
        if (fence->event_received) {
           wsi_display_debug("%9lu fence %lu passed\n",
                             pthread_self(), fence->sequence);
           result = VK_SUCCESS;
           break;
        }
  
        if (ret == ETIMEDOUT) {
           wsi_display_debug("%9lu fence %lu timeout\n",
                             pthread_self(), fence->sequence);
           result = VK_TIMEOUT;
           break;
        }
  
        ret = wsi_display_wait_for_event(wsi, timeout);
  
        if (ret && ret != ETIMEDOUT) {
           wsi_display_debug("%9lu fence %lu error\n",
                             pthread_self(), fence->sequence);
           result = VK_ERROR_DEVICE_LOST;
           break;
        }
     }
     pthread_mutex_unlock(&wsi->wait_mutex);
     wsi_display_debug("%9lu fence wait %f ms\n",
                       pthread_self(),
                       ((int64_t) (wsi_common_get_current_time() - start_ns)) /
                       1.0e6);
     return result;
  }
  
  static void
  wsi_display_fence_check_free(struct wsi_display_fence *fence)
  {
     if (fence->event_received && fence->destroyed)
        vk_free(fence->base.alloc, fence);
  }
  
  static void wsi_display_fence_event_handler(struct wsi_display_fence *fence)
  {
     fence->event_received = true;
     wsi_display_fence_check_free(fence);
  }
  
  static void
  wsi_display_fence_destroy(struct wsi_fence *fence_wsi)
  {
     struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
  
     assert(!fence->destroyed);
     fence->destroyed = true;
     wsi_display_fence_check_free(fence);
  }
  
  static struct wsi_display_fence *
  wsi_display_fence_alloc(VkDevice device,
                          const struct wsi_device *wsi_device,
                          VkDisplayKHR display,
                          const VkAllocationCallbacks *allocator)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_fence *fence =
        vk_zalloc2(wsi->alloc, allocator, sizeof (*fence),
                  8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
  
     if (!fence)
        return NULL;
  
     fence->base.device = device;
     fence->base.display = display;
     fence->base.wsi_device = wsi_device;
     fence->base.alloc = allocator ? allocator : wsi->alloc;
     fence->base.wait = wsi_display_fence_wait;
     fence->base.destroy = wsi_display_fence_destroy;
     fence->event_received = false;
     fence->destroyed = false;
     fence->sequence = ++fence_sequence;
     return fence;
  }
  
  static VkResult
  wsi_register_vblank_event(struct wsi_display_fence *fence,
                            const struct wsi_device *wsi_device,
                            VkDisplayKHR display,
                            uint32_t flags,
                            uint64_t frame_requested,
                            uint64_t *frame_queued)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_connector *connector =
        wsi_display_connector_from_handle(display);
  
     if (wsi->fd < 0)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     for (;;) {
        int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id,
                                       flags,
                                       frame_requested,
                                       frame_queued,
                                       (uintptr_t) fence);
  
        if (!ret)
           return VK_SUCCESS;
  
        if (errno != ENOMEM) {
  
           /* Something unexpected happened. Pause for a moment so the
            * application doesn't just spin and then return a failure indication
            */
  
           wsi_display_debug("queue vblank event %lu failed\n", fence->sequence);
           struct timespec delay = {
              .tv_sec = 0,
              .tv_nsec = 100000000ull,
           };
           nanosleep(&delay, NULL);
           return VK_ERROR_OUT_OF_HOST_MEMORY;
        }
  
        /* The kernel event queue is full. Wait for some events to be
         * processed and try again
         */
  
        pthread_mutex_lock(&wsi->wait_mutex);
        ret = wsi_display_wait_for_event(wsi, wsi_rel_to_abs_time(100000000ull));
        pthread_mutex_unlock(&wsi->wait_mutex);
  
        if (ret) {
           wsi_display_debug("vblank queue full, event wait failed\n");
           return VK_ERROR_OUT_OF_HOST_MEMORY;
        }
     }
  }
  
  /*
   * Check to see if the kernel has no flip queued and if there's an image
   * waiting to be displayed.
   */
  static VkResult
  _wsi_display_queue_next(struct wsi_swapchain *drv_chain)
  {
     struct wsi_display_swapchain *chain =
        (struct wsi_display_swapchain *) drv_chain;
     struct wsi_display *wsi = chain->wsi;
     VkIcdSurfaceDisplay *surface = chain->surface;
     wsi_display_mode *display_mode =
        wsi_display_mode_from_handle(surface->displayMode);
     wsi_display_connector *connector = display_mode->connector;
  
     if (wsi->fd < 0)
        return VK_ERROR_SURFACE_LOST_KHR;
  
     if (display_mode != connector->current_mode)
        connector->active = false;
  
     for (;;) {
  
        /* Check to see if there is an image to display, or if some image is
         * already queued */
  
        struct wsi_display_image *image = NULL;
  
        for (uint32_t i = 0; i < chain->base.image_count; i++) {
           struct wsi_display_image *tmp_image = &chain->images[i];
  
           switch (tmp_image->state) {
           case WSI_IMAGE_FLIPPING:
              /* already flipping, don't send another to the kernel yet */
              return VK_SUCCESS;
           case WSI_IMAGE_QUEUED:
              /* find the oldest queued */
              if (!image || tmp_image->flip_sequence < image->flip_sequence)
                 image = tmp_image;
              break;
           default:
              break;
           }
        }
  
        if (!image)
           return VK_SUCCESS;
  
        int ret;
        if (connector->active) {
           ret = drmModePageFlip(wsi->fd, connector->crtc_id, image->fb_id,
                                     DRM_MODE_PAGE_FLIP_EVENT, image);
           if (ret == 0) {
              image->state = WSI_IMAGE_FLIPPING;
              return VK_SUCCESS;
           }
           wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret));
        } else {
           ret = -EINVAL;
        }
  
        if (ret == -EINVAL) {
           VkResult result = wsi_display_setup_connector(connector, display_mode);
  
           if (result != VK_SUCCESS) {
              image->state = WSI_IMAGE_IDLE;
              return result;
           }
  
           /* XXX allow setting of position */
           ret = drmModeSetCrtc(wsi->fd, connector->crtc_id,
                                image->fb_id, 0, 0,
                                &connector->id, 1,
                                &connector->current_drm_mode);
           if (ret == 0) {
              /* Assume that the mode set is synchronous and that any
               * previous image is now idle.
               */
              image->state = WSI_IMAGE_DISPLAYING;
              wsi_display_idle_old_displaying(image);
              connector->active = true;
              return VK_SUCCESS;
           }
        }
  
        if (ret != -EACCES) {
           connector->active = false;
           image->state = WSI_IMAGE_IDLE;
           return VK_ERROR_SURFACE_LOST_KHR;
        }
  
        /* Some other VT is currently active. Sit here waiting for
         * our VT to become active again by polling once a second
         */
        usleep(1000 * 1000);
        connector->active = false;
     }
  }
  
  static VkResult
  wsi_display_queue_present(struct wsi_swapchain *drv_chain,
                            uint32_t image_index,
                            const VkPresentRegionKHR *damage)
  {
     struct wsi_display_swapchain *chain =
        (struct wsi_display_swapchain *) drv_chain;
     struct wsi_display *wsi = chain->wsi;
     struct wsi_display_image *image = &chain->images[image_index];
     VkResult result;
  
     /* Bail early if the swapchain is broken */
     if (chain->status != VK_SUCCESS)
        return chain->status;
  
     assert(image->state == WSI_IMAGE_DRAWING);
     wsi_display_debug("present %d\n", image_index);
  
     pthread_mutex_lock(&wsi->wait_mutex);
  
     image->flip_sequence = ++chain->flip_sequence;
     image->state = WSI_IMAGE_QUEUED;
  
     result = _wsi_display_queue_next(drv_chain);
     if (result != VK_SUCCESS)
        chain->status = result;
  
     pthread_mutex_unlock(&wsi->wait_mutex);
  
     if (result != VK_SUCCESS)
        return result;
  
     return chain->status;
  }
  
  static VkResult
  wsi_display_surface_create_swapchain(
     VkIcdSurfaceBase *icd_surface,
     VkDevice device,
     struct wsi_device *wsi_device,
     const VkSwapchainCreateInfoKHR *create_info,
     const VkAllocationCallbacks *allocator,
     struct wsi_swapchain **swapchain_out)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
  
     const unsigned num_images = create_info->minImageCount;
     struct wsi_display_swapchain *chain =
        vk_zalloc(allocator,
                  sizeof(*chain) + num_images * sizeof(chain->images[0]),
                  8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
  
     if (chain == NULL)
        return VK_ERROR_OUT_OF_HOST_MEMORY;
  
     VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device,
                                          create_info, allocator);
     if (result != VK_SUCCESS) {
        vk_free(allocator, chain);
        return result;
     }
  
     chain->base.destroy = wsi_display_swapchain_destroy;
     chain->base.get_wsi_image = wsi_display_get_wsi_image;
     chain->base.acquire_next_image = wsi_display_acquire_next_image;
     chain->base.queue_present = wsi_display_queue_present;
     chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);
     chain->base.image_count = num_images;
  
     chain->wsi = wsi;
     chain->status = VK_SUCCESS;
  
     chain->surface = (VkIcdSurfaceDisplay *) icd_surface;
  
     for (uint32_t image = 0; image < chain->base.image_count; image++) {
        result = wsi_display_image_init(device, &chain->base,
                                        create_info, allocator,
                                        &chain->images[image]);
        if (result != VK_SUCCESS) {
           while (image > 0) {
              --image;
              wsi_display_image_finish(&chain->base, allocator,
                                       &chain->images[image]);
           }
           vk_free(allocator, chain);
           goto fail_init_images;
        }
     }
  
     *swapchain_out = &chain->base;
  
     return VK_SUCCESS;
  
  fail_init_images:
     return result;
  }
  
  static bool
  wsi_init_pthread_cond_monotonic(pthread_cond_t *cond)
  {
     pthread_condattr_t condattr;
     bool ret = false;
  
     if (pthread_condattr_init(&condattr) != 0)
        goto fail_attr_init;
  
     if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC) != 0)
        goto fail_attr_set;
  
     if (pthread_cond_init(cond, &condattr) != 0)
        goto fail_cond_init;
  
     ret = true;
  
  fail_cond_init:
  fail_attr_set:
     pthread_condattr_destroy(&condattr);
  fail_attr_init:
     return ret;
  }
  
  
  /*
   * Local version fo the libdrm helper. Added to avoid depending on bleeding
   * edge version of the library.
   */
  static int
  local_drmIsMaster(int fd)
  {
     /* Detect master by attempting something that requires master.
      *
      * Authenticating magic tokens requires master and 0 is an
      * internal kernel detail which we could use. Attempting this on
      * a master fd would fail therefore fail with EINVAL because 0
      * is invalid.
      *
      * A non-master fd will fail with EACCES, as the kernel checks
      * for master before attempting to do anything else.
      *
      * Since we don't want to leak implementation details, use
      * EACCES.
      */
     return drmAuthMagic(fd, 0) != -EACCES;
  }
  
  VkResult
  wsi_display_init_wsi(struct wsi_device *wsi_device,
                       const VkAllocationCallbacks *alloc,
                       int display_fd)
  {
     struct wsi_display *wsi = vk_zalloc(alloc, sizeof(*wsi), 8,
                                         VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
     VkResult result;
  
     if (!wsi) {
        result = VK_ERROR_OUT_OF_HOST_MEMORY;
        goto fail;
     }
  
     wsi->fd = display_fd;
     if (wsi->fd != -1 && !local_drmIsMaster(wsi->fd))
        wsi->fd = -1;
  
     wsi->alloc = alloc;
  
     list_inithead(&wsi->connectors);
  
     int ret = pthread_mutex_init(&wsi->wait_mutex, NULL);
     if (ret) {
        result = VK_ERROR_OUT_OF_HOST_MEMORY;
        goto fail_mutex;
     }
  
     if (!wsi_init_pthread_cond_monotonic(&wsi->wait_cond)) {
        result = VK_ERROR_OUT_OF_HOST_MEMORY;
        goto fail_cond;
     }
  
     wsi->base.get_support = wsi_display_surface_get_support;
     wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2;
     wsi->base.get_formats = wsi_display_surface_get_formats;
     wsi->base.get_formats2 = wsi_display_surface_get_formats2;
     wsi->base.get_present_modes = wsi_display_surface_get_present_modes;
     wsi->base.get_present_rectangles = wsi_display_surface_get_present_rectangles;
     wsi->base.create_swapchain = wsi_display_surface_create_swapchain;
  
     wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;
  
     return VK_SUCCESS;
  
  fail_cond:
     pthread_mutex_destroy(&wsi->wait_mutex);
  fail_mutex:
     vk_free(alloc, wsi);
  fail:
     return result;
  }
  
  void
  wsi_display_finish_wsi(struct wsi_device *wsi_device,
                         const VkAllocationCallbacks *alloc)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     if (wsi) {
        wsi_for_each_connector(connector, wsi) {
           wsi_for_each_display_mode(mode, connector) {
              vk_free(wsi->alloc, mode);
           }
           vk_free(wsi->alloc, connector);
        }
  
        pthread_mutex_lock(&wsi->wait_mutex);
        if (wsi->wait_thread) {
           pthread_cancel(wsi->wait_thread);
           pthread_join(wsi->wait_thread, NULL);
        }
        pthread_mutex_unlock(&wsi->wait_mutex);
        pthread_mutex_destroy(&wsi->wait_mutex);
        pthread_cond_destroy(&wsi->wait_cond);
  
        vk_free(alloc, wsi);
     }
  }
  
  /*
   * Implement vkReleaseDisplay
   */
  VkResult
  wsi_release_display(VkPhysicalDevice            physical_device,
                      struct wsi_device           *wsi_device,
                      VkDisplayKHR                display)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     if (wsi->fd >= 0) {
        close(wsi->fd);
        wsi->fd = -1;
     }
  #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
     wsi_display_connector_from_handle(display)->output = None;
  #endif
  
     return VK_SUCCESS;
  }
  
  #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
  
  static struct wsi_display_connector *
  wsi_display_find_output(struct wsi_device *wsi_device,
                          xcb_randr_output_t output)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
  
     wsi_for_each_connector(connector, wsi) {
        if (connector->output == output)
           return connector;
     }
  
     return NULL;
  }
  
  /*
   * Given a RandR output, find the associated kernel connector_id by
   * looking at the CONNECTOR_ID property provided by the X server
   */
  
  static uint32_t
  wsi_display_output_to_connector_id(xcb_connection_t *connection,
                                     xcb_atom_t *connector_id_atom_p,
                                     xcb_randr_output_t output)
  {
     uint32_t connector_id = 0;
     xcb_atom_t connector_id_atom = *connector_id_atom_p;
  
     if (connector_id_atom == 0) {
     /* Go dig out the CONNECTOR_ID property */
        xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection,
                                                            true,
                                                            12,
                                                            "CONNECTOR_ID");
        xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection,
                                                                   ia_c,
                                                                   NULL);
        if (ia_r) {
           *connector_id_atom_p = connector_id_atom = ia_r->atom;
           free(ia_r);
        }
     }
  
     /* If there's an CONNECTOR_ID atom in the server, then there may be a
      * CONNECTOR_ID property. Otherwise, there will not be and we don't even
      * need to bother.
      */
     if (connector_id_atom) {
  
        xcb_randr_query_version_cookie_t qv_c =
           xcb_randr_query_version(connection, 1, 6);
        xcb_randr_get_output_property_cookie_t gop_c =
           xcb_randr_get_output_property(connection,
                                         output,
                                         connector_id_atom,
                                         0,
                                         0,
                                         0xffffffffUL,
                                         0,
                                         0);
        xcb_randr_query_version_reply_t *qv_r =
           xcb_randr_query_version_reply(connection, qv_c, NULL);
        free(qv_r);
        xcb_randr_get_output_property_reply_t *gop_r =
           xcb_randr_get_output_property_reply(connection, gop_c, NULL);
        if (gop_r) {
           if (gop_r->num_items == 1 && gop_r->format == 32)
              memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4);
           free(gop_r);
        }
     }
     return connector_id;
  }
  
  static bool
  wsi_display_check_randr_version(xcb_connection_t *connection)
  {
     xcb_randr_query_version_cookie_t qv_c =
        xcb_randr_query_version(connection, 1, 6);
     xcb_randr_query_version_reply_t *qv_r =
        xcb_randr_query_version_reply(connection, qv_c, NULL);
     bool ret = false;
  
     if (!qv_r)
        return false;
  
     /* Check for version 1.6 or newer */
     ret = (qv_r->major_version > 1 ||
            (qv_r->major_version == 1 && qv_r->minor_version >= 6));
  
     free(qv_r);
     return ret;
  }
  
  /*
   * Given a kernel connector id, find the associated RandR output using the
   * CONNECTOR_ID property
   */
  
  static xcb_randr_output_t
  wsi_display_connector_id_to_output(xcb_connection_t *connection,
                                     uint32_t connector_id)
  {
     if (!wsi_display_check_randr_version(connection))
        return 0;
  
     const xcb_setup_t *setup = xcb_get_setup(connection);
  
     xcb_atom_t connector_id_atom = 0;
     xcb_randr_output_t output = 0;
  
     /* Search all of the screens for the provided output */
     xcb_screen_iterator_t iter;
     for (iter = xcb_setup_roots_iterator(setup);
          output == 0 && iter.rem;
          xcb_screen_next(&iter))
     {
        xcb_randr_get_screen_resources_cookie_t gsr_c =
           xcb_randr_get_screen_resources(connection, iter.data->root);
        xcb_randr_get_screen_resources_reply_t *gsr_r =
           xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
  
        if (!gsr_r)
           return 0;
  
        xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
        int o;
  
        for (o = 0; o < gsr_r->num_outputs; o++) {
           if (wsi_display_output_to_connector_id(connection,
                                                  &connector_id_atom, ro[o])
               == connector_id)
           {
              output = ro[o];
              break;
           }
        }
        free(gsr_r);
     }
     return output;
  }
  
  /*
   * Given a RandR output, find out which screen it's associated with
   */
  static xcb_window_t
  wsi_display_output_to_root(xcb_connection_t *connection,
                             xcb_randr_output_t output)
  {
     if (!wsi_display_check_randr_version(connection))
        return 0;
  
     const xcb_setup_t *setup = xcb_get_setup(connection);
     xcb_window_t root = 0;
  
     /* Search all of the screens for the provided output */
     for (xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
          root == 0 && iter.rem;
          xcb_screen_next(&iter))
     {
        xcb_randr_get_screen_resources_cookie_t gsr_c =
           xcb_randr_get_screen_resources(connection, iter.data->root);
        xcb_randr_get_screen_resources_reply_t *gsr_r =
           xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
  
        if (!gsr_r)
           return 0;
  
        xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
  
        for (int o = 0; o < gsr_r->num_outputs; o++) {
           if (ro[o] == output) {
              root = iter.data->root;
              break;
           }
        }
        free(gsr_r);
     }
     return root;
  }
  
  static bool
  wsi_display_mode_matches_x(struct wsi_display_mode *wsi,
                             xcb_randr_mode_info_t *xcb)
  {
     return wsi->clock == (xcb->dot_clock + 500) / 1000 &&
        wsi->hdisplay == xcb->width &&
        wsi->hsync_start == xcb->hsync_start &&
        wsi->hsync_end == xcb->hsync_end &&
        wsi->htotal == xcb->htotal &&
        wsi->hskew == xcb->hskew &&
        wsi->vdisplay == xcb->height &&
        wsi->vsync_start == xcb->vsync_start &&
        wsi->vsync_end == xcb->vsync_end &&
        wsi->vtotal == xcb->vtotal &&
        wsi->vscan <= 1 &&
        wsi->flags == xcb->mode_flags;
  }
  
  static struct wsi_display_mode *
  wsi_display_find_x_mode(struct wsi_device *wsi_device,
                          struct wsi_display_connector *connector,
                          xcb_randr_mode_info_t *mode)
  {
     wsi_for_each_display_mode(display_mode, connector) {
        if (wsi_display_mode_matches_x(display_mode, mode))
           return display_mode;
     }
     return NULL;
  }
  
  static VkResult
  wsi_display_register_x_mode(struct wsi_device *wsi_device,
                              struct wsi_display_connector *connector,
                              xcb_randr_mode_info_t *x_mode,
                              bool preferred)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_mode *display_mode =
        wsi_display_find_x_mode(wsi_device, connector, x_mode);
  
     if (display_mode) {
        display_mode->valid = true;
        return VK_SUCCESS;
     }
  
     display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),
                              8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
     if (!display_mode)
        return VK_ERROR_OUT_OF_HOST_MEMORY;
  
     display_mode->connector = connector;
     display_mode->valid = true;
     display_mode->preferred = preferred;
     display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */
     display_mode->hdisplay = x_mode->width;
     display_mode->hsync_start = x_mode->hsync_start;
     display_mode->hsync_end = x_mode->hsync_end;
     display_mode->htotal = x_mode->htotal;
     display_mode->hskew = x_mode->hskew;
     display_mode->vdisplay = x_mode->height;
     display_mode->vsync_start = x_mode->vsync_start;
     display_mode->vsync_end = x_mode->vsync_end;
     display_mode->vtotal = x_mode->vtotal;
     display_mode->vscan = 0;
     display_mode->flags = x_mode->mode_flags;
  
     list_addtail(&display_mode->list, &connector->display_modes);
     return VK_SUCCESS;
  }
  
  static struct wsi_display_connector *
  wsi_display_get_output(struct wsi_device *wsi_device,
                         xcb_connection_t *connection,
                         xcb_randr_output_t output)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_connector *connector;
     uint32_t connector_id;
  
     xcb_window_t root = wsi_display_output_to_root(connection, output);
     if (!root)
        return NULL;
  
     /* See if we already have a connector for this output */
     connector = wsi_display_find_output(wsi_device, output);
  
     if (!connector) {
        xcb_atom_t connector_id_atom = 0;
  
        /*
         * Go get the kernel connector ID for this X output
         */
        connector_id = wsi_display_output_to_connector_id(connection,
                                                          &connector_id_atom,
                                                          output);
  
        /* Any X server with lease support will have this atom */
        if (!connector_id) {
           return NULL;
        }
  
        /* See if we already have a connector for this id */
        connector = wsi_display_find_connector(wsi_device, connector_id);
  
        if (connector == NULL) {
           connector = wsi_display_alloc_connector(wsi, connector_id);
           if (!connector) {
              return NULL;
           }
           list_addtail(&connector->list, &wsi->connectors);
        }
        connector->output = output;
     }
  
     xcb_randr_get_screen_resources_cookie_t src =
        xcb_randr_get_screen_resources(connection, root);
     xcb_randr_get_output_info_cookie_t oic =
        xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME);
     xcb_randr_get_screen_resources_reply_t *srr =
        xcb_randr_get_screen_resources_reply(connection, src, NULL);
     xcb_randr_get_output_info_reply_t *oir =
        xcb_randr_get_output_info_reply(connection, oic, NULL);
  
     if (oir && srr) {
        /* Get X modes and add them */
  
        connector->connected =
           oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED;
  
        wsi_display_invalidate_connector_modes(wsi_device, connector);
  
        xcb_randr_mode_t *x_modes = xcb_randr_get_output_info_modes(oir);
        for (int m = 0; m < oir->num_modes; m++) {
           xcb_randr_mode_info_iterator_t i =
              xcb_randr_get_screen_resources_modes_iterator(srr);
           while (i.rem) {
              xcb_randr_mode_info_t *mi = i.data;
              if (mi->id == x_modes[m]) {
                 VkResult result = wsi_display_register_x_mode(
                    wsi_device, connector, mi, m < oir->num_preferred);
                 if (result != VK_SUCCESS) {
                    free(oir);
                    free(srr);
                    return NULL;
                 }
                 break;
              }
              xcb_randr_mode_info_next(&i);
           }
        }
     }
  
     free(oir);
     free(srr);
     return connector;
  }
  
  static xcb_randr_crtc_t
  wsi_display_find_crtc_for_output(xcb_connection_t *connection,
                                   xcb_window_t root,
                                   xcb_randr_output_t output)
  {
     xcb_randr_get_screen_resources_cookie_t gsr_c =
        xcb_randr_get_screen_resources(connection, root);
     xcb_randr_get_screen_resources_reply_t *gsr_r =
        xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
  
     if (!gsr_r)
        return 0;
  
     xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r);
     xcb_randr_crtc_t idle_crtc = 0;
     xcb_randr_crtc_t active_crtc = 0;
  
     /* Find either a crtc already connected to the desired output or idle */
     for (int c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) {
        xcb_randr_get_crtc_info_cookie_t gci_c =
           xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp);
        xcb_randr_get_crtc_info_reply_t *gci_r =
           xcb_randr_get_crtc_info_reply(connection, gci_c, NULL);
  
        if (gci_r) {
           if (gci_r->mode) {
              int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r);
              xcb_randr_output_t *outputs =
                 xcb_randr_get_crtc_info_outputs(gci_r);
  
              if (num_outputs == 1 && outputs[0] == output)
                 active_crtc = rc[c];
  
           } else if (idle_crtc == 0) {
              int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r);
              xcb_randr_output_t *possible =
                 xcb_randr_get_crtc_info_possible(gci_r);
  
              for (int p = 0; p < num_possible; p++)
                 if (possible[p] == output) {
                    idle_crtc = rc[c];
                    break;
                 }
           }
           free(gci_r);
        }
     }
     free(gsr_r);
  
     if (active_crtc)
        return active_crtc;
     return idle_crtc;
  }
  
  VkResult
  wsi_acquire_xlib_display(VkPhysicalDevice physical_device,
                           struct wsi_device *wsi_device,
                           Display *dpy,
                           VkDisplayKHR display)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     xcb_connection_t *connection = XGetXCBConnection(dpy);
     struct wsi_display_connector *connector =
        wsi_display_connector_from_handle(display);
     xcb_window_t root;
  
     /* XXX no support for multiple leases yet */
     if (wsi->fd >= 0)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     if (!connector->output) {
        connector->output = wsi_display_connector_id_to_output(connection,
                                                               connector->id);
  
        /* Check and see if we found the output */
        if (!connector->output)
           return VK_ERROR_INITIALIZATION_FAILED;
     }
  
     root = wsi_display_output_to_root(connection, connector->output);
     if (!root)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection,
                                                              root,
                                                              connector->output);
  
     if (!crtc)
        return VK_ERROR_INITIALIZATION_FAILED;
  
  #ifdef HAVE_DRI3_MODIFIERS
     xcb_randr_lease_t lease = xcb_generate_id(connection);
     xcb_randr_create_lease_cookie_t cl_c =
        xcb_randr_create_lease(connection, root, lease, 1, 1,
                               &crtc, &connector->output);
     xcb_randr_create_lease_reply_t *cl_r =
        xcb_randr_create_lease_reply(connection, cl_c, NULL);
     if (!cl_r)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     int fd = -1;
     if (cl_r->nfd > 0) {
        int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r);
  
        fd = rcl_f[0];
     }
     free (cl_r);
     if (fd < 0)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     wsi->fd = fd;
  #endif
  
     return VK_SUCCESS;
  }
  
  VkResult
  wsi_get_randr_output_display(VkPhysicalDevice physical_device,
                               struct wsi_device *wsi_device,
                               Display *dpy,
                               RROutput output,
                               VkDisplayKHR *display)
  {
     xcb_connection_t *connection = XGetXCBConnection(dpy);
     struct wsi_display_connector *connector =
        wsi_display_get_output(wsi_device, connection, (xcb_randr_output_t) output);
  
     if (connector)
        *display = wsi_display_connector_to_handle(connector);
     else
        *display = VK_NULL_HANDLE;
     return VK_SUCCESS;
  }
  
  #endif
  
  /* VK_EXT_display_control */
  VkResult
  wsi_display_power_control(VkDevice device,
                            struct wsi_device *wsi_device,
                            VkDisplayKHR display,
                            const VkDisplayPowerInfoEXT *display_power_info)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_connector *connector =
        wsi_display_connector_from_handle(display);
     int mode;
  
     if (wsi->fd < 0)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     switch (display_power_info->powerState) {
     case VK_DISPLAY_POWER_STATE_OFF_EXT:
        mode = DRM_MODE_DPMS_OFF;
        break;
     case VK_DISPLAY_POWER_STATE_SUSPEND_EXT:
        mode = DRM_MODE_DPMS_SUSPEND;
        break;
     default:
        mode = DRM_MODE_DPMS_ON;
        break;
     }
     drmModeConnectorSetProperty(wsi->fd,
                                 connector->id,
                                 connector->dpms_property,
                                 mode);
     return VK_SUCCESS;
  }
  
  VkResult
  wsi_register_device_event(VkDevice device,
                            struct wsi_device *wsi_device,
                            const VkDeviceEventInfoEXT *device_event_info,
                            const VkAllocationCallbacks *allocator,
                            struct wsi_fence **fence_p)
  {
     return VK_ERROR_FEATURE_NOT_PRESENT;
  }
  
  VkResult
  wsi_register_display_event(VkDevice device,
                             struct wsi_device *wsi_device,
                             VkDisplayKHR display,
                             const VkDisplayEventInfoEXT *display_event_info,
                             const VkAllocationCallbacks *allocator,
                             struct wsi_fence **fence_p)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_fence *fence;
     VkResult ret;
  
     switch (display_event_info->displayEvent) {
     case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT:
  
        fence = wsi_display_fence_alloc(device, wsi_device, display, allocator);
  
        if (!fence)
           return VK_ERROR_OUT_OF_HOST_MEMORY;
  
        ret = wsi_register_vblank_event(fence, wsi_device, display,
                                        DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL);
  
        if (ret == VK_SUCCESS)
           *fence_p = &fence->base;
        else if (fence != NULL)
           vk_free2(wsi->alloc, allocator, fence);
  
        break;
     default:
        ret = VK_ERROR_FEATURE_NOT_PRESENT;
        break;
     }
  
     return ret;
  }
  
  
  VkResult
  wsi_get_swapchain_counter(VkDevice device,
                            struct wsi_device *wsi_device,
                            VkSwapchainKHR _swapchain,
                            VkSurfaceCounterFlagBitsEXT flag_bits,
                            uint64_t *value)
  {
     struct wsi_display *wsi =
        (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
     struct wsi_display_swapchain *swapchain =
        (struct wsi_display_swapchain *) wsi_swapchain_from_handle(_swapchain);
     struct wsi_display_connector *connector =
        wsi_display_mode_from_handle(swapchain->surface->displayMode)->connector;
  
     if (wsi->fd < 0)
        return VK_ERROR_INITIALIZATION_FAILED;
  
     if (!connector->active) {
        *value = 0;
        return VK_SUCCESS;
     }
  
     int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id, value, NULL);
     if (ret)
        *value = 0;
  
     return VK_SUCCESS;
  }
  
  