[mutter] MetaScreen: Add tracking of whether there are fullscreen windows



commit 5ceffe86ee82d08e7157bb7ff670e46559fdaf92
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Thu Mar 14 16:55:49 2013 -0400

    MetaScreen: Add tracking of whether there are fullscreen windows
    
    Trying to track the fullscreen status outside of Mutter, as GNOME Shell
    was doing previously, was very prone to errors, because Mutter has a
    very tricky definition of when a window is set to be fullscreen and
    *actually* acting like a fullscreen window.
    
     * Add meta_screen_get_monitor_in_fullscreen() and an
       ::in-fullscreen-changed signal. This allows an application to
       track when there are fullscreen windows on a monitor.
    
     * Do the computation of fullscreen status in a "later" function that
       runs after showing, so we properly take focus into account.
    
     * To get ordering of different phases right, add more values
       to MetaLaterType.
    
     * Add auto-minimization, similar to what was added to GNOME Shell
       earlier in this cycle - if a window is set to be fullscreen, but
       not actually fullscreen, minimize.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=649748

 src/core/screen-private.h |    4 +
 src/core/screen.c         |  157 +++++++++++++++++++++++++++++++++++++++++++++
 src/core/stack-tracker.c  |    2 +-
 src/core/stack.c          |    6 ++
 src/core/util.c           |    3 +
 src/core/window.c         |   19 +++++-
 src/meta/screen.h         |    3 +
 src/meta/util.h           |    6 ++
 8 files changed, 198 insertions(+), 2 deletions(-)
---
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index e1a7f07..56a0be4 100644
--- a/src/core/screen-private.h
+++ b/src/core/screen-private.h
@@ -46,6 +46,7 @@ struct _MetaMonitorInfo
   int number;
   MetaRectangle rect;
   gboolean is_primary;
+  gboolean in_fullscreen;
   XID output; /* The primary or first output for this crtc, None if no xrandr */
 };
 
@@ -117,6 +118,7 @@ struct _MetaScreen
   guint32 wm_cm_timestamp;
 
   guint work_area_later;
+  guint check_fullscreen_later;
 
   int rows_of_workspaces;
   int columns_of_workspaces;
@@ -201,6 +203,8 @@ void          meta_screen_get_natural_monitor_list (MetaScreen *screen,
 void          meta_screen_update_workspace_layout (MetaScreen             *screen);
 void          meta_screen_update_workspace_names  (MetaScreen             *screen);
 void          meta_screen_queue_workarea_recalc   (MetaScreen             *screen);
+void          meta_screen_queue_check_fullscreen  (MetaScreen             *screen);
+
 
 Window meta_create_offscreen_window (Display *xdisplay,
                                      Window   parent,
diff --git a/src/core/screen.c b/src/core/screen.c
index 938606f..a1fd12c 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -93,6 +93,7 @@ enum
   STARTUP_SEQUENCE_CHANGED,
   WORKAREAS_CHANGED,
   MONITORS_CHANGED,
+  IN_FULLSCREEN_CHANGED,
 
   LAST_SIGNAL
 };
@@ -247,6 +248,14 @@ meta_screen_class_init (MetaScreenClass *klass)
           NULL, NULL, NULL,
                  G_TYPE_NONE, 0);
 
+  screen_signals[IN_FULLSCREEN_CHANGED] =
+    g_signal_new ("in-fullscreen-changed",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                 G_TYPE_NONE, 0);
+
   g_object_class_install_property (object_class,
                                    PROP_N_WORKSPACES,
                                    pspec);
@@ -480,11 +489,13 @@ reload_monitor_infos (MetaScreen *screen)
       screen->monitor_infos[0].number = 0;
       screen->monitor_infos[0].rect = screen->rect;
       screen->monitor_infos[0].rect.width = screen->rect.width / 2;
+      screen->monitor_infos[0].in_fullscreen = -1;
 
       screen->monitor_infos[1].number = 1;
       screen->monitor_infos[1].rect = screen->rect;
       screen->monitor_infos[1].rect.x = screen->rect.width / 2;
       screen->monitor_infos[1].rect.width = screen->rect.width / 2;
+      screen->monitor_infos[0].in_fullscreen = -1;
     }
 
   if (screen->n_monitor_infos == 0 &&
@@ -514,6 +525,7 @@ reload_monitor_infos (MetaScreen *screen)
               screen->monitor_infos[i].rect.y = infos[i].y_org;
               screen->monitor_infos[i].rect.width = infos[i].width;
               screen->monitor_infos[i].rect.height = infos[i].height;
+              screen->monitor_infos[i].in_fullscreen = -1;
 
               meta_topic (META_DEBUG_XINERAMA,
                           "Monitor %d is %d,%d %d x %d\n",
@@ -573,6 +585,7 @@ reload_monitor_infos (MetaScreen *screen)
           
       screen->monitor_infos[0].number = 0;
       screen->monitor_infos[0].rect = screen->rect;
+      screen->monitor_infos[0].in_fullscreen = -1;
     }
 
   filter_mirrored_monitors (screen);
@@ -828,6 +841,7 @@ meta_screen_new (MetaDisplay *display,
                                                                  xroot, 
                                                                  NoEventMask);
   screen->work_area_later = 0;
+  screen->check_fullscreen_later = 0;
 
   screen->active_workspace = NULL;
   screen->workspaces = NULL;
@@ -991,6 +1005,8 @@ meta_screen_free (MetaScreen *screen,
   
   if (screen->work_area_later != 0)
     g_source_remove (screen->work_area_later);
+  if (screen->check_fullscreen_later != 0)
+    g_source_remove (screen->work_area_later);
 
   if (screen->monitor_infos)
     g_free (screen->monitor_infos);
@@ -2997,6 +3013,8 @@ meta_screen_resize (MetaScreen *screen,
   g_free (old_monitor_infos);
   g_slist_free (windows);
 
+  meta_screen_queue_check_fullscreen (screen);
+
   g_signal_emit (screen, screen_signals[MONITORS_CHANGED], 0);
 }
 
@@ -3649,3 +3667,142 @@ meta_screen_set_active_workspace_hint (MetaScreen *screen)
   meta_error_trap_pop (screen->display);
 }
 
+static gboolean
+check_fullscreen_func (gpointer data)
+{
+  MetaScreen *screen = data;
+  GSList *windows;
+  GSList *tmp;
+  GSList *fullscreen_monitors = NULL;
+  gboolean in_fullscreen_changed = FALSE;
+  int i;
+
+  screen->check_fullscreen_later = 0;
+
+  windows = meta_display_list_windows (screen->display,
+                                       META_LIST_INCLUDE_OVERRIDE_REDIRECT);
+
+  for (tmp = windows; tmp != NULL; tmp = tmp->next)
+    {
+      MetaWindow *window = tmp->data;
+      gboolean covers_monitors = FALSE;
+
+      if (window->screen != screen || window->hidden)
+        continue;
+
+      if (window->fullscreen)
+        /* The checks for determining a fullscreen window's layer are quite
+         * elaborate, and we do a poor job at keeping it dynamically up-to-date.
+         * (It depends, for example, on whether the focus window is on the
+         * same monitor as the fullscreen window.) But because we minimize
+         * fullscreen windows not in LAYER_FULLSCREEN (see below), if the
+         * layer is stale here, it's really bad, so just force recomputation for
+         * here. This is expensive, but hopefully this function won't be
+         * called too often.
+         */
+        meta_window_update_layer (window);
+
+      if (window->override_redirect)
+        {
+          /* We want to handle the case where an application is creating an
+           * override-redirect window the size of the screen (monitor) and treat
+           * it similarly to a fullscreen window, though it doesn't have fullscreen
+           * window management behavior. (Being O-R, it's not managed at all.)
+           */
+          if (meta_window_is_monitor_sized (window))
+            covers_monitors = TRUE;
+        }
+      else
+        {
+          if (window->layer == META_LAYER_FULLSCREEN)
+            covers_monitors = TRUE;
+        }
+
+      if (covers_monitors)
+        {
+          int *monitors;
+          gsize n_monitors;
+          gsize j;
+
+          monitors = meta_window_get_all_monitors (window, &n_monitors);
+          for (j = 0; j < n_monitors; j++)
+            {
+              /* + 1 to avoid NULL */
+              gpointer monitor_p = GINT_TO_POINTER(monitors[j] + 1);
+              if (!g_slist_find (fullscreen_monitors, monitor_p))
+                fullscreen_monitors = g_slist_prepend (fullscreen_monitors, monitor_p);
+            }
+
+          g_free (monitors);
+        }
+
+      /* If we find a window that is fullscreen but not in the FULLSCREEN
+       * layer, it means that we've kicked it out of the layer because
+       * we've focused another window on the same monitor. In this case
+       * it would be confusing to keep the window fullscreen and visible,
+       * so minimize it. We can't do the same thing for override-redirect
+       * windows, so we just hope the application does the right thing.
+       */
+      if (!covers_monitors && window->fullscreen)
+        {
+          meta_window_minimize (window);
+          meta_topic (META_DEBUG_WINDOW_OPS,
+                      "Minimizing %s: was fullscreen but in a lower layer\n",
+                      window->desc);
+        }
+    }
+
+  g_slist_free (windows);
+
+  for (i = 0; i < screen->n_monitor_infos; i++)
+    {
+      MetaMonitorInfo *info = &screen->monitor_infos[i];
+      gboolean in_fullscreen = g_slist_find (fullscreen_monitors, GINT_TO_POINTER (i + 1)) != NULL;
+      if (in_fullscreen != info->in_fullscreen)
+        {
+          info->in_fullscreen = in_fullscreen;
+          in_fullscreen_changed = TRUE;
+        }
+    }
+
+  g_slist_free (fullscreen_monitors);
+
+  if (in_fullscreen_changed)
+    g_signal_emit (screen, screen_signals[IN_FULLSCREEN_CHANGED], 0, NULL);
+
+  return FALSE;
+}
+
+void
+meta_screen_queue_check_fullscreen (MetaScreen *screen)
+{
+  if (!screen->check_fullscreen_later)
+    screen->check_fullscreen_later = meta_later_add (META_LATER_CHECK_FULLSCREEN,
+                                                     check_fullscreen_func,
+                                                     screen, NULL);
+}
+
+/**
+ * meta_screen_get_monitor_in_fullscreen:
+ * @screen: a #MetaScreen
+ * @monitor: the monitor number
+ *
+ * Determines whether there is a fullscreen window obscuring the specified
+ * monitor. If there is a fullscreen window, the desktop environment will
+ * typically hide any controls that might obscure the fullscreen window.
+ *
+ * You can get notification when this changes by connecting to
+ * MetaScreen::in-fullscreen-changed.
+ *
+ * Returns: %TRUE if there is a fullscreen window covering the specified monitor.
+ */
+gboolean
+meta_screen_get_monitor_in_fullscreen (MetaScreen  *screen,
+                                       int          monitor)
+{
+  g_return_val_if_fail (META_IS_SCREEN (screen), FALSE);
+  g_return_val_if_fail (monitor >= 0 && monitor < screen->n_monitor_infos, FALSE);
+
+  /* We use -1 as a flag to mean "not known yet" for notification purposes */
+  return screen->monitor_infos[monitor].in_fullscreen == TRUE;
+}
diff --git a/src/core/stack-tracker.c b/src/core/stack-tracker.c
index 2c0c046..85deca6 100644
--- a/src/core/stack-tracker.c
+++ b/src/core/stack-tracker.c
@@ -754,7 +754,7 @@ meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker)
 {
   if (tracker->sync_stack_later == 0)
     {
-      tracker->sync_stack_later = meta_later_add (META_LATER_BEFORE_REDRAW,
+      tracker->sync_stack_later = meta_later_add (META_LATER_SYNC_STACK,
                                                   stack_tracker_sync_stack_later,
                                                   tracker, NULL);
     }
diff --git a/src/core/stack.c b/src/core/stack.c
index c5fd30f..e91094d 100644
--- a/src/core/stack.c
+++ b/src/core/stack.c
@@ -395,6 +395,8 @@ get_maximum_layer_in_group (MetaWindow *window)
 static void
 compute_layer (MetaWindow *window)
 {
+  MetaStackLayer old_layer = window->layer;
+
   window->layer = get_standalone_layer (window);
   
   /* We can only do promotion-due-to-group for dialogs and other
@@ -430,6 +432,10 @@ compute_layer (MetaWindow *window)
   meta_topic (META_DEBUG_STACK, "Window %s on layer %u type = %u has_focus = %d\n",
               window->desc, window->layer,
               window->type, window->has_focus);
+
+  if (window->layer != old_layer &&
+      (old_layer == META_LAYER_FULLSCREEN || window->layer == META_LAYER_FULLSCREEN))
+    meta_screen_queue_check_fullscreen (window->screen);
 }
 
 /* Front of the layer list is the topmost window,
diff --git a/src/core/util.c b/src/core/util.c
index 2e21876..92a33e4 100644
--- a/src/core/util.c
+++ b/src/core/util.c
@@ -909,6 +909,9 @@ meta_later_add (MetaLaterType  when,
       later->source = g_idle_add_full (META_PRIORITY_RESIZE, call_idle_later, later, NULL);
       ensure_later_repaint_func ();
       break;
+    case META_LATER_CALC_SHOWING:
+    case META_LATER_CHECK_FULLSCREEN:
+    case META_LATER_SYNC_STACK:
     case META_LATER_BEFORE_REDRAW:
       ensure_later_repaint_func ();
       break;
diff --git a/src/core/window.c b/src/core/window.c
index 58c7c2e..bfcb2f3 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -1943,6 +1943,8 @@ meta_window_unmanage (MetaWindow  *window,
 
   meta_prefs_remove_listener (prefs_changed_callback, window);
 
+  meta_screen_queue_check_fullscreen (window->screen);
+
   g_signal_emit (window, window_signals[UNMANAGED], 0);
 
   g_object_unref (window);
@@ -2527,7 +2529,7 @@ meta_window_queue (MetaWindow *window, guint queuebits)
 
           const MetaLaterType window_queue_later_when[NUMBER_OF_QUEUES] =
             {
-              META_LATER_BEFORE_REDRAW, /* CALC_SHOWING */
+              META_LATER_CALC_SHOWING, /* CALC_SHOWING */
               META_LATER_RESIZE,        /* MOVE_RESIZE */
               META_LATER_BEFORE_REDRAW  /* UPDATE_ICON */
             };
@@ -3225,6 +3227,9 @@ meta_window_show (MetaWindow *window)
       invalidate_work_areas (window);
     }
 
+  if (did_show)
+    meta_screen_queue_check_fullscreen (window->screen);
+
   /*
    * Now that we have shown the window, we no longer want to consider the
    * initial timestamp in any subsequent deliberations whether to focus this
@@ -3356,6 +3361,9 @@ meta_window_hide (MetaWindow *window)
                                            not_this_one,
                                            timestamp);
     }
+
+  if (did_hide)
+    meta_screen_queue_check_fullscreen (window->screen);
 }
 
 static gboolean
@@ -4134,6 +4142,9 @@ meta_window_make_fullscreen_internal (MetaWindow  *window)
       recalc_window_features (window);
       set_net_wm_state (window);
 
+      /* For the auto-minimize feature, if we fail to get focus */
+      meta_screen_queue_check_fullscreen (window->screen);
+
       g_object_notify (G_OBJECT (window), "fullscreen");
     }
 }
@@ -5632,6 +5643,12 @@ meta_window_configure_notify (MetaWindow      *window,
   window->rect.height = event->height;
   meta_window_update_monitor (window);
 
+  /* Whether an override-redirect window is considered fullscreen depends
+   * on its geometry.
+   */
+  if (window->override_redirect)
+    meta_screen_queue_check_fullscreen (window->screen);
+
   if (!event->override_redirect && !event->send_event)
     meta_warning ("Unhandled change of windows override redirect status\n");
 
diff --git a/src/meta/screen.h b/src/meta/screen.h
index de36dba..8c120fa 100644
--- a/src/meta/screen.h
+++ b/src/meta/screen.h
@@ -82,6 +82,9 @@ void meta_screen_get_monitor_geometry (MetaScreen    *screen,
                                        int            monitor,
                                        MetaRectangle *geometry);
 
+gboolean meta_screen_get_monitor_in_fullscreen (MetaScreen  *screen,
+                                                int          monitor);
+
 int meta_screen_get_monitor_index_for_rect (MetaScreen    *screen,
                                             MetaRectangle *rect);
 
diff --git a/src/meta/util.h b/src/meta/util.h
index 9e02484..be87190 100644
--- a/src/meta/util.h
+++ b/src/meta/util.h
@@ -167,12 +167,18 @@ GPid meta_show_dialog (const char *type,
  * MetaLaterType:
  * @META_LATER_RESIZE: call in a resize processing phase that is done
  *   before GTK+ repainting (including window borders) is done.
+ * @META_LATER_CALC_SHOWING: used by Mutter to compute which windows should be mapped
+ * @META_LATER_CHECK_FULLSCREEN: used by Mutter to see if there's a fullscreen window
+ * @META_LATER_SYNC_STACK: used by Mutter to send it's idea of the stacking order to the server
  * @META_LATER_BEFORE_REDRAW: call before the stage is redrawn
  * @META_LATER_IDLE: call at a very low priority (can be blocked
  *    by running animations or redrawing applications)
  **/
 typedef enum {
   META_LATER_RESIZE,
+  META_LATER_CALC_SHOWING,
+  META_LATER_CHECK_FULLSCREEN,
+  META_LATER_SYNC_STACK,
   META_LATER_BEFORE_REDRAW,
   META_LATER_IDLE
 } MetaLaterType;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]