Blob Blame History Raw
From 3f681d71d1153db53ace8156ed0083a211419347 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 11 May 2015 16:53:41 -0400
Subject: [PATCH] Force cursor update after applying configuration

The qxl kms driver has a bug where the cursor gets hidden
implicitly after a drmModeSetCrtc call.

This commit works around the bug by forcing a drmModeSetCursor2
call after the drmModeSetCrtc calls.

This is pretty hacky and won't ever go upstream.

https://bugzilla.gnome.org/show_bug.cgi?id=746078
---
 src/backends/native/meta-launcher.c            | 37 +++++++++++++++++++++++++
 src/backends/native/meta-monitor-manager-kms.c | 38 ++++++++++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/src/backends/native/meta-launcher.c b/src/backends/native/meta-launcher.c
index 54ab85c..5b124d8 100644
--- a/src/backends/native/meta-launcher.c
+++ b/src/backends/native/meta-launcher.c
@@ -75,85 +75,122 @@ get_session_proxy (GCancellable *cancellable,
 
   session_proxy = login1_session_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
                                                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
                                                          "org.freedesktop.login1",
                                                          proxy_path,
                                                          cancellable, error);
   if (!session_proxy)
     g_prefix_error(error, "Could not get session proxy: ");
 
   free (proxy_path);
 
   return session_proxy;
 }
 
 static Login1Seat *
 get_seat_proxy (GCancellable *cancellable,
                 GError      **error)
 {
   Login1Seat *seat = login1_seat_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
                                                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
                                                          "org.freedesktop.login1",
                                                          "/org/freedesktop/login1/seat/self",
                                                          cancellable, error);
   if (!seat)
     g_prefix_error(error, "Could not get seat proxy: ");
 
   return seat;
 }
 
 static void
+frame_callback (CoglOnscreen  *onscreen,
+                CoglFrameEvent event,
+                CoglFrameInfo *frame_info,
+                void          *user_data)
+{
+  CoglFrameClosure **frame_closure = user_data;
+
+  MetaBackend *backend = meta_get_backend ();
+  MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (backend);
+  CoglOnscreen *cogl_onscreen;
+
+  if (event != COGL_FRAME_EVENT_COMPLETE)
+    return;
+
+  meta_cursor_renderer_native_force_update (META_CURSOR_RENDERER_NATIVE (renderer));
+
+  cogl_onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ());
+  cogl_onscreen_remove_frame_callback (cogl_onscreen,
+                                       *frame_closure);
+
+  *frame_closure = NULL;
+}
+
+static void
 session_unpause (void)
 {
   ClutterBackend *clutter_backend;
   CoglContext *cogl_context;
   CoglDisplay *cogl_display;
 
   clutter_backend = clutter_get_default_backend ();
   cogl_context = clutter_backend_get_cogl_context (clutter_backend);
   cogl_display = cogl_context_get_display (cogl_context);
   cogl_kms_display_queue_modes_reset (cogl_display);
 
   clutter_evdev_reclaim_devices ();
   clutter_egl_thaw_master_clock ();
 
   {
     MetaBackend *backend = meta_get_backend ();
     MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (backend);
     ClutterActor *stage = meta_backend_get_stage (backend);
+    CoglOnscreen *cogl_onscreen;
+    static CoglFrameClosure *frame_closure = NULL;
 
     /* When we mode-switch back, we need to immediately queue a redraw
      * in case nothing else queued one for us, and force the cursor to
      * update. */
 
     clutter_actor_queue_redraw (stage);
     meta_cursor_renderer_native_force_update (META_CURSOR_RENDERER_NATIVE (renderer));
+
+    cogl_onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ());
+
+    if (frame_closure)
+        cogl_onscreen_remove_frame_callback (cogl_onscreen, frame_closure);
+
+    frame_closure = cogl_onscreen_add_frame_callback (cogl_onscreen,
+                                                      frame_callback,
+                                                      &frame_closure,
+                                                      NULL);
+
     meta_idle_monitor_native_reset_idletime (meta_idle_monitor_get_core ());
   }
 }
 
 static void
 session_pause (void)
 {
   clutter_evdev_release_devices ();
   clutter_egl_freeze_master_clock ();
 }
 
 static gboolean
 take_device (Login1Session *session_proxy,
              int            dev_major,
              int            dev_minor,
              int           *out_fd,
              GCancellable  *cancellable,
              GError       **error)
 {
   g_autoptr (GVariant) fd_variant = NULL;
   g_autoptr (GUnixFDList) fd_list = NULL;
   int fd = -1;
 
   if (!login1_session_call_take_device_sync (session_proxy,
                                              dev_major,
                                              dev_minor,
                                              NULL,
                                              &fd_variant,
                                              NULL, /* paused */
                                              &fd_list,
diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
index cc2b18e..b56d6d3 100644
--- a/src/backends/native/meta-monitor-manager-kms.c
+++ b/src/backends/native/meta-monitor-manager-kms.c
@@ -1,109 +1,114 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
 /*
  * Copyright (C) 2013 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  * 02111-1307, USA.
  *
  * Author: Giovanni Campagna <gcampagn@redhat.com>
  */
 
 #include "config.h"
 
 #include "meta-monitor-manager-kms.h"
 #include "meta-monitor-config.h"
+#include "backends/meta-backend-private.h"
+#include "meta-cursor-renderer-native.h"
 
 #include <string.h>
 #include <stdlib.h>
 #include <clutter/clutter.h>
 
 #include <errno.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
 #include <meta/main.h>
 #include <meta/errors.h>
 
 #include <gudev/gudev.h>
 
 typedef struct {
   drmModeConnector *connector;
 
   unsigned n_encoders;
   drmModeEncoderPtr *encoders;
   drmModeEncoderPtr  current_encoder;
 
   /* bitmasks of encoder position in the resources array */
   uint32_t encoder_mask;
   uint32_t enc_clone_mask;
 
   uint32_t dpms_prop_id;
   uint32_t edid_blob_id;
   uint32_t tile_blob_id;
 
   int suggested_x;
   int suggested_y;
   uint32_t hotplug_mode_update;
 } MetaOutputKms;
 
 typedef struct {
   uint32_t underscan_prop_id;
   uint32_t underscan_hborder_prop_id;
   uint32_t underscan_vborder_prop_id;
 } MetaCRTCKms;
 
 struct _MetaMonitorManagerKms
 {
   MetaMonitorManager parent_instance;
 
   int fd;
 
   drmModeConnector **connectors;
   unsigned int       n_connectors;
 
+  /* used to find out when configuration has been applied */
+  CoglFrameClosure *frame_closure;
+
   GUdevClient *udev;
 
   GSettings *desktop_settings;
 };
 
 struct _MetaMonitorManagerKmsClass
 {
   MetaMonitorManagerClass parent_class;
 };
 
 G_DEFINE_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META_TYPE_MONITOR_MANAGER);
 
 static void
 free_resources (MetaMonitorManagerKms *manager_kms)
 {
   unsigned i;
 
   for (i = 0; i < manager_kms->n_connectors; i++)
     drmModeFreeConnector (manager_kms->connectors[i]);
 
   g_free (manager_kms->connectors);
 }
 
 static int
 compare_outputs (const void *one,
                  const void *two)
 {
   const MetaOutput *o_one = one, *o_two = two;
 
   return strcmp (o_one->name, o_two->name);
@@ -879,70 +884,94 @@ set_underscan (MetaMonitorManagerKms *manager_kms,
     {
       drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id,
                                 DRM_MODE_OBJECT_CRTC,
                                 crtc_kms->underscan_prop_id, (uint64_t) 1);
 
       if (crtc_kms->underscan_hborder_prop_id)
         {
           uint64_t value = crtc->current_mode->width * 0.05;
           drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id,
                                     DRM_MODE_OBJECT_CRTC,
                                     crtc_kms->underscan_hborder_prop_id, value);
         }
       if (crtc_kms->underscan_vborder_prop_id)
         {
           uint64_t value = crtc->current_mode->height * 0.05;
           drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id,
                                     DRM_MODE_OBJECT_CRTC,
                                     crtc_kms->underscan_vborder_prop_id, value);
         }
 
     }
   else
     {
       drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id,
                                 DRM_MODE_OBJECT_CRTC,
                                 crtc_kms->underscan_prop_id, (uint64_t) 0);
     }
 }
 
 static void
+frame_callback (CoglOnscreen  *onscreen,
+                CoglFrameEvent event,
+                CoglFrameInfo *frame_info,
+                void          *user_data)
+{
+  MetaMonitorManagerKms *manager_kms = user_data;
+  MetaBackend *backend = meta_get_backend ();
+  MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (backend);
+  CoglOnscreen *cogl_onscreen;
+
+  if (event != COGL_FRAME_EVENT_COMPLETE)
+    return;
+
+  meta_cursor_renderer_native_force_update (META_CURSOR_RENDERER_NATIVE (renderer));
+
+  cogl_onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ());
+  cogl_onscreen_remove_frame_callback (cogl_onscreen,
+                                       manager_kms->frame_closure);
+
+  manager_kms->frame_closure = NULL;
+}
+
+static void
 meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
                                               MetaCRTCInfo       **crtcs,
                                               unsigned int         n_crtcs,
                                               MetaOutputInfo     **outputs,
                                               unsigned int         n_outputs)
 {
   MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
   ClutterBackend *backend;
   CoglContext *cogl_context;
   CoglDisplay *cogl_display;
+  CoglOnscreen *cogl_onscreen;
   unsigned i;
   GPtrArray *cogl_crtcs;
   int screen_width, screen_height;
   gboolean ok;
   GError *error;
 
   cogl_crtcs = g_ptr_array_new_full (manager->n_crtcs, (GDestroyNotify)crtc_free);
   screen_width = 0; screen_height = 0;
   for (i = 0; i < n_crtcs; i++)
     {
       MetaCRTCInfo *crtc_info = crtcs[i];
       MetaCRTC *crtc = crtc_info->crtc;
       CoglKmsCrtc *cogl_crtc;
 
       crtc->is_dirty = TRUE;
 
       cogl_crtc = g_slice_new0 (CoglKmsCrtc);
       g_ptr_array_add (cogl_crtcs, cogl_crtc);
 
       if (crtc_info->mode == NULL)
         {
           cogl_crtc->id = crtc->crtc_id;
           cogl_crtc->x = 0;
           cogl_crtc->y = 0;
           cogl_crtc->count = 0;
           memset (&cogl_crtc->mode, 0, sizeof (drmModeModeInfo));
           cogl_crtc->connectors = NULL;
           cogl_crtc->count = 0;
 
           crtc->rect.x = 0;
@@ -1024,60 +1053,69 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
       cogl_crtc->x = 0;
       cogl_crtc->y = 0;
       cogl_crtc->count = 0;
       memset (&cogl_crtc->mode, 0, sizeof (drmModeModeInfo));
       cogl_crtc->connectors = NULL;
       cogl_crtc->count = 0;
 
       crtc->rect.x = 0;
       crtc->rect.y = 0;
       crtc->rect.width = 0;
       crtc->rect.height = 0;
       crtc->current_mode = NULL;
     }
 
   backend = clutter_get_default_backend ();
   cogl_context = clutter_backend_get_cogl_context (backend);
   cogl_display = cogl_context_get_display (cogl_context);
 
   error = NULL;
   ok = cogl_kms_display_set_layout (cogl_display, screen_width, screen_height,
                                     (CoglKmsCrtc**)cogl_crtcs->pdata, cogl_crtcs->len, &error);
   g_ptr_array_unref (cogl_crtcs);
 
   if (!ok)
     {
       meta_warning ("Applying display configuration failed: %s\n", error->message);
       g_error_free (error);
       return;
     }
 
+  cogl_onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ());
+  if (manager_kms->frame_closure)
+    cogl_onscreen_remove_frame_callback (cogl_onscreen,
+                                         manager_kms->frame_closure);
+  manager_kms->frame_closure = cogl_onscreen_add_frame_callback (cogl_onscreen,
+                                                                 frame_callback,
+                                                                 manager,
+                                                                 NULL);
+
   for (i = 0; i < n_outputs; i++)
     {
       MetaOutputInfo *output_info = outputs[i];
       MetaOutput *output = output_info->output;
 
       output->is_primary = output_info->is_primary;
       output->is_presentation = output_info->is_presentation;
       output->is_underscanning = output_info->is_underscanning;
 
       set_underscan (manager_kms, output);
     }
 
   /* Disable outputs not mentioned in the list */
   for (i = 0; i < manager->n_outputs; i++)
     {
       MetaOutput *output = &manager->outputs[i];
 
       if (output->is_dirty)
         {
           output->is_dirty = FALSE;
           continue;
         }
 
       output->crtc = NULL;
       output->is_primary = FALSE;
     }
 
   manager->screen_width = screen_width;
   manager->screen_height = screen_height;
 
-- 
2.5.0