diff --git a/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs b/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs index 2f9cf1bd..3f1c8ea2 100644 --- a/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs +++ b/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs @@ -92,7 +92,7 @@ namespace OpenTK.Platform.MacOS } DisplayDevice opentk_dev = - new DisplayDevice(opentk_dev_current_res, primary, opentk_dev_available_res); + new DisplayDevice(opentk_dev_current_res, primary, opentk_dev_available_res, Rectangle.Empty); displayMap.Add(opentk_dev, currentDisplay); } diff --git a/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs b/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs index acd61103..ec0025a8 100644 --- a/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs +++ b/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs @@ -1,4 +1,4 @@ -#region --- License --- +#region --- License --- /* Licensed under the MIT/X11 license. * Copyright (c) 2006-2008 the OpenTK team. * This notice may not be removed. @@ -81,7 +81,8 @@ namespace OpenTK.Platform.Windows opentk_dev = new DisplayDevice( opentk_dev_current_res, opentk_dev_primary, - opentk_dev_available_res); + opentk_dev_available_res, + opentk_dev_current_res.Bounds); available_device_names.Add(opentk_dev, dev1.DeviceName); } diff --git a/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs b/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs index c0b7f85f..b6e4ec2f 100644 --- a/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs +++ b/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text; using OpenTK.Graphics; @@ -35,79 +36,123 @@ namespace OpenTK.Platform.X11 static X11XrandrDisplayDevice() { - // Get available resolutions. Then, for each resolution get all - // available rates. - // TODO: Global X11 lock. - // TODO: Use xinerama to get the bounds of each monitor. - for (int screen = 0; screen < API.ScreenCount; screen++) + using (new XLock(API.DefaultDisplay)) { - IntPtr timestamp_of_last_update; - using (new XLock(API.DefaultDisplay)) + List devices = new List(); + bool xinerama_supported = false; + try { + // Try to use Xinerama to obtain the geometry of all output devices. + int event_base, error_base; + if (NativeMethods.XineramaQueryExtension(API.DefaultDisplay, out event_base, out error_base) && + NativeMethods.XineramaIsActive(API.DefaultDisplay)) + { + IList screens = NativeMethods.XineramaQueryScreens(API.DefaultDisplay); + bool first = true; + foreach (XineramaScreenInfo screen in screens) + { + DisplayDevice dev = new DisplayDevice(); + dev.Bounds = new Rectangle(screen.X, screen.Y, screen.Width, screen.Height); + if (first) + { + // We consider the first device returned by Xinerama as the primary one. + // Makes sense conceptually, but is there a way to verify this? + dev.IsPrimary = true; + first = false; + } + devices.Add(dev); + // It seems that all X screens are equal to 0 is Xinerama is enabled, at least on Nvidia (verify?) + deviceToScreen.Add(dev, 0 /*screen.ScreenNumber*/); + xinerama_supported = true; + } + } + } + catch { Debug.Print("Xinerama query failed."); } + + if (!xinerama_supported) + { + // We assume that devices are equivalent to the number of available screens. + // Note: this won't work correctly in the case of distinct X servers. + for (int i = 0; i < API.ScreenCount; i++) + { + DisplayDevice dev = new DisplayDevice(); + dev.IsPrimary = i == Functions.XDefaultScreen(API.DefaultDisplay); + devices.Add(dev); + deviceToScreen.Add(dev, i); + } + } + + // Get available resolutions. Then, for each resolution get all available rates. + foreach (DisplayDevice dev in devices) + { + int screen = deviceToScreen[dev]; + + IntPtr timestamp_of_last_update; Functions.XRRTimes(API.DefaultDisplay, screen, out timestamp_of_last_update); - } - lastConfigUpdate.Add(timestamp_of_last_update); - - List available_res = new List(); - - // Add info for a new screen. - screenResolutionToIndex.Add(new Dictionary()); - - int[] depths = FindAvailableDepths(screen); - - int resolution_count = 0; - foreach (XRRScreenSize size in FindAvailableResolutions(screen)) - { - if (size.Width == 0 || size.Height == 0) - { - Debug.Print("[Warning] XRandR returned an invalid resolution ({0}) for display device {1}", size, screen); - continue; - } - short[] rates = null; - using (new XLock(API.DefaultDisplay)) + lastConfigUpdate.Add(timestamp_of_last_update); + + List available_res = new List(); + + // Add info for a new screen. + screenResolutionToIndex.Add(new Dictionary()); + + int[] depths = FindAvailableDepths(screen); + + int resolution_count = 0; + foreach (XRRScreenSize size in FindAvailableResolutions(screen)) { + if (size.Width == 0 || size.Height == 0) + { + Debug.Print("[Warning] XRandR returned an invalid resolution ({0}) for display device {1}", size, screen); + continue; + } + short[] rates = null; rates = Functions.XRRRates(API.DefaultDisplay, screen, resolution_count); + + // It seems that XRRRates returns 0 for modes that are larger than the screen + // can support, as well as for all supported modes. On Ubuntu 7.10 the tool + // "Screens and Graphics" does report these modes, though. + foreach (short rate in rates) + { + // Note: some X servers (like Xming on Windows) do not report any rates other than 0. + // If we only have 1 rate, add it even if it is 0. + if (rate != 0 || rates.Length == 1) + foreach (int depth in depths) + available_res.Add(new DisplayResolution(0, 0, size.Width, size.Height, depth, (float)rate)); + } + // Keep the index of this resolution - we will need it for resolution changes later. + foreach (int depth in depths) + { + // Note that Xinerama may return multiple devices for a single screen. XRandR will + // not distinguish between the two as far as resolutions are supported (since XRandR + // operates on X screens, not display devices) - we need to be careful not to add the + // same resolution twice! + DisplayResolution res = new DisplayResolution(0, 0, size.Width, size.Height, depth, 0); + if (!screenResolutionToIndex[screen].ContainsKey(res)) + screenResolutionToIndex[screen].Add(res, resolution_count); + } + + ++resolution_count; } - // It seems that XRRRates returns 0 for modes that are larger than the screen - // can support, as well as for all supported modes. On Ubuntu 7.10 the tool - // "Screens and Graphics" does report these modes, though. - foreach (short rate in rates) - { - // Note: some X servers (like Xming on Windows) do not report any rates other than 0. - // If we only have 1 rate, add it even if it is 0. - if (rate != 0 || rates.Length == 1) - foreach (int depth in depths) - available_res.Add(new DisplayResolution(0, 0, size.Width, size.Height, depth, (float)rate)); - } - // Keep the index of this resolution - we will need it for resolution changes later. - foreach (int depth in depths) - screenResolutionToIndex[screen].Add(new DisplayResolution(0, 0, size.Width, size.Height, depth, 0), - resolution_count); - - ++resolution_count; + + // The resolution of the current DisplayDevice is discovered through XRRConfigCurrentConfiguration. + // Its refresh rate is discovered by the FindCurrentRefreshRate call. + // Its depth is discovered by the FindCurrentDepth call. + float current_refresh_rate = FindCurrentRefreshRate(screen); + int current_depth = FindCurrentDepth(screen); + IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); + ushort current_rotation; // Not needed. + int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); + + if (dev.Bounds == Rectangle.Empty) + dev.Bounds = new Rectangle(0, 0, available_res[current_resolution_index].Width, available_res[current_resolution_index].Height); + dev.BitsPerPixel = current_depth; + dev.RefreshRate = current_refresh_rate; + dev.AvailableResolutions = available_res; + + deviceToDefaultResolution.Add(dev, current_resolution_index); } - - - // The resolution of the current DisplayDevice is discovered through XRRConfigCurrentConfiguration. - // Its refresh rate is discovered by the FindCurrentRefreshRate call. - // Its depth is discovered by the FindCurrentDepth call. - float current_refresh_rate = FindCurrentRefreshRate(screen); - int current_depth = FindCurrentDepth(screen); - IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); - ushort current_rotation; // Not needed. - int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); - - DisplayDevice current_device = new DisplayDevice( - new DisplayResolution( - 0, 0, - available_res[current_resolution_index].Width, available_res[current_resolution_index].Height, - current_depth, current_refresh_rate), - screen == Functions.XDefaultScreen(API.DefaultDisplay), - available_res); - - deviceToScreen.Add(current_device, screen); - deviceToDefaultResolution.Add(current_device, current_resolution_index); } } @@ -121,10 +166,7 @@ namespace OpenTK.Platform.X11 static int[] FindAvailableDepths(int screen) { - using (new XLock(API.DefaultDisplay)) - { - return Functions.XListDepths(API.DefaultDisplay, screen); - } + return Functions.XListDepths(API.DefaultDisplay, screen); } #endregion @@ -134,10 +176,7 @@ namespace OpenTK.Platform.X11 static XRRScreenSize[] FindAvailableResolutions(int screen) { XRRScreenSize[] resolutions = null; - using (new XLock(API.DefaultDisplay)) - { - resolutions = Functions.XRRSizes(API.DefaultDisplay, screen); - } + resolutions = Functions.XRRSizes(API.DefaultDisplay, screen); if (resolutions == null) throw new NotSupportedException("XRandR extensions not available."); return resolutions; @@ -150,14 +189,11 @@ namespace OpenTK.Platform.X11 static float FindCurrentRefreshRate(int screen) { short rate = 0; - using (new XLock(API.DefaultDisplay)) - { - IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); - ushort rotation = 0; - int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation); - rate = Functions.XRRConfigCurrentRate(screen_config); - Functions.XRRFreeScreenConfigInfo(screen_config); - } + IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); + ushort rotation = 0; + int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation); + rate = Functions.XRRConfigCurrentRate(screen_config); + Functions.XRRFreeScreenConfigInfo(screen_config); return (float)rate; } @@ -167,10 +203,7 @@ namespace OpenTK.Platform.X11 private static int FindCurrentDepth(int screen) { - using (new XLock(API.DefaultDisplay)) - { - return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen); - } + return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen); } #endregion @@ -181,14 +214,14 @@ namespace OpenTK.Platform.X11 public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) { - // If resolution == null, restore to default resolution (new_resolution_index = 0). + // If resolution is null, restore the default resolution (new_resolution_index = 0). using (new XLock(API.DefaultDisplay)) { int screen = deviceToScreen[device]; IntPtr root = Functions.XRootWindow(API.DefaultDisplay, screen); IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, root); - + ushort current_rotation; int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); int new_resolution_index; @@ -213,5 +246,55 @@ namespace OpenTK.Platform.X11 } #endregion + + #region NativeMethods + + static class NativeMethods + { + const string Xinerama = "libXinerama"; + + [DllImport(Xinerama)] + public static extern bool XineramaQueryExtension(IntPtr dpy, out int event_basep, out int error_basep); + + [DllImport(Xinerama)] + public static extern int XineramaQueryVersion (IntPtr dpy, out int major_versionp, out int minor_versionp); + + [DllImport(Xinerama)] + public static extern bool XineramaIsActive(IntPtr dpy); + + [DllImport(Xinerama)] + static extern IntPtr XineramaQueryScreens(IntPtr dpy, out int number); + + public static IList XineramaQueryScreens(IntPtr dpy) + { + int number; + IntPtr screen_ptr = XineramaQueryScreens(dpy, out number); + List screens = new List(number); + + unsafe + { + XineramaScreenInfo* ptr = (XineramaScreenInfo*)screen_ptr; + while (--number >= 0) + { + screens.Add(*ptr); + ptr++; + } + } + + return screens; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct XineramaScreenInfo + { + public int ScreenNumber; + public short X; + public short Y; + public short Width; + public short Height; + } + + #endregion } }