e4b3d2e
From 92777b4dfc3920edb449d0be6ead65d8460653cc Mon Sep 17 00:00:00 2001
e4b3d2e
From: Ray Strode <rstrode@redhat.com>
e4b3d2e
Date: Tue, 12 May 2015 12:51:18 -0400
e4b3d2e
Subject: [PATCH] drm/qxl: reapply cursor after SetCrtc calls
e4b3d2e
e4b3d2e
The qxl driver currently destroys and recreates the
e4b3d2e
qxl "primary" any time the first crtc is set.
e4b3d2e
e4b3d2e
A side-effect of destroying the primary is mouse state
e4b3d2e
associated with the crtc is lost, which leads to
e4b3d2e
disappearing mouse cursors on wayland sessions.
e4b3d2e
e4b3d2e
This commit changes the driver to reapply the cursor
e4b3d2e
any time SetCrtc is called. It achieves this by keeping
e4b3d2e
a copy of the cursor data on the qxl_crtc struct.
e4b3d2e
e4b3d2e
Signed-off-by: Ray Strode <rstrode@redhat.com>
e4b3d2e
e4b3d2e
https://bugzilla.redhat.com/show_bug.cgi?id=1200901
e4b3d2e
---
e4b3d2e
 drivers/gpu/drm/qxl/qxl_display.c | 98 +++++++++++++++++++++++++++++++++++++++
e4b3d2e
 drivers/gpu/drm/qxl/qxl_drv.h     |  1 +
e4b3d2e
 2 files changed, 99 insertions(+)
e4b3d2e
e4b3d2e
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
e4b3d2e
index 3aef127..34ab444 100644
e4b3d2e
--- a/drivers/gpu/drm/qxl/qxl_display.c
e4b3d2e
+++ b/drivers/gpu/drm/qxl/qxl_display.c
e4b3d2e
@@ -184,60 +184,61 @@ static struct mode_size {
e4b3d2e
 	{1440,  900},
e4b3d2e
 	{1400, 1050},
e4b3d2e
 	{1680, 1050},
e4b3d2e
 	{1600, 1200},
e4b3d2e
 	{1920, 1080},
e4b3d2e
 	{1920, 1200}
e4b3d2e
 };
e4b3d2e
 
e4b3d2e
 static int qxl_add_common_modes(struct drm_connector *connector,
e4b3d2e
                                 unsigned pwidth,
e4b3d2e
                                 unsigned pheight)
e4b3d2e
 {
e4b3d2e
 	struct drm_device *dev = connector->dev;
e4b3d2e
 	struct drm_display_mode *mode = NULL;
e4b3d2e
 	int i;
e4b3d2e
 	for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
e4b3d2e
 		mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h,
e4b3d2e
 				    60, false, false, false);
e4b3d2e
 		if (common_modes[i].w == pwidth && common_modes[i].h == pheight)
e4b3d2e
 			mode->type |= DRM_MODE_TYPE_PREFERRED;
e4b3d2e
 		drm_mode_probed_add(connector, mode);
e4b3d2e
 	}
e4b3d2e
 	return i - 1;
e4b3d2e
 }
e4b3d2e
 
e4b3d2e
 static void qxl_crtc_destroy(struct drm_crtc *crtc)
e4b3d2e
 {
e4b3d2e
 	struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc);
e4b3d2e
 
e4b3d2e
 	drm_crtc_cleanup(crtc);
e4b3d2e
+	kfree(qxl_crtc->cursor);
e4b3d2e
 	kfree(qxl_crtc);
e4b3d2e
 }
e4b3d2e
 
e4b3d2e
 static int qxl_crtc_page_flip(struct drm_crtc *crtc,
e4b3d2e
                               struct drm_framebuffer *fb,
e4b3d2e
                               struct drm_pending_vblank_event *event,
e4b3d2e
                               uint32_t page_flip_flags)
e4b3d2e
 {
e4b3d2e
 	struct drm_device *dev = crtc->dev;
e4b3d2e
 	struct qxl_device *qdev = dev->dev_private;
e4b3d2e
 	struct qxl_framebuffer *qfb_src = to_qxl_framebuffer(fb);
e4b3d2e
 	struct qxl_framebuffer *qfb_old = to_qxl_framebuffer(crtc->primary->fb);
e4b3d2e
 	struct qxl_bo *bo_old = gem_to_qxl_bo(qfb_old->obj);
e4b3d2e
 	struct qxl_bo *bo = gem_to_qxl_bo(qfb_src->obj);
e4b3d2e
 	unsigned long flags;
e4b3d2e
 	struct drm_clip_rect norect = {
e4b3d2e
 	    .x1 = 0,
e4b3d2e
 	    .y1 = 0,
e4b3d2e
 	    .x2 = fb->width,
e4b3d2e
 	    .y2 = fb->height
e4b3d2e
 	};
e4b3d2e
 	int inc = 1;
e4b3d2e
 	int one_clip_rect = 1;
e4b3d2e
 	int ret = 0;
e4b3d2e
 
e4b3d2e
 	crtc->primary->fb = fb;
e4b3d2e
 	bo_old->is_primary = false;
e4b3d2e
 	bo->is_primary = true;
e4b3d2e
 
e4b3d2e
 	ret = qxl_bo_reserve(bo, false);
e4b3d2e
@@ -269,60 +270,145 @@ static int qxl_crtc_page_flip(struct drm_crtc *crtc,
e4b3d2e
 	return 0;
e4b3d2e
 }
e4b3d2e
 
e4b3d2e
 static int
e4b3d2e
 qxl_hide_cursor(struct qxl_device *qdev)
e4b3d2e
 {
e4b3d2e
 	struct qxl_release *release;
e4b3d2e
 	struct qxl_cursor_cmd *cmd;
e4b3d2e
 	int ret;
e4b3d2e
 
e4b3d2e
 	ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
e4b3d2e
 					 &release, NULL);
e4b3d2e
 	if (ret)
e4b3d2e
 		return ret;
e4b3d2e
 
e4b3d2e
 	ret = qxl_release_reserve_list(release, true);
e4b3d2e
 	if (ret) {
e4b3d2e
 		qxl_release_free(qdev, release);
e4b3d2e
 		return ret;
e4b3d2e
 	}
e4b3d2e
 
e4b3d2e
 	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
e4b3d2e
 	cmd->type = QXL_CURSOR_HIDE;
e4b3d2e
 	qxl_release_unmap(qdev, release, &cmd->release_info);
e4b3d2e
 
e4b3d2e
 	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
e4b3d2e
 	qxl_release_fence_buffer_objects(release);
e4b3d2e
 	return 0;
e4b3d2e
 }
e4b3d2e
 
e4b3d2e
+static int qxl_crtc_stash_cursor(struct drm_crtc *crtc,
e4b3d2e
+				struct qxl_cursor *cursor)
e4b3d2e
+{
e4b3d2e
+	struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
e4b3d2e
+	size_t cursor_size;
e4b3d2e
+
e4b3d2e
+	cursor_size = sizeof(struct qxl_cursor) + cursor->data_size;
e4b3d2e
+
e4b3d2e
+	if (!qcrtc->cursor || qcrtc->cursor->data_size != cursor->data_size) {
e4b3d2e
+		kfree(qcrtc->cursor);
e4b3d2e
+		qcrtc->cursor = kmalloc(cursor_size, GFP_KERNEL);
e4b3d2e
+
e4b3d2e
+		if (!qcrtc->cursor)
e4b3d2e
+			return -ENOMEM;
e4b3d2e
+	}
e4b3d2e
+
e4b3d2e
+	memcpy(qcrtc->cursor, cursor, cursor_size);
e4b3d2e
+
e4b3d2e
+	return 0;
e4b3d2e
+}
e4b3d2e
+
e4b3d2e
+static int qxl_crtc_apply_cursor(struct drm_crtc *crtc)
e4b3d2e
+{
e4b3d2e
+	struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
e4b3d2e
+	struct drm_device *dev = crtc->dev;
e4b3d2e
+	struct qxl_device *qdev = dev->dev_private;
e4b3d2e
+	struct qxl_cursor *cursor;
e4b3d2e
+	struct qxl_cursor_cmd *cmd;
e4b3d2e
+	struct qxl_bo *cursor_bo;
e4b3d2e
+	struct qxl_release *release;
e4b3d2e
+	size_t cursor_size;
e4b3d2e
+	int ret = 0;
e4b3d2e
+
e4b3d2e
+	if (!qcrtc->cursor)
e4b3d2e
+		return 0;
e4b3d2e
+
e4b3d2e
+	cursor_size = sizeof(*qcrtc->cursor) + qcrtc->cursor->data_size;
e4b3d2e
+
e4b3d2e
+	ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
e4b3d2e
+					 QXL_RELEASE_CURSOR_CMD,
e4b3d2e
+					 &release, NULL);
e4b3d2e
+	if (ret)
e4b3d2e
+		return ret;
e4b3d2e
+
e4b3d2e
+	ret = qxl_alloc_bo_reserved(qdev, release, cursor_size, &cursor_bo);
e4b3d2e
+	if (ret)
e4b3d2e
+		goto out_free_release;
e4b3d2e
+
e4b3d2e
+	ret = qxl_release_reserve_list(release, false);
e4b3d2e
+	if (ret)
e4b3d2e
+		goto out_free_bo;
e4b3d2e
+
e4b3d2e
+	ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
e4b3d2e
+	if (ret)
e4b3d2e
+		goto out_backoff;
e4b3d2e
+
e4b3d2e
+	memcpy(cursor, qcrtc->cursor, cursor_size);
e4b3d2e
+
e4b3d2e
+	qxl_bo_kunmap(cursor_bo);
e4b3d2e
+
e4b3d2e
+	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
e4b3d2e
+	cmd->type = QXL_CURSOR_SET;
e4b3d2e
+	cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
e4b3d2e
+	cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
e4b3d2e
+
e4b3d2e
+	cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
e4b3d2e
+
e4b3d2e
+	cmd->u.set.visible = 1;
e4b3d2e
+	qxl_release_unmap(qdev, release, &cmd->release_info);
e4b3d2e
+
e4b3d2e
+	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
e4b3d2e
+	qxl_release_fence_buffer_objects(release);
e4b3d2e
+	qxl_bo_unref(&cursor_bo);
e4b3d2e
+
e4b3d2e
+	return ret;
e4b3d2e
+
e4b3d2e
+out_backoff:
e4b3d2e
+	qxl_release_backoff_reserve_list(release);
e4b3d2e
+out_free_bo:
e4b3d2e
+	qxl_bo_unref(&cursor_bo);
e4b3d2e
+out_free_release:
e4b3d2e
+	qxl_release_free(qdev, release);
e4b3d2e
+	return ret;
e4b3d2e
+}
e4b3d2e
+
e4b3d2e
 static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
e4b3d2e
 				struct drm_file *file_priv,
e4b3d2e
 				uint32_t handle,
e4b3d2e
 				uint32_t width,
e4b3d2e
 				uint32_t height, int32_t hot_x, int32_t hot_y)
e4b3d2e
 {
e4b3d2e
 	struct drm_device *dev = crtc->dev;
e4b3d2e
 	struct qxl_device *qdev = dev->dev_private;
e4b3d2e
 	struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
e4b3d2e
 	struct drm_gem_object *obj;
e4b3d2e
 	struct qxl_cursor *cursor;
e4b3d2e
 	struct qxl_cursor_cmd *cmd;
e4b3d2e
 	struct qxl_bo *cursor_bo, *user_bo;
e4b3d2e
 	struct qxl_release *release;
e4b3d2e
 	void *user_ptr;
e4b3d2e
 
e4b3d2e
 	int size = 64*64*4;
e4b3d2e
 	int ret = 0;
e4b3d2e
 	if (!handle)
e4b3d2e
 		return qxl_hide_cursor(qdev);
e4b3d2e
 
e4b3d2e
 	obj = drm_gem_object_lookup(file_priv, handle);
e4b3d2e
 	if (!obj) {
e4b3d2e
 		DRM_ERROR("cannot find cursor object\n");
e4b3d2e
 		return -ENOENT;
e4b3d2e
 	}
e4b3d2e
 
e4b3d2e
 	user_bo = gem_to_qxl_bo(obj);
e4b3d2e
 
e4b3d2e
 	ret = qxl_bo_reserve(user_bo, false);
e4b3d2e
@@ -343,60 +429,66 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
e4b3d2e
 					 &release, NULL);
e4b3d2e
 	if (ret)
e4b3d2e
 		goto out_kunmap;
e4b3d2e
 
e4b3d2e
 	ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_cursor) + size,
e4b3d2e
 			   &cursor_bo);
e4b3d2e
 	if (ret)
e4b3d2e
 		goto out_free_release;
e4b3d2e
 
e4b3d2e
 	ret = qxl_release_reserve_list(release, false);
e4b3d2e
 	if (ret)
e4b3d2e
 		goto out_free_bo;
e4b3d2e
 
e4b3d2e
 	ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
e4b3d2e
 	if (ret)
e4b3d2e
 		goto out_backoff;
e4b3d2e
 
e4b3d2e
 	cursor->header.unique = 0;
e4b3d2e
 	cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
e4b3d2e
 	cursor->header.width = 64;
e4b3d2e
 	cursor->header.height = 64;
e4b3d2e
 	cursor->header.hot_spot_x = hot_x;
e4b3d2e
 	cursor->header.hot_spot_y = hot_y;
e4b3d2e
 	cursor->data_size = size;
e4b3d2e
 	cursor->chunk.next_chunk = 0;
e4b3d2e
 	cursor->chunk.prev_chunk = 0;
e4b3d2e
 	cursor->chunk.data_size = size;
e4b3d2e
 
e4b3d2e
 	memcpy(cursor->chunk.data, user_ptr, size);
e4b3d2e
 
e4b3d2e
+	ret = qxl_crtc_stash_cursor(crtc, cursor);
e4b3d2e
+	if (ret) {
e4b3d2e
+		DRM_ERROR("cannot save cursor, may be lost on next mode set\n");
e4b3d2e
+		ret = 0;
e4b3d2e
+	}
e4b3d2e
+
e4b3d2e
 	qxl_bo_kunmap(cursor_bo);
e4b3d2e
 
e4b3d2e
 	qxl_bo_kunmap(user_bo);
e4b3d2e
 
e4b3d2e
 	qcrtc->cur_x += qcrtc->hot_spot_x - hot_x;
e4b3d2e
 	qcrtc->cur_y += qcrtc->hot_spot_y - hot_y;
e4b3d2e
 	qcrtc->hot_spot_x = hot_x;
e4b3d2e
 	qcrtc->hot_spot_y = hot_y;
e4b3d2e
 
e4b3d2e
 	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
e4b3d2e
 	cmd->type = QXL_CURSOR_SET;
e4b3d2e
 	cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
e4b3d2e
 	cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
e4b3d2e
 
e4b3d2e
 	cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
e4b3d2e
 
e4b3d2e
 	cmd->u.set.visible = 1;
e4b3d2e
 	qxl_release_unmap(qdev, release, &cmd->release_info);
e4b3d2e
 
e4b3d2e
 	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
e4b3d2e
 	qxl_release_fence_buffer_objects(release);
e4b3d2e
 
e4b3d2e
 	/* finish with the userspace bo */
e4b3d2e
 	ret = qxl_bo_reserve(user_bo, false);
e4b3d2e
 	if (!ret) {
e4b3d2e
 		qxl_bo_unpin(user_bo);
e4b3d2e
 		qxl_bo_unreserve(user_bo);
e4b3d2e
 	}
e4b3d2e
 	drm_gem_object_unreference_unlocked(obj);
e4b3d2e
 
e4b3d2e
@@ -628,60 +720,66 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
e4b3d2e
 		  x, y,
e4b3d2e
 		  mode->hdisplay, mode->vdisplay,
e4b3d2e
 		  adjusted_mode->hdisplay,
e4b3d2e
 		  adjusted_mode->vdisplay);
e4b3d2e
 
e4b3d2e
 	if (bo->is_primary == false)
e4b3d2e
 		recreate_primary = true;
e4b3d2e
 
e4b3d2e
 	if (bo->surf.stride * bo->surf.height > qdev->vram_size) {
e4b3d2e
 		DRM_ERROR("Mode doesn't fit in vram size (vgamem)");
e4b3d2e
 		return -EINVAL;
e4b3d2e
         }
e4b3d2e
 
e4b3d2e
 	ret = qxl_bo_reserve(bo, false);
e4b3d2e
 	if (ret != 0)
e4b3d2e
 		return ret;
e4b3d2e
 	ret = qxl_bo_pin(bo, bo->type, NULL);
e4b3d2e
 	if (ret != 0) {
e4b3d2e
 		qxl_bo_unreserve(bo);
e4b3d2e
 		return -EINVAL;
e4b3d2e
 	}
e4b3d2e
 	qxl_bo_unreserve(bo);
e4b3d2e
 	if (recreate_primary) {
e4b3d2e
 		qxl_io_destroy_primary(qdev);
e4b3d2e
 		qxl_io_log(qdev,
e4b3d2e
 			   "recreate primary: %dx%d,%d,%d\n",
e4b3d2e
 			   bo->surf.width, bo->surf.height,
e4b3d2e
 			   bo->surf.stride, bo->surf.format);
e4b3d2e
 		qxl_io_create_primary(qdev, 0, bo);
e4b3d2e
 		bo->is_primary = true;
e4b3d2e
+
e4b3d2e
+		ret = qxl_crtc_apply_cursor(crtc);
e4b3d2e
+		if (ret) {
e4b3d2e
+			DRM_ERROR("could not set cursor after modeset");
e4b3d2e
+			ret = 0;
e4b3d2e
+		}
e4b3d2e
 	}
e4b3d2e
 
e4b3d2e
 	if (bo->is_primary) {
e4b3d2e
 		DRM_DEBUG_KMS("setting surface_id to 0 for primary surface %d on crtc %d\n", bo->surface_id, qcrtc->index);
e4b3d2e
 		surf_id = 0;
e4b3d2e
 	} else {
e4b3d2e
 		surf_id = bo->surface_id;
e4b3d2e
 	}
e4b3d2e
 
e4b3d2e
 	if (old_bo && old_bo != bo) {
e4b3d2e
 		old_bo->is_primary = false;
e4b3d2e
 		ret = qxl_bo_reserve(old_bo, false);
e4b3d2e
 		qxl_bo_unpin(old_bo);
e4b3d2e
 		qxl_bo_unreserve(old_bo);
e4b3d2e
 	}
e4b3d2e
 
e4b3d2e
 	qxl_monitors_config_set(qdev, qcrtc->index, x, y,
e4b3d2e
 				mode->hdisplay,
e4b3d2e
 				mode->vdisplay, surf_id);
e4b3d2e
 	return 0;
e4b3d2e
 }
e4b3d2e
 
e4b3d2e
 static void qxl_crtc_prepare(struct drm_crtc *crtc)
e4b3d2e
 {
e4b3d2e
 	DRM_DEBUG("current: %dx%d+%d+%d (%d).\n",
e4b3d2e
 		  crtc->mode.hdisplay, crtc->mode.vdisplay,
e4b3d2e
 		  crtc->x, crtc->y, crtc->enabled);
e4b3d2e
 }
e4b3d2e
 
e4b3d2e
 static void qxl_crtc_commit(struct drm_crtc *crtc)
e4b3d2e
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
e4b3d2e
index 8e633ca..6b63c54 100644
e4b3d2e
--- a/drivers/gpu/drm/qxl/qxl_drv.h
e4b3d2e
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
e4b3d2e
@@ -110,60 +110,61 @@ struct qxl_bo {
e4b3d2e
 	void				*kptr;
e4b3d2e
 	int                             type;
e4b3d2e
 
e4b3d2e
 	/* Constant after initialization */
e4b3d2e
 	struct drm_gem_object		gem_base;
e4b3d2e
 	bool is_primary; /* is this now a primary surface */
e4b3d2e
 	bool hw_surf_alloc;
e4b3d2e
 	struct qxl_surface surf;
e4b3d2e
 	uint32_t surface_id;
e4b3d2e
 	struct qxl_release *surf_create;
e4b3d2e
 };
e4b3d2e
 #define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base)
e4b3d2e
 #define to_qxl_bo(tobj) container_of((tobj), struct qxl_bo, tbo)
e4b3d2e
 
e4b3d2e
 struct qxl_gem {
e4b3d2e
 	struct mutex		mutex;
e4b3d2e
 	struct list_head	objects;
e4b3d2e
 };
e4b3d2e
 
e4b3d2e
 struct qxl_bo_list {
e4b3d2e
 	struct ttm_validate_buffer tv;
e4b3d2e
 };
e4b3d2e
 
e4b3d2e
 struct qxl_crtc {
e4b3d2e
 	struct drm_crtc base;
e4b3d2e
 	int index;
e4b3d2e
 	int cur_x;
e4b3d2e
 	int cur_y;
e4b3d2e
 	int hot_spot_x;
e4b3d2e
 	int hot_spot_y;
e4b3d2e
+	struct qxl_cursor *cursor;
e4b3d2e
 };
e4b3d2e
 
e4b3d2e
 struct qxl_output {
e4b3d2e
 	int index;
e4b3d2e
 	struct drm_connector base;
e4b3d2e
 	struct drm_encoder enc;
e4b3d2e
 };
e4b3d2e
 
e4b3d2e
 struct qxl_framebuffer {
e4b3d2e
 	struct drm_framebuffer base;
e4b3d2e
 	struct drm_gem_object *obj;
e4b3d2e
 };
e4b3d2e
 
e4b3d2e
 #define to_qxl_crtc(x) container_of(x, struct qxl_crtc, base)
e4b3d2e
 #define drm_connector_to_qxl_output(x) container_of(x, struct qxl_output, base)
e4b3d2e
 #define drm_encoder_to_qxl_output(x) container_of(x, struct qxl_output, enc)
e4b3d2e
 #define to_qxl_framebuffer(x) container_of(x, struct qxl_framebuffer, base)
e4b3d2e
 
e4b3d2e
 struct qxl_mman {
e4b3d2e
 	struct ttm_bo_global_ref        bo_global_ref;
e4b3d2e
 	struct drm_global_reference	mem_global_ref;
e4b3d2e
 	bool				mem_global_referenced;
e4b3d2e
 	struct ttm_bo_device		bdev;
e4b3d2e
 };
e4b3d2e
 
e4b3d2e
 struct qxl_mode_info {
e4b3d2e
 	int num_modes;
e4b3d2e
 	struct qxl_mode *modes;
e4b3d2e
 	bool mode_config_initialized;
e4b3d2e
 
e4b3d2e
-- 
e4b3d2e
2.7.4
e4b3d2e