Bug 1350643 - Part 2: Use gdk functions to enumerate monitors instead of Xinerama. r=karlt
authorSamael Wang <freesamael@gmail.com>
Tue, 06 Jun 2017 17:57:54 +0800
changeset 418572 dce3ec16f2b530c75009fd1f6c4317d05df7e396
parent 418571 caf2bbdc4100e8da2cb6ed82d729c9ea01b4a8f7
child 418573 abfd88baf39108831d7a9337f841ecb623b5594c
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs1350643
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1350643 - Part 2: Use gdk functions to enumerate monitors instead of Xinerama. r=karlt MozReview-Commit-ID: D9f65oZMBuV
widget/gtk/ScreenHelperGTK.cpp
widget/gtk/ScreenHelperGTK.h
widget/gtk/mozgtk/mozgtk.c
--- a/widget/gtk/ScreenHelperGTK.cpp
+++ b/widget/gtk/ScreenHelperGTK.cpp
@@ -2,40 +2,26 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ScreenHelperGTK.h"
 
 #ifdef MOZ_X11
-#include <X11/Xatom.h>
 #include <gdk/gdkx.h>
-// from Xinerama.h
-typedef struct {
-   int   screen_number;
-   short x_org;
-   short y_org;
-   short width;
-   short height;
-} XineramaScreenInfo;
-// prototypes from Xinerama.h
-typedef Bool (*_XnrmIsActive_fn)(Display *dpy);
-typedef XineramaScreenInfo* (*_XnrmQueryScreens_fn)(Display *dpy, int *number);
 #endif
 #include <dlfcn.h>
 #include <gtk/gtk.h>
 
 #include "gfxPlatformGtk.h"
 #include "mozilla/Logging.h"
 #include "nsGtkUtils.h"
 #include "nsTArray.h"
 
-#define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1)
-
 namespace mozilla {
 namespace widget {
 
 static LazyLogModule sScreenLog("WidgetScreen");
 
 static void
 monitors_changed(GdkScreen* aScreen, gpointer aClosure)
 {
@@ -66,25 +52,26 @@ root_window_event_filter(GdkXEvent* aGdk
       break;
   }
 #endif
 
   return GDK_FILTER_CONTINUE;
 }
 
 ScreenHelperGTK::ScreenHelperGTK()
-  : mXineramalib(nullptr)
-  , mRootWindow(nullptr)
+  : mRootWindow(nullptr)
+#ifdef MOZ_X11
   , mNetWorkareaAtom(0)
+#endif
 {
   MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperGTK created"));
   GdkScreen *defaultScreen = gdk_screen_get_default();
   if (!defaultScreen) {
     // Sometimes we don't initial X (e.g., xpcshell)
-    MOZ_LOG(sScreenLog, LogLevel::Debug, ("mRootWindow is nullptr, running headless"));
+    MOZ_LOG(sScreenLog, LogLevel::Debug, ("defaultScreen is nullptr, running headless"));
     return;
   }
   mRootWindow = gdk_get_default_root_window();
   MOZ_ASSERT(mRootWindow);
 
   g_object_ref(mRootWindow);
 
   // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
@@ -110,221 +97,89 @@ ScreenHelperGTK::~ScreenHelperGTK()
     g_signal_handlers_disconnect_by_func(gdk_screen_get_default(),
                                          FuncToGpointer(monitors_changed),
                                          this);
 
     gdk_window_remove_filter(mRootWindow, root_window_event_filter, this);
     g_object_unref(mRootWindow);
     mRootWindow = nullptr;
   }
-
-  /* XineramaIsActive() registers a callback function close_display()
-   * in X, which is to be called in XCloseDisplay(). This is the case
-   * if Xinerama is active, even if only with one screen.
-   *
-   * We can't unload libXinerama.so.1 here because this will make
-   * the address of close_display() registered in X to be invalid and
-   * it will crash when XCloseDisplay() is called later. */
 }
 
 gint
-ScreenHelperGTK::GetGTKMonitorScaleFactor()
+ScreenHelperGTK::GetGTKMonitorScaleFactor(gint aMonitorNum)
 {
 #if (MOZ_WIDGET_GTK >= 3)
   // Since GDK 3.10
   static auto sGdkScreenGetMonitorScaleFactorPtr = (gint (*)(GdkScreen*, gint))
     dlsym(RTLD_DEFAULT, "gdk_screen_get_monitor_scale_factor");
   if (sGdkScreenGetMonitorScaleFactorPtr) {
-    // FIXME: In the future, we'll want to fix this for GTK on Wayland which
-    // supports a variable scale factor per display.
     GdkScreen *screen = gdk_screen_get_default();
-    return sGdkScreenGetMonitorScaleFactorPtr(screen, 0);
+    return sGdkScreenGetMonitorScaleFactorPtr(screen, aMonitorNum);
   }
 #endif
   return 1;
 }
 
-static float
-GetDefaultCssScale()
-{
-  return ScreenHelperGTK::GetGTKMonitorScaleFactor() * gfxPlatformGtk::GetFontScaleFactor();
-}
-
 static uint32_t
 GetGTKPixelDepth()
 {
   GdkVisual * visual = gdk_screen_get_system_visual(gdk_screen_get_default());
   return gdk_visual_get_depth(visual);
 }
 
 static already_AddRefed<Screen>
-MakeScreen(GdkWindow* aRootWindow)
+MakeScreen(GdkScreen* aScreen, gint aMonitorNum)
 {
-  RefPtr<Screen> screen;
+  GdkRectangle monitor;
+  GdkRectangle workarea;
+  gdk_screen_get_monitor_geometry(aScreen, aMonitorNum, &monitor);
+  gdk_screen_get_monitor_workarea(aScreen, aMonitorNum, &workarea);
+  gint gdkScaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor(aMonitorNum);
 
-  gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
-  gint width = gdk_screen_width() * scale;
-  gint height = gdk_screen_height() * scale;
+  // gdk_screen_get_monitor_geometry / workarea returns application pixels
+  // (desktop pixels), so we need to convert it to device pixels with
+  // gdkScaleFactor.
+  LayoutDeviceIntRect rect(monitor.x * gdkScaleFactor,
+                           monitor.y * gdkScaleFactor,
+                           monitor.width * gdkScaleFactor,
+                           monitor.height * gdkScaleFactor);
+  LayoutDeviceIntRect availRect(workarea.x * gdkScaleFactor,
+                                workarea.y * gdkScaleFactor,
+                                workarea.width * gdkScaleFactor,
+                                workarea.height * gdkScaleFactor);
   uint32_t pixelDepth = GetGTKPixelDepth();
   DesktopToLayoutDeviceScale contentsScale(1.0);
-  CSSToLayoutDeviceScale defaultCssScale(GetDefaultCssScale());
-
-  LayoutDeviceIntRect rect;
-  LayoutDeviceIntRect availRect;
-  rect = availRect = LayoutDeviceIntRect(0, 0, width, height);
-
-#ifdef MOZ_X11
-  // We need to account for the taskbar, etc in the available rect.
-  // See http://freedesktop.org/Standards/wm-spec/index.html#id2767771
-
-  // XXX do we care about _NET_WM_STRUT_PARTIAL?  That will
-  // add much more complexity to the code here (our screen
-  // could have a non-rectangular shape), but should
-  // lead to greater accuracy.
-
-  long *workareas;
-  GdkAtom type_returned;
-  int format_returned;
-  int length_returned;
-
-  GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
-
-  gdk_error_trap_push();
-
-  // gdk_property_get uses (length + 3) / 4, hence G_MAXLONG - 3 here.
-  if (!gdk_property_get(aRootWindow,
-                        gdk_atom_intern ("_NET_WORKAREA", FALSE),
-                        cardinal_atom,
-                        0, G_MAXLONG - 3, FALSE,
-                        &type_returned,
-                        &format_returned,
-                        &length_returned,
-                        (guchar **) &workareas)) {
-    // This window manager doesn't support the freedesktop standard.
-    // Nothing we can do about it, so assume full screen size.
-    MOZ_LOG(sScreenLog, LogLevel::Debug, ("New screen [%d %d %d %d %d %f]",
-                                          rect.x, rect.y, rect.width, rect.height,
-                                          pixelDepth, defaultCssScale.scale));
-    screen = new Screen(rect, availRect,
-                        pixelDepth, pixelDepth,
-                        contentsScale, defaultCssScale);
-    return screen.forget();
-  }
-
-  // Flush the X queue to catch errors now.
-  gdk_flush();
+  CSSToLayoutDeviceScale defaultCssScale(
+    gdkScaleFactor * gfxPlatformGtk::GetFontScaleFactor());
 
-  if (!gdk_error_trap_pop() &&
-      type_returned == cardinal_atom &&
-      length_returned && (length_returned % 4) == 0 &&
-      format_returned == 32) {
-    int num_items = length_returned / sizeof(long);
-
-    for (int i = 0; i < num_items; i += 4) {
-      LayoutDeviceIntRect workarea(workareas[i],     workareas[i + 1],
-                                   workareas[i + 2], workareas[i + 3]);
-      if (!rect.Contains(workarea)) {
-        // Note that we hit this when processing screen size changes,
-        // since we'll get the configure event before the toolbars have
-        // been moved.  We'll end up cleaning this up when we get the
-        // change notification to the _NET_WORKAREA property.  However,
-        // we still want to listen to both, so we'll handle changes
-        // properly for desktop environments that don't set the
-        // _NET_WORKAREA property.
-        NS_WARNING("Invalid bounds");
-        continue;
-      }
-
-      availRect.IntersectRect(availRect, workarea);
-    }
-  }
-  g_free(workareas);
-#endif
-  MOZ_LOG(sScreenLog, LogLevel::Debug, ("New screen [%d %d %d %d %d %f]",
-                                        rect.x, rect.y, rect.width, rect.height,
-                                        pixelDepth, defaultCssScale.scale));
-  screen = new Screen(rect, availRect,
-                      pixelDepth, pixelDepth,
-                      contentsScale, defaultCssScale);
-  return screen.forget();
-}
-
-static already_AddRefed<Screen>
-MakeScreen(const XineramaScreenInfo& aScreenInfo)
-{
-  LayoutDeviceIntRect xineRect(aScreenInfo.x_org, aScreenInfo.y_org,
-                               aScreenInfo.width, aScreenInfo.height);
-  uint32_t pixelDepth = GetGTKPixelDepth();
-  DesktopToLayoutDeviceScale contentsScale(1.0);
-  CSSToLayoutDeviceScale defaultCssScale(GetDefaultCssScale());
-
-  MOZ_LOG(sScreenLog, LogLevel::Debug, ("New screen [%d %d %d %d %d %f]",
-                                        xineRect.x, xineRect.y,
-                                        xineRect.width, xineRect.height,
-                                        pixelDepth, defaultCssScale.scale));
-  RefPtr<Screen> screen = new Screen(xineRect, xineRect,
+  MOZ_LOG(sScreenLog, LogLevel::Debug,
+           ("New screen [%d %d %d %d (%d %d %d %d) %d %f]",
+            rect.x, rect.y, rect.width, rect.height,
+            availRect.x, availRect.y, availRect.width, availRect.height,
+            pixelDepth, defaultCssScale.scale));
+  RefPtr<Screen> screen = new Screen(rect, availRect,
                                      pixelDepth, pixelDepth,
                                      contentsScale, defaultCssScale);
   return screen.forget();
 }
 
 void
 ScreenHelperGTK::RefreshScreens()
 {
   MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
   AutoTArray<RefPtr<Screen>, 4> screenList;
-#ifdef MOZ_X11
-  XineramaScreenInfo *screenInfo = nullptr;
-  int numScreens;
-
-  bool useXinerama = GDK_IS_X11_DISPLAY(gdk_display_get_default());
 
-  if (useXinerama && !mXineramalib) {
-    mXineramalib = PR_LoadLibrary("libXinerama.so.1");
-    if (!mXineramalib) {
-      mXineramalib = SCREEN_MANAGER_LIBRARY_LOAD_FAILED;
-    }
-  }
-  if (mXineramalib && mXineramalib != SCREEN_MANAGER_LIBRARY_LOAD_FAILED) {
-    _XnrmIsActive_fn _XnrmIsActive = (_XnrmIsActive_fn)
-        PR_FindFunctionSymbol(mXineramalib, "XineramaIsActive");
+  GdkScreen *defaultScreen = gdk_screen_get_default();
+  gint numScreens = gdk_screen_get_n_monitors(defaultScreen);
+  MOZ_LOG(sScreenLog, LogLevel::Debug,
+          ("GDK reports %d screens", numScreens));
 
-    _XnrmQueryScreens_fn _XnrmQueryScreens = (_XnrmQueryScreens_fn)
-        PR_FindFunctionSymbol(mXineramalib, "XineramaQueryScreens");
-
-    // get the number of screens via xinerama
-    Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
-    if (_XnrmIsActive && _XnrmQueryScreens && _XnrmIsActive(display)) {
-      screenInfo = _XnrmQueryScreens(display, &numScreens);
-    }
+  for (gint i = 0; i < numScreens; i++) {
+    screenList.AppendElement(MakeScreen(defaultScreen, i));
   }
 
-  // screenInfo == nullptr if either Xinerama couldn't be loaded or
-  // isn't running on the current display
-  if (!screenInfo || numScreens == 1) {
-    numScreens = 1;
-#endif
-    MOZ_LOG(sScreenLog, LogLevel::Debug, ("Find only one screen available"));
-    // Get primary screen
-    screenList.AppendElement(MakeScreen(mRootWindow));
-#ifdef MOZ_X11
-  }
-  // If Xinerama is enabled and there's more than one screen, fill
-  // in the info for all of the screens.  If that's not the case
-  // then defaults to the screen width + height
-  else {
-    MOZ_LOG(sScreenLog, LogLevel::Debug,
-            ("Xinerama enabled for %d screens", numScreens));
-    for (int i = 0; i < numScreens; ++i) {
-      screenList.AppendElement(MakeScreen(screenInfo[i]));
-    }
-  }
-
-  if (screenInfo) {
-    XFree(screenInfo);
-  }
-#endif
   ScreenManager& screenManager = ScreenManager::GetSingleton();
   screenManager.Refresh(Move(screenList));
 }
 
 } // namespace widget
 } // namespace mozilla
--- a/widget/gtk/ScreenHelperGTK.h
+++ b/widget/gtk/ScreenHelperGTK.h
@@ -4,42 +4,40 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_widget_gtk_ScreenHelperGTK_h
 #define mozilla_widget_gtk_ScreenHelperGTK_h
 
 #include "mozilla/widget/ScreenManager.h"
 
-#include "prlink.h"
 #include "gdk/gdk.h"
 #ifdef MOZ_X11
 #include <X11/Xlib.h>
 #endif
 
 namespace mozilla {
 namespace widget {
 
 class ScreenHelperGTK final : public ScreenManager::Helper
 {
 public:
   ScreenHelperGTK();
   ~ScreenHelperGTK() override;
 
-  static gint GetGTKMonitorScaleFactor();
+  static gint GetGTKMonitorScaleFactor(gint aMonitorNum = 0);
 
 #ifdef MOZ_X11
   Atom NetWorkareaAtom() { return mNetWorkareaAtom; }
 #endif
 
   // For internal use from signal callback functions
   void RefreshScreens();
 
 private:
-  PRLibrary* mXineramalib;
   GdkWindow* mRootWindow;
 #ifdef MOZ_X11
   Atom mNetWorkareaAtom;
 #endif
 };
 
 } // namespace widget
 } // namespace mozilla
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -53,18 +53,21 @@ STUB(gdk_pango_context_get)
 STUB(gdk_pointer_grab)
 STUB(gdk_pointer_ungrab)
 STUB(gdk_property_get)
 STUB(gdk_screen_get_default)
 STUB(gdk_screen_get_display)
 STUB(gdk_screen_get_font_options)
 STUB(gdk_screen_get_height)
 STUB(gdk_screen_get_height_mm)
+STUB(gdk_screen_get_n_monitors)
 STUB(gdk_screen_get_monitor_at_window)
 STUB(gdk_screen_get_monitor_geometry)
+STUB(gdk_screen_get_monitor_workarea)
+STUB(gdk_screen_get_monitor_height_mm)
 STUB(gdk_screen_get_number)
 STUB(gdk_screen_get_resolution)
 STUB(gdk_screen_get_rgba_visual)
 STUB(gdk_screen_get_root_window)
 STUB(gdk_screen_get_system_visual)
 STUB(gdk_screen_get_width)
 STUB(gdk_screen_height)
 STUB(gdk_screen_is_composited)