[MWF] Implement multi-display support on Linux (Fixes #325669)
authorEberhard Beilharz <eb1@sil.org>
Fri, 16 May 2014 14:21:51 +0000 (16:21 +0200)
committerEberhard Beilharz <eb1@sil.org>
Fri, 16 May 2014 14:21:51 +0000 (16:21 +0200)
The number of displays and sizes can be retrieved with the help of
libXinerama. The implementation was done in a way that is backwards
compatible, so on non-Linux systems or when libXinerama is not
available the behavior will be the same as before.

Also fixed the implementation of FormStartPosition.CenterScreen to
bring up the form centered on the current display (with the current
display being either the screen the top left corner of the owner
form is on, or if there is no owner the screen that has the mouse
pointer). This fixes Novell bug #325669 on Linux
(https://bugzilla.novell.com/show_bug.cgi?id=325669).

configure.in
data/config.in
mcs/class/Managed.Windows.Forms/System.Windows.Forms/Form.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/Screen.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/SystemInformation.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/X11Structs.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUI.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUICarbon.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIDriver.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIWin32.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs

index f6fb3b31fbad1448495e42545ff24e04b77a7b49..a5b478fb98cb81c4d6144931f29f0205d86eba5b 100644 (file)
@@ -2492,6 +2492,7 @@ INTL="libc.so.6"
 SQLITE="libsqlite.so.0"
 SQLITE3="libsqlite3.so.0"
 X11="libX11.so"
+XINERAMA="libXinerama.so"
 
 sizeof_register="SIZEOF_VOID_P"
 
@@ -2963,6 +2964,18 @@ case "$host" in
                AC_MSG_WARN([Could not find X development libs. Do you have the -devel package installed? Assuming libX11.so.6...]);
                X11=libX11.so.6
        fi
+       AC_MSG_CHECKING(for the soname of libXinerama.so)
+       for i in $x_libraries $dlsearch_path; do
+               for r in 1 2 3; do
+                       if test -f $i/libXinerama.so.$r; then
+                               XINERAMA=libXinerama.so.$r
+                               AC_MSG_RESULT($XINERAMA)
+                       fi
+               done
+       done
+       if test "x$XINERAMA" = "xlibXinerama.so"; then
+               AC_MSG_WARN([Could not find Xinerama development libs. Support for multiple monitors might not work...]);
+       fi
        ;;
 esac
 
@@ -3310,6 +3323,7 @@ AC_SUBST(INTL)
 AC_SUBST(SQLITE)
 AC_SUBST(SQLITE3)
 AC_SUBST(X11)
+AC_SUBST(XINERAMA)
 AC_DEFINE_UNQUOTED(ARCHITECTURE,"$arch_target",[The architecture this is running on])
 AC_SUBST(arch_target)
 AC_SUBST(CFLAGS)
index cf457fc207bac9f58a5f335a5f8bc31aa45f629b..44fefc080276ca63a6080c5327fa6ce66034ce77 100644 (file)
@@ -16,6 +16,7 @@
        <dllmap dll="sqlite" target="@SQLITE@" os="!windows"/>
        <dllmap dll="sqlite3" target="@SQLITE3@" os="!windows"/>
        <dllmap dll="libX11" target="@X11@" os="!windows" />
+       <dllmap dll="libXinerama" target="@XINERAMA@" os="!windows" />
        <dllmap dll="libcairo-2.dll" target="libcairo.so.2" os="!windows"/>
        <dllmap dll="libcairo-2.dll" target="libcairo.2.dylib" os="osx"/>
        <dllmap dll="libcups" target="libcups.so.2" os="!windows"/>
index 897bc75ea6b84ad6c6449e982e6a9df0238434d1..5b6ed663ee251300d8c4ac1396f8796004961a30 100644 (file)
@@ -1872,9 +1872,8 @@ namespace System.Windows.Forms {
                }
 
                protected void CenterToScreen() {
-                       Size    DisplaySize;
-                       int     w;
-                       int     h;
+                       int w;
+                       int h;
 
                        // MS creates the handle here.
                        if (TopLevel) {
@@ -1894,8 +1893,14 @@ namespace System.Windows.Forms {
                                h = DefaultSize.Height;
                        }
 
-                       XplatUI.GetDisplaySize(out DisplaySize);
-                       this.Location = new Point(DisplaySize.Width / 2 - w / 2, DisplaySize.Height / 2 - h / 2);
+                       Rectangle workingArea;
+                       if (Owner == null) {
+                               workingArea = Screen.FromPoint (MousePosition).WorkingArea;
+                       } else {
+                               workingArea = Screen.FromControl (Owner).WorkingArea;
+                       }
+                       this.Location = new Point (workingArea.Left + workingArea.Width / 2 - w / 2,
+                               workingArea.Top + workingArea.Height / 2 - h / 2);
                }
 
                [EditorBrowsable(EditorBrowsableState.Advanced)]
index eeb4f80000b96c0abe129155c934f43dc56f92b7..a7e0a29812bc49452ccc48c68ded9b8e0d05330f 100644 (file)
 //
 //
 
-// NOTE: We made a concious decision to have only a single 'screen'
-// due to the differences in platforms. On Win32 we could gather
-// all information, but not for X11 (and possibly Mac). So for now
-// we'll stick with a single screen, but the functions are still 
-// written to support multiple screens, simply beef up the all_screens 
-// assignment to get multiples
-// To support multiples, we need to use GetMonitorInfo API on Win32
-
 using System;
 using System.Drawing;
 
 namespace System.Windows.Forms {
        public class Screen {
                #region Local Variables
-               private static Screen[] all_screens = { new Screen(true, "Mono MWF Primary Display", SystemInformation.VirtualScreen, SystemInformation.WorkingArea) };
+               private static Screen[] all_screens;
                private bool            primary;
                private Rectangle       bounds;
                private Rectangle       workarea;
@@ -47,12 +39,28 @@ namespace System.Windows.Forms {
                #endregion      // Local Variables
 
                #region Constructors
-               private Screen() {
+               static Screen ()
+               {
+                       try {
+                               all_screens = XplatUI.AllScreens;
+                       }
+                       catch (Exception e) {
+                               Console.WriteLine ("{0} trying to get all screens: {1}", e.GetType (), e.Message);
+                       }
+
+                       if (all_screens == null || all_screens.Length == 0) {
+                               // just use a default one
+                               all_screens = new[] { new Screen(true, "Mono MWF Primary Display",
+                                       XplatUI.VirtualScreen, XplatUI.WorkingArea) };
+                       }
+               }
+
+               internal Screen() {
                        this.primary = true;
-                       this.bounds = SystemInformation.WorkingArea;
+                       this.bounds = XplatUI.WorkingArea;
                }
 
-               private Screen(bool primary, string name, Rectangle bounds, Rectangle workarea) {
+               internal Screen(bool primary, string name, Rectangle bounds, Rectangle workarea) {
                        this.primary = primary;
                        this.name = name;
                        this.bounds = bounds;
index 95a677e2d778b685b7872e0c6857d1e1bc368a4e..6a10a9173584dc7f02507d75bfcc03013662c1c0 100644 (file)
@@ -385,7 +385,7 @@ namespace System.Windows.Forms
 
                public static int MonitorCount {
                        get {
-                               return 1;               // Why bother...
+                               return Screen.AllScreens.Length;
                        }
                }
 
@@ -479,13 +479,15 @@ namespace System.Windows.Forms
 
                public static Size PrimaryMonitorMaximizedWindowSize {
                        get {
-                               return new Size(WorkingArea.Width, WorkingArea.Height);
+                               var workingArea = Screen.PrimaryScreen.WorkingArea;
+                               return new Size (workingArea.Width, workingArea.Height);
                        }
                }
 
                public static Size PrimaryMonitorSize {
                        get {
-                               return new Size(WorkingArea.Width, WorkingArea.Height);
+                               var bounds = Screen.PrimaryScreen.Bounds;
+                               return new Size (bounds.Width, bounds.Height);
                        }
                }
 
@@ -593,13 +595,16 @@ namespace System.Windows.Forms
 
                public static Rectangle VirtualScreen {
                        get {
-                               return XplatUI.VirtualScreen;
+                               var rect = new Rectangle ();
+                               foreach (var screen in Screen.AllScreens)
+                                       rect = Rectangle.Union (rect, screen.Bounds);
+                               return rect;
                        }
                }
 
                public static Rectangle WorkingArea {
                        get {
-                               return XplatUI.WorkingArea;
+                               return Screen.PrimaryScreen.WorkingArea;
                        }
                }
        }
index f2dd4ca10f1e25ad6ad18450265e97fc25ecf825..cfac29e9bed8d18eda8fb460b4a87647b5ba3136 100644 (file)
@@ -1808,4 +1808,14 @@ namespace System.Windows.Forms {
                public const string XNSpotLocation = "spotLocation";
                public const string XNFontSet = "fontSet";
        }
+
+       [StructLayout (LayoutKind.Sequential)]
+       internal struct XineramaScreenInfo
+       {
+               public int screen_number;
+               public short x_org;
+               public short y_org;
+               public short width;
+               public short height;
+       }
 }
index 89f91da939c0baf542260b71880a5ca72601da80..565569c43f27b82c6565cf5cc0b7222b04dbd4c7 100644 (file)
@@ -427,6 +427,12 @@ namespace System.Windows.Forms {
                        }
                }
 
+               public static Screen[] AllScreens {
+                       get {
+                               return driver.AllScreens;
+                       }
+               }
+
                public static bool ThemesEnabled {
                        get {
                                return XplatUI.driver.ThemesEnabled;
index 18afe41221fbf888cac28d6393c84225ae3788ad..7a568b4c09ec7656cdfd3416b7ce566eaf1d649a 100644 (file)
@@ -2261,6 +2261,14 @@ namespace System.Windows.Forms {
                                return new Rectangle ((int)bounds.origin.x, (int)bounds.origin.y, (int)bounds.size.width, (int)bounds.size.height);
                        }
                }
+
+               [MonoTODO]
+               internal override Screen[] AllScreens {
+                       get {
+                               return null;
+                       }
+               }
+
                internal override bool ThemesEnabled {
                        get {
                                return XplatUICarbon.themes_enabled;
index b79b967566cf9eb257748c133b24bd562142b7e7..ff2427e069041443921e17fff29bb2903512acac 100644 (file)
@@ -248,6 +248,7 @@ namespace System.Windows.Forms {
                internal abstract bool MouseWheelPresent { get; }
                internal abstract Rectangle VirtualScreen { get; }
                internal abstract Rectangle WorkingArea { get; }
+               internal abstract Screen[] AllScreens { get; }
                internal abstract bool ThemesEnabled { get; }
 
                internal virtual bool RequiresPositiveClientAreaSize {
index efc464d5d124bb11c44f58f23b742727b2c59bac..e541b1fdb97e8ac428e21922b014da5a404fed6f 100644 (file)
@@ -1505,6 +1505,14 @@ namespace System.Windows.Forms {
                        }
                }
 
+               [MonoTODO]
+               internal override Screen[] AllScreens {
+                       get {
+                               // To support multiples, we need to use GetMonitorInfo API on Win32
+                               return null;
+                       }
+               }
+
                internal override bool ThemesEnabled {
                        get {
                                return XplatUIWin32.themes_enabled;
index be2966157251a3edacf74110190c667537623fb2..3500501dbe62e1de6e54b42334abcb73d416ee7b 100644 (file)
@@ -2594,6 +2594,28 @@ namespace System.Windows.Forms {
                        }
                }
 
+               internal override Screen[] AllScreens {
+                       get {
+                               if (!XineramaIsActive (DisplayHandle))
+                                       return null;
+                               int nScreens;
+                               IntPtr xineramaScreens = XineramaQueryScreens (DisplayHandle, out nScreens);
+                               var screens = new Screen [nScreens];
+                               IntPtr current = xineramaScreens;
+                               for (int i = 0; i < nScreens; i++) {
+                                       var screen = (XineramaScreenInfo)Marshal.PtrToStructure (current,
+                                               typeof (XineramaScreenInfo));
+                                       var screenRect = new Rectangle (screen.x_org, screen.y_org, screen.width,
+                                               screen.height);
+                                       var name = string.Format ("Display {0}", screen.screen_number);
+                                       screens [i] = new Screen (i == 0, name, screenRect, screenRect);
+                                       current = (IntPtr)( (ulong)current + (ulong)Marshal.SizeOf(typeof (XineramaScreenInfo)));
+                               }
+                               XFree (xineramaScreens);
+                               return screens;
+                       }
+               }
+
                internal override bool ThemesEnabled {
                        get {
                                return XplatUIX11.themes_enabled;
@@ -7231,6 +7253,34 @@ namespace System.Windows.Forms {
                }
 #endregion
 
+#region Xinerama imports
+               [DllImport ("libXinerama", EntryPoint="XineramaQueryScreens")]
+               extern static IntPtr _XineramaQueryScreens (IntPtr display, out int number);
+               internal static IntPtr XineramaQueryScreens (IntPtr display, out int number)
+               {
+                       DebugHelper.TraceWriteLine ("XineramaQueryScreens");
+                       return _XineramaQueryScreens (display, out number);
+               }
+
+               [DllImport ("libXinerama", EntryPoint="XineramaIsActive")]
+               extern static bool _XineramaIsActive (IntPtr display);
+               static bool XineramaNotInstalled;
+
+               internal static bool XineramaIsActive (IntPtr display)
+               {
+                       DebugHelper.TraceWriteLine ("XineramaIsActive");
+
+                       if (XineramaNotInstalled)
+                               return false;
+                       try {
+                               return _XineramaIsActive (display);
+                       } catch (DllNotFoundException) {
+                               // Xinerama isn't installed
+                               XineramaNotInstalled = true;
+                               return false;
+                       }
+               }
+#endregion
 
 #else //no TRACE defined
 
@@ -7601,6 +7651,29 @@ namespace System.Windows.Forms {
                internal extern static void gtk_clipboard_set_text (IntPtr clipboard, string text, int len);
 #endregion
 
+
+#region Xinerama imports
+               [DllImport ("libXinerama")]
+               internal extern static IntPtr XineramaQueryScreens (IntPtr display, out int number);
+
+               [DllImport ("libXinerama", EntryPoint = "XineramaIsActive")]
+               extern static bool _XineramaIsActive (IntPtr display);
+               static bool XineramaNotInstalled;
+
+               internal static bool XineramaIsActive (IntPtr display)
+               {
+                       if (XineramaNotInstalled)
+                               return false;
+                       try {
+                               return _XineramaIsActive (display);
+                       } catch (DllNotFoundException) {
+                               // Xinerama isn't installed
+                               XineramaNotInstalled = true;
+                               return false;
+                       }
+               }
+#endregion
+
 #endif
        }
 }