diff options
| author | jpbruyere <jp.bruyere@hotmail.com> | 2017-03-03 13:45:46 +0100 |
|---|---|---|
| committer | jpbruyere <jp.bruyere@hotmail.com> | 2017-03-03 13:45:46 +0100 |
| commit | 15b061c9e753c651f7499adaf67a7286a5c9bf5e (patch) | |
| tree | 9672ad76698328b862f1a706d09cf8ad2060f441 /CrowX/X11/X11DisplayDevice.cs | |
| parent | 1f34a1c79707ee635b1025d6e2e78bea0856d1a6 (diff) | |
first testsX11Backend
Diffstat (limited to 'CrowX/X11/X11DisplayDevice.cs')
| -rw-r--r-- | CrowX/X11/X11DisplayDevice.cs | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/CrowX/X11/X11DisplayDevice.cs b/CrowX/X11/X11DisplayDevice.cs new file mode 100644 index 00000000..0ef87407 --- /dev/null +++ b/CrowX/X11/X11DisplayDevice.cs @@ -0,0 +1,472 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 the Open Toolkit library. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +#if !MINIMAL +using System.Drawing; +#endif +using System.Runtime.InteropServices; + +namespace X11Sharp +{ + sealed class X11DisplayDevice : DisplayDeviceBase + { + // Store a mapping between resolutions and their respective + // size_index (needed for XRRSetScreenConfig). The size_index + // is simply the sequence number of the resolution as returned by + // XRRSizes. This is done per available screen. + readonly List<Dictionary<DisplayResolution, int>> screenResolutionToIndex = + new List<Dictionary<DisplayResolution, int>>(); + // Store a mapping between DisplayDevices and their default resolutions. + readonly Dictionary<DisplayDevice, int> deviceToDefaultResolution = new Dictionary<DisplayDevice, int>(); + // Store a mapping between DisplayDevices and X11 screens. + readonly Dictionary<DisplayDevice, int> deviceToScreen = new Dictionary<DisplayDevice, int>(); + // Keep the time when the config of each screen was last updated. + readonly List<IntPtr> lastConfigUpdate = new List<IntPtr>(); + + bool xinerama_supported, xrandr_supported, xf86_supported; + + #region Constructors + + public X11DisplayDevice() + { + RefreshDisplayDevices(); + } + + #endregion + + #region Private Methods + + void RefreshDisplayDevices() + { + using (new XLock(API.DefaultDisplay)) + { + List<DisplayDevice> devices = new List<DisplayDevice>(); + xinerama_supported = false; + try + { + xinerama_supported = QueryXinerama(devices); + } + 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); + } + } + + try + { + xrandr_supported = QueryXRandR(devices); + } + catch { } + + if (!xrandr_supported) + { + Debug.Print("XRandR query failed, falling back to XF86."); + try + { + xf86_supported = QueryXF86(devices); + } + catch { } + + if (!xf86_supported) + { + Debug.Print("XF86 query failed, no DisplayDevice support available."); + } + } + + AvailableDevices.Clear(); + AvailableDevices.AddRange(devices); + Primary = FindDefaultDevice(devices); + } + } + + static DisplayDevice FindDefaultDevice(IEnumerable<DisplayDevice> devices) + { + foreach (DisplayDevice dev in devices) + if (dev.IsPrimary) + return dev; + + throw new InvalidOperationException("No primary display found. Please file a bug at http://www.opentk.com"); + } + + bool QueryXinerama(List<DisplayDevice> devices) + { + // 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<XineramaScreenInfo> 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*/); + } + } + return (devices.Count>0); + } + + bool QueryXRandR(List<DisplayDevice> devices) + { + // 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<DisplayResolution> available_res = new List<DisplayResolution>(); + + // Add info for a new screen. + screenResolutionToIndex.Add(new Dictionary<DisplayResolution, int>()); + + 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; + } + + + // 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) + { + // We have added depths.Length copies of each resolution + // Adjust the return value of XRRGetScreenInfo to retrieve the correct resolution + int index = current_resolution_index * depths.Length; + + // Make sure we are within the bounds of the available_res array + if (index >= available_res.Count) + { + // If not, use the return value of XRRGetScreenInfo directly + index = current_resolution_index; + } + DisplayResolution current_resolution = available_res[index]; + dev.Bounds = new Rectangle(0, 0, current_resolution.Width, current_resolution.Height); + } + dev.BitsPerPixel = current_depth; + dev.RefreshRate = current_refresh_rate; + dev.AvailableResolutions = available_res; + + deviceToDefaultResolution.Add(dev, current_resolution_index); + } + + return true; + } + + bool QueryXF86(List<DisplayDevice> devices) + { + int major; + int minor; + + try + { + if (!API.XF86VidModeQueryVersion(API.DefaultDisplay, out major, out minor)) + return false; + } + catch (DllNotFoundException) + { + return false; + } + + int currentScreen = 0; + Debug.Print("Using XF86 v" + major.ToString() + "." + minor.ToString()); + + foreach (DisplayDevice dev in devices) + { + int count; + + IntPtr srcArray; + API.XF86VidModeGetAllModeLines(API.DefaultDisplay, currentScreen, out count, out srcArray); + Debug.Print(count.ToString() + " modes detected on screen " + currentScreen.ToString()); + IntPtr[] array = new IntPtr[count]; + Marshal.Copy(srcArray, array, 0, count); + API.XF86VidModeModeInfo Mode = new API.XF86VidModeModeInfo(); + + int x; + int y; + API.XF86VidModeGetViewPort(API.DefaultDisplay, currentScreen, out x, out y); + List<DisplayResolution> resolutions = new List<DisplayResolution>(); + for (int i = 0; i < count; i++) + { + Mode = (API.XF86VidModeModeInfo)Marshal.PtrToStructure(array[i], typeof(API.XF86VidModeModeInfo)); + resolutions.Add(new DisplayResolution(x, y, Mode.hdisplay, Mode.vdisplay, 24, (Mode.dotclock * 1000F) / (Mode.vtotal * Mode.htotal))); + } + + dev.AvailableResolutions = resolutions; + int pixelClock; + API.XF86VidModeModeLine currentMode; + API.XF86VidModeGetModeLine(API.DefaultDisplay, currentScreen, out pixelClock, out currentMode); + dev.Bounds = new Rectangle(x, y, currentMode.hdisplay, (currentMode.vdisplay == 0) ? currentMode.vsyncstart : currentMode.vdisplay); + dev.BitsPerPixel = FindCurrentDepth(currentScreen); + dev.RefreshRate = (pixelClock * 1000F) / (currentMode.vtotal * currentMode.htotal); + currentScreen++; + } + return true; + } + + #region static int[] FindAvailableDepths(int screen) + + static int[] FindAvailableDepths(int screen) + { + return Functions.XListDepths(API.DefaultDisplay, screen); + } + + #endregion + + #region static XRRScreenSize[] FindAvailableResolutions(int screen) + + static XRRScreenSize[] FindAvailableResolutions(int screen) + { + XRRScreenSize[] resolutions = null; + resolutions = Functions.XRRSizes(API.DefaultDisplay, screen); + if (resolutions == null) + throw new NotSupportedException("XRandR extensions not available."); + return resolutions; + } + + #endregion + + #region static float FindCurrentRefreshRate(int screen) + + static float FindCurrentRefreshRate(int screen) + { + short rate = 0; + IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); + rate = Functions.XRRConfigCurrentRate(screen_config); + Functions.XRRFreeScreenConfigInfo(screen_config); + return (float)rate; + } + + #endregion + + #region private static int FindCurrentDepth(int screen) + + static int FindCurrentDepth(int screen) + { + return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen); + } + + #endregion + + bool ChangeResolutionXRandR(DisplayDevice device, DisplayResolution resolution) + { + 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; + if (resolution != null) + new_resolution_index = screenResolutionToIndex[screen] + [new DisplayResolution(0, 0, resolution.Width, resolution.Height, resolution.BitsPerPixel, 0)]; + else + new_resolution_index = deviceToDefaultResolution[device]; + + Debug.Print("Changing size of screen {0} from {1} to {2}", + screen, current_resolution_index, new_resolution_index); + + int ret = 0; + short refresh_rate =(short)(resolution != null ? resolution.RefreshRate : 0); + if (refresh_rate > 0) + { + ret = Functions.XRRSetScreenConfigAndRate(API.DefaultDisplay, + screen_config, root, new_resolution_index, current_rotation, + refresh_rate, IntPtr.Zero); + } + else + { + ret = Functions.XRRSetScreenConfig(API.DefaultDisplay, + screen_config, root, new_resolution_index, current_rotation, + IntPtr.Zero); + } + + if (ret != 0) + { + Debug.Print("[Error] Change to resolution {0} failed with error {1}.", + resolution, (ErrorCode)ret); + } + + return ret == 0; + } + } + + static bool ChangeResolutionXF86(DisplayDevice device, DisplayResolution resolution) + { + return false; + } + + #endregion + + #region IDisplayDeviceDriver Members + + public sealed override bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) + { + // If resolution is null, restore the default resolution (new_resolution_index = 0). + + if (xrandr_supported) + { + return ChangeResolutionXRandR(device, resolution); + } + else if (xf86_supported) + { + return ChangeResolutionXF86(device, resolution); + } + else + { + return false; + } + } + + public sealed override bool TryRestoreResolution(DisplayDevice device) + { + return TryChangeResolution(device, null); + } + + #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<XineramaScreenInfo> XineramaQueryScreens(IntPtr dpy) + { + int number; + IntPtr screen_ptr = XineramaQueryScreens(dpy, out number); + List<XineramaScreenInfo> screens = new List<XineramaScreenInfo>(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 + } +} |